How to generate a GUID in C?
Asked Answered
J

4

6

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.
Janeyjangle answered 13/9, 2011 at 8:45 Comment(1)
possible duplicate of Simple Generation of GUID in CArchaeology
P
4

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

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.

Penis answered 13/9, 2011 at 9:9 Comment(2)
libuuid is small enough on linux that it does not require boost's uuid mechanism.Terr
@jørgensen : libuuid is small enough on linux that it does not require boost's uuid mechanism : Isn't what my answer was about? Looking at Boost to see/extract how it was done on each platform?...Penis
P
7

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 .. https://www.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 .. How do I create a GUID / UUID? .. 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.

Pangaro answered 24/2, 2014 at 6:59 Comment(4)
This isn't a guid, but just a formtated random number.Kirsti
This is a version 4 random UUID and is perfectly valid.Gyp
Well, valid except for the extraordinarily weak random seeding.Gyp
Could be improved a bit by replacing the last two cases with default: break;, initializing c = szTemp[t] and replacing switch (c).Leucopenia
C
4

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
Carbonaceous answered 13/9, 2011 at 9:7 Comment(2)
the problem is that the databases are created in seperate locations with no central authority, therefore no managment can verify uniquenessJaneyjangle
@chacham: the database ID could be a MAC address, an OS generated GUID, etcCarbonaceous
P
4

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

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.

Penis answered 13/9, 2011 at 9:9 Comment(2)
libuuid is small enough on linux that it does not require boost's uuid mechanism.Terr
@jørgensen : libuuid is small enough on linux that it does not require boost's uuid mechanism : Isn't what my answer was about? Looking at Boost to see/extract how it was done on each platform?...Penis
P
1

Here's a Linux example. Linux has a header file for uuid generation, uuid.h. For my Ubuntu 20.x OS, the uuid.h header file is in /usr/include/uuid/.

/* uuid.c
 * 
 * Defines function uuid
 *
 * Print a universally unique identifer, created using Linux uuid_generate.
 *
 * 
 * Compile
 *
 * gcc uuid.c -o uuid -luuid -Wall -g
 *
 *
 * Run
 * 
 * ./uuid
 * 
 *
 * Debug
 *
 * gdb uuid
 * b main
 * r
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <uuid/uuid.h>

char* uuid(char out[UUID_STR_LEN]){
  uuid_t b;
  uuid_generate(b);
  uuid_unparse_lower(b, out);
  return out;
}

int main(){
  char out[UUID_STR_LEN]={0};
  puts(uuid(out));
  return EXIT_SUCCESS;
}
Patience answered 9/8, 2021 at 21:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.