As I read the man page, you shouldn't get duplicates from a single process calling uuid_generate_time() but it is possible to get duplicates from multiple concurrently running processes, although the library tries to use a number of different methods to ensure that that doesn't happen. The uuid_generate_time_safe() function returns a status indicating whether it was able to use any of those methods or not. It's also worth noting that many devices these days support MAC address cloning, so using the MAC address doesn't actually guarantee uniqueness across devices, either. Finally, it's also worth noting that the uuid library is not part of Standard C, so your mileage may vary depending on what system you're using (it may not be available at all on some systems).
Just because it's theoretically possible, doesn't mean it's relevant. That the world doesn't exist anymore in a year is as likely as getting duplicates. We still get up every morning.
Great video! I just look the header and found a useful UUID_STR_LEN to use as size for the parsed string. (linux uuid-dev package). Thanks for your explanation!
I have actually generated duplicate UUIDs by accident. If you manage to seed the random number generator used by your UUID library, and make calls generate UUIDs in parallel (say on a 40 core processor) is indeed possible to generate collisions for the random and time based uuid types. Generating UUID1s that collide is actually pretty easy because according to the birthday paradox you'd expect to get 1 after only 128 calls because there are only 2**14 bits of entropy on any given node. This is why at least python's UUID1 function let's you provide a "clock_seq" which can be populated with a value from a lamport clock to back upto 16384 calls before a collision on a single node.
I had a weird experience. I generated two UUIDs, then unparsed both of them into uuidtext variables and then used printf on them. The first unparsed ID was messed up. char uuidtext1[sizeof(uuid_t)]; char uuidtext2[sizeof(uuid_t)]; uuid_unparse(myuuid1, uuidtext1); uuid_unparse(myuuid2, uuidtext2); printf("uuid1: %s uuid2: %s ", uuidtext1, uuidtext2); If I print the first uuidtext before I unparse the second then it looks OK. So it seems like the 2nd instance of unparsing the ID is altering the one before it? At least on my system which is a hyper-v VM running debian 11.
So I want to have a program only accessible to one computer unless I give permission if the user needs to. Is there a way where if a program is bought and user downloads it will automatically generate a uuid just for that pc?
What's going on with the endianness of those UUIDs? From my experience, the raw binary representation versus the formatted one has the first four blocks in reverse order.
I put it in a do..while loop, keeps going, not sure it'll ever stop? do{ uuid_generate_time(myuuid); uuid_generate_time(myuuid2); }while(myuuid != myuuid2)
Well for software keys that's fine I guess, but for runtime objects like hardware or some custom object in app I prefer to just use a global buffer with some very basic objects that reference both the linked object & the basic information about the object such as what type it is and just use the index as UUID, here's a rough example (ignoring code for actually allocating etc): OBJECT_TYPE custom_type = { "INHERIT/CUSTOM", init_custom, term_custom, free_custom }; int create_custom( CUSTOM **custom ) { int err; OBJECT *object = NULL; *custom = NULL; err = create_object( &object, &custom_type ); if ( err ) return err; *custom = get_object_data( object ); return 0; } I prefer to return error codes & pass a pointer, if the developer wants to handle the error then they can do so without fluff code that locks global error numbers since that can just be done by whatever they override the allocation handler with, I'm aware that errno is supposed to be thread specific but you never know if your software will be run on old systems that either don't make it thread specific or do a poor job of it, better to briefly lock it at the time of allocation, quickly grab the errno even if you got a valid pointer and unlock it, can always pass 0 manually after checking the pointer was indeed valid, something like the below: ... lock_mutex(&errno_mutex); errno = 0; tmp = realloc( *ptr, size ) err = errno; unlock_mutex( &errno_mutex ); if ( tmp ) { *ptr = tmp; return 0; } return err; ... You can always compile out the locks if you're compiling on a system you've detected to be conforming correctly like this: # if ... # define lock_mutex(mutex) # define unlock_mutex(mutex) # else # define lock_mutex(mutex) pthread_lock_mutex(mutex) # define unlock_mutex(mutex) pthread_unlock_mutex(mutex) # endif
Hi! I know this video is kinda old, but no-one else mentioned it so I figured I would. uuid_t is typedef'd as "unsigned char uuid[16]" which I believe is why there isn't an & put in front whenever you pass one into a function.
Its visual studio code. VScode is available on Mac, Linux and Windows. Looks like he is also using the C/C++ extensions to get code completion and syntax highlighting.
Good that you have covered this topic. I use UIDs a lot, but I do not use hex (base-16), I take all the digits 0-9 plus all uppercase English letters except 'O' as my base. It ends up being base-35 or 34, I believe. Then even an 8-char ID covers a huge range and can be used as UID. I use these to label things, for example log entries - same ID in log and in source code. Easy way to find the src which emitted a given log entry. I wrote a time-based utility that generates these IDs.
At around 9:30 you pass id into the sizeof operator, I assumed this was going to break things because of array decay, and would always return 8 (the sizeof a char*). Why is it that c-strings dont decay like this (if thats what uuid_t is under the hood)?
uuid_t is a char[16], not a char*. It will only decay to a char* if you pass it into a function, else sizeof() will return the number of bytes in the array.
@@hentaihero2201 I totally misread the code and thought he passed in id to the sizeof operator and looking back he passed the type uuid_t, my bad. Thanks!
At around 9:30 you pass id into the sizeof operator, I assumed this was going to break things because of array decay, and would always return 8 (the sizeof a char*). Why is it that c-strings dont decay like this (if thats what uuid_t is under the hood)?
While a char array may in many contexts behave like a char* it still is a different kind of object. The former contains the storage for all its elements, while the latter only has the storage for the pointer. The sizeof operator reflects this difference too, so for an array it will be the size of all its elements combined. For example, assuming our pointer size is 8, if we declare "char a[32]; char *p; char c;" then we have sizeof(a)==32, sizeof(a[3])==1, sizeof(&a[3])==8, sizeof(p)==8, sizeof(*p)==1 and sizeof(c)==1. Presumably the 'uuid_t' is an alias for the array type, not a pointer type, since it does provide the storage for entire UUID value.
As I read the man page, you shouldn't get duplicates from a single process calling uuid_generate_time() but it is possible to get duplicates from multiple concurrently running processes, although the library tries to use a number of different methods to ensure that that doesn't happen. The uuid_generate_time_safe() function returns a status indicating whether it was able to use any of those methods or not. It's also worth noting that many devices these days support MAC address cloning, so using the MAC address doesn't actually guarantee uniqueness across devices, either. Finally, it's also worth noting that the uuid library is not part of Standard C, so your mileage may vary depending on what system you're using (it may not be available at all on some systems).
Perhaps a better name would be "Usually Unique IDentifier".
Just because it's theoretically possible, doesn't mean it's relevant. That the world doesn't exist anymore in a year is as likely as getting duplicates. We still get up every morning.
A great follow-up would be to augment printf() to handle "%U" for uuids.
Great video! I just look the header and found a useful UUID_STR_LEN to use as size for the parsed string. (linux uuid-dev package). Thanks for your explanation!
Ah, nice. I definitely missed that. thanks!
I have actually generated duplicate UUIDs by accident. If you manage to seed the random number generator used by your UUID library, and make calls generate UUIDs in parallel (say on a 40 core processor) is indeed possible to generate collisions for the random and time based uuid types. Generating UUID1s that collide is actually pretty easy because according to the birthday paradox you'd expect to get 1 after only 128 calls because there are only 2**14 bits of entropy on any given node. This is why at least python's UUID1 function let's you provide a "clock_seq" which can be populated with a value from a lamport clock to back upto 16384 calls before a collision on a single node.
amazing content as usual!
I had a weird experience. I generated two UUIDs, then unparsed both of them into uuidtext variables and then used printf on them. The first unparsed ID was messed up.
char uuidtext1[sizeof(uuid_t)];
char uuidtext2[sizeof(uuid_t)];
uuid_unparse(myuuid1, uuidtext1);
uuid_unparse(myuuid2, uuidtext2);
printf("uuid1: %s
uuid2: %s
", uuidtext1, uuidtext2);
If I print the first uuidtext before I unparse the second then it looks OK. So it seems like the 2nd instance of unparsing the ID is altering the one before it? At least on my system which is a hyper-v VM running debian 11.
So I want to have a program only accessible to one computer unless I give permission if the user needs to. Is there a way where if a program is bought and user downloads it will automatically generate a uuid just for that pc?
What's going on with the endianness of those UUIDs? From my experience, the raw binary representation versus the formatted one has the first four blocks in reverse order.
hey, can you please explain what exactly is file holes? y we want then and what is that fallocate syscall? I dont find any source online
I learnt sth new today!
I put it in a do..while loop, keeps going, not sure it'll ever stop?
do{
uuid_generate_time(myuuid);
uuid_generate_time(myuuid2);
}while(myuuid != myuuid2)
Well for software keys that's fine I guess, but for runtime objects like hardware or some custom object in app I prefer to just use a global buffer with some very basic objects that reference both the linked object & the basic information about the object such as what type it is and just use the index as UUID, here's a rough example (ignoring code for actually allocating etc):
OBJECT_TYPE custom_type = { "INHERIT/CUSTOM", init_custom, term_custom, free_custom };
int create_custom( CUSTOM **custom )
{
int err;
OBJECT *object = NULL;
*custom = NULL;
err = create_object( &object, &custom_type );
if ( err ) return err;
*custom = get_object_data( object );
return 0;
}
I prefer to return error codes & pass a pointer, if the developer wants to handle the error then they can do so without fluff code that locks global error numbers since that can just be done by whatever they override the allocation handler with, I'm aware that errno is supposed to be thread specific but you never know if your software will be run on old systems that either don't make it thread specific or do a poor job of it, better to briefly lock it at the time of allocation, quickly grab the errno even if you got a valid pointer and unlock it, can always pass 0 manually after checking the pointer was indeed valid, something like the below:
...
lock_mutex(&errno_mutex);
errno = 0;
tmp = realloc( *ptr, size )
err = errno;
unlock_mutex( &errno_mutex );
if ( tmp )
{
*ptr = tmp;
return 0;
}
return err;
...
You can always compile out the locks if you're compiling on a system you've detected to be conforming correctly like this:
# if ...
# define lock_mutex(mutex)
# define unlock_mutex(mutex)
# else
# define lock_mutex(mutex) pthread_lock_mutex(mutex)
# define unlock_mutex(mutex) pthread_unlock_mutex(mutex)
# endif
great videos on your channel, very well explained. liked the printf customization also. so +1 sub! 👍
Thanks, Katia.
Hi! I know this video is kinda old, but no-one else mentioned it so I figured I would. uuid_t is typedef'd as "unsigned char uuid[16]" which I believe is why there isn't an & put in front whenever you pass one into a function.
I think libuuid just won the price for the strangest api 😂 uuid_t as an array?? "unparse" to what feels for me like a "parse"??
would be nice to see a cryptography video
Thanks. I'll see what I can do.
Can somebody knows what is the font family of vscode that Jacob uses?
Looks like cascadia code
He has a video on that.
Hi Jacob, sorry for this totally stupid question but which IDE are you using in your videos like this one ? Is it on a Mac ? Tx !
Its visual studio code. VScode is available on Mac, Linux and Windows. Looks like he is also using the C/C++ extensions to get code completion and syntax highlighting.
your 'raw' function also didn't print the zeros :-/
I would love to see a video about cryptography!
Good that you have covered this topic. I use UIDs a lot, but I do not use hex (base-16), I take all the digits 0-9 plus all uppercase English letters except 'O' as my base. It ends up being base-35 or 34, I believe. Then even an 8-char ID covers a huge range and can be used as UID. I use these to label things, for example log entries - same ID in log and in source code. Easy way to find the src which emitted a given log entry. I wrote a time-based utility that generates these IDs.
At around 9:30 you pass id into the sizeof operator, I assumed this was going to break things because of array decay, and would always return 8 (the sizeof a char*). Why is it that c-strings dont decay like this (if thats what uuid_t is under the hood)?
uuid_t is a char[16], not a char*. It will only decay to a char* if you pass it into a function, else sizeof() will return the number of bytes in the array.
@@hentaihero2201 I totally misread the code and thought he passed in id to the sizeof operator and looking back he passed the type uuid_t, my bad. Thanks!
More crypto please
Instead of spending 20 minutes watching this video, just do
cat /proc/sys/kernel/random/uuid
Available on all good operating systems!
Yikes. UUIDTEXTSIZE needs brackets.
#define UUIDTEXTSIZE ((sizeof(uuid_t) * 2) + 5)
At around 9:30 you pass id into the sizeof operator, I assumed this was going to break things because of array decay, and would always return 8 (the sizeof a char*). Why is it that c-strings dont decay like this (if thats what uuid_t is under the hood)?
While a char array may in many contexts behave like a char* it still is a different kind of object. The former contains the storage for all its elements, while the latter only has the storage for the pointer. The sizeof operator reflects this difference too, so for an array it will be the size of all its elements combined. For example, assuming our pointer size is 8, if we declare "char a[32]; char *p; char c;" then we have sizeof(a)==32, sizeof(a[3])==1, sizeof(&a[3])==8, sizeof(p)==8, sizeof(*p)==1 and sizeof(c)==1. Presumably the 'uuid_t' is an alias for the array type, not a pointer type, since it does provide the storage for entire UUID value.