How to generate a GUID in C?

2020-06-22 05:25发布

问题:

I want to generate guids to insert into a SQLite database (i.e. no support from the db itself). However, I would like to have control over certain properties:

  1. Orderedness for generating increasing guid values.
  2. Computer independence. The db is public and may/may not want guids to allow someone to trace data back to a specific machine.
  3. 'Enough' randomness. The guids are keys in the db which is going to be merged with a lot of other dbs and can get quite large, meaning that faking a guid like many algorithms do is no good.
  4. I can deal with using system specific APIs but please link both Windows and Linux functions, and something like SQLite is preferred, where I can just use code someone else wrote.
  5. I would also prefer code which is OK to use in a commercial application.

回答1:

You can either use or look at the code of Boost.Uuid :

http://www.boost.org/doc/libs/1_47_0/libs/uuid/index.html

It is a C++ library, but still, you can find inside how the code author retrieved the Uuid on multiple systems. Last time I checked (january 2010), I found at least the following implementations for Windows and Linux/Solaris (this info could be outdated):

UUID/GUID on Linux/Solaris

Open a file to /dev/urandom and read enough bytes (16) to make up a GUID/UUID.

UUID/GUID on Windows

Use the following WinAPI functions

  • CryptAcquireContext to acquire a random context
  • CryptReleaseContext to release the acquired random context
  • CryptGenRandom to generate enough bytes to make up a GUID/UUID

Other implementations

The Wikipedia page on GUID/UUID has a list of alternative implementations you could use/study:

https://en.wikipedia.org/wiki/UUID#Implementations

About your conditions

There is a GUID/UUID type which is always random (the version 4), meaning that to be compatible with other GUID/UUID semantics, you should respect that.

Now, you want the GUID/UUID to be ordered in time. The only way to do that without weakening the GUID/UUID randomness would be to prefix the 16-byte GUID/UUID with an unsigned integer (which would make your identifier data 20-bytes, or more, depending on your integer). Just generate a GUID/UUID, and increase the integer.



回答2:

One place to look for an answer to creating a GUID that contains many of the elements the author is looking for is in PHP .. http://us3.php.net/uniqid .. In their examples, they discuss how to add server names, database names, and other elements to a GUID.

However, to address the need for a C based GUID function, here is code based on a JavaScript function .. Create GUID / UUID in JavaScript? .. this example uses RegEx to create the GUID.

Below is code that will create a GUID based on the JavaSCript example. I'm sure there are more elegant solutions out there. This is something cobbled together to help give a clean example for others to follow.

srand (clock());
char GUID[40];
int t = 0;
char *szTemp = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";
char *szHex = "0123456789ABCDEF-";
int nLen = strlen (szTemp);

for (t=0; t<nLen+1; t++)
{
    int r = rand () % 16;
    char c = ' ';   

    switch (szTemp[t])
    {
        case 'x' : { c = szHex [r]; } break;
        case 'y' : { c = szHex [r & 0x03 | 0x08]; } break;
        case '-' : { c = '-'; } break;
        case '4' : { c = '4'; } break;
    }

    GUID[t] = ( t < nLen ) ? c : 0x00;
}

printf ("%s\r\n", GUID);

Note: strings end with a 0x00 character.



回答3:

First off, GUIDs aren't random, they are very well defined mathmatically.

As for your problem, put the GUID creation into the database itself as a stored procedure, that way the system is platform independent. Then, make the GUID an auto-incrementing integer prefixed with a database ID. The prefix allows databases to be merged easily. The database ID needs to be unique for each database. If you control each database then it is straightforward to ensure these are unique. Otherwise you may need a lookup system which maps the database IP address (or some other unique identifier) to a unique database ID.

If you don't have stored procedures then create a table with "NextIndex" and "DatabaseID" fields and update them when a new record is added:

read NextIndex and DatabaseID
increment NextIndex
ID = NextIndex + DatabaseID
add new record, setting "GUID" to the ID value


标签: c guid uuid