Skip to content

Instantly share code, notes, and snippets.

@fabiolimace
Last active January 17, 2024 23:06
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save fabiolimace/9873fe7bbcb1e6dc40638a4f98676d72 to your computer and use it in GitHub Desktop.
Save fabiolimace/9873fe7bbcb1e6dc40638a4f98676d72 to your computer and use it in GitHub Desktop.
UUID v7 for C
#include <time.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/random.h>
#define UUID_T_LENGTH (16)
#define UNIX_TS_LENGTH (6)
#define RAND_A_LENGTH (2)
#define RAND_B_LENGTH (8)
typedef uint8_t uuid_t[UUID_T_LENGTH];
static struct {
uint64_t unix_ts;
uint8_t rand_a[RAND_A_LENGTH];
uint8_t rand_b[RAND_B_LENGTH];
} state;
uint64_t get_milliseconds(void)
{
struct timespec tp;
clock_gettime(CLOCK_REALTIME, &tp);
return ((tp.tv_sec * 1000) + (tp.tv_nsec / 1000000));
}
void get_random_bytes(uint8_t buffer[], size_t len)
{
getentropy(buffer, len);
if (errno != EXIT_SUCCESS)
{
exit(EXIT_FAILURE);
}
}
void create_uuid7_stateless(uuid_t uuid)
{
static const int rand_ab_length = RAND_A_LENGTH + RAND_B_LENGTH;
// get the timestamp bytes
uint64_t unix_ts = get_milliseconds();
for(int i = 0; i < UNIX_TS_LENGTH; i++)
{
uuid[UNIX_TS_LENGTH - 1 - i] = (uint8_t) (unix_ts >> (8 * i));
}
// get the random bytes
uint8_t rand_ab[rand_ab_length];
get_random_bytes(rand_ab, rand_ab_length);
for(int i = 0; i < rand_ab_length; i++)
{
uuid[UNIX_TS_LENGTH + i] = rand_ab[i];
}
// set version and variant
uuid[6] = 0x70 | (uuid[6] & 0x0f); // version 7
uuid[8] = 0x80 | (uuid[8] & 0x3f); // variant 2
}
void create_uuid7_stateful_method1_type1(uuid_t uuid)
{
// get the timestamp bytes
uint64_t unix_ts = get_milliseconds();
for(int i = 0; i < UNIX_TS_LENGTH; i++)
{
uuid[UNIX_TS_LENGTH - 1 - i] = (uint8_t) (unix_ts >> (8 * i));
}
// get the rand_a bytes
if (unix_ts > state.unix_ts) {
get_random_bytes(state.rand_a, RAND_A_LENGTH);
} else {
// increment rand_a bytes from right to left
for (int i = RAND_A_LENGTH - 1; i >= 0; i--) {
if (++state.rand_a[i] != 0x00) {
break;
}
}
}
for(int i = 0; i < RAND_A_LENGTH; i++)
{
uuid[UNIX_TS_LENGTH + i] = state.rand_a[i];
}
// get the rand_b bytes
get_random_bytes(state.rand_b, RAND_B_LENGTH);
for(int i = 0; i < RAND_B_LENGTH; i++)
{
uuid[UNIX_TS_LENGTH + RAND_A_LENGTH + i] = state.rand_b[i];
}
// save the last timestamp
state.unix_ts = unix_ts;
// set version and variant
uuid[6] = 0x70 | (uuid[6] & 0x0f); // version 7
uuid[8] = 0x80 | (uuid[8] & 0x3f); // variant 2
}
void create_uuid7_stateful_method1_type2(uuid_t uuid)
{
// get the timestamp bytes
uint64_t unix_ts = get_milliseconds();
for(int i = 0; i < UNIX_TS_LENGTH; i++)
{
uuid[UNIX_TS_LENGTH - 1 - i] = (uint8_t) (unix_ts >> (8 * i));
}
// get the rand_a bytes
if (unix_ts > state.unix_ts) {
get_random_bytes(state.rand_a, RAND_A_LENGTH);
} else {
// get random byte
uint8_t increment[1];
get_random_bytes(increment, 1);
// set increment between 1 and 255
const uint8_t increment_max = 0xff;
increment[0] = increment[0] % increment_max + 1;
// increment rand_a bytes from right to left
if ((uint32_t) state.rand_a[1] + (uint32_t) increment[0] <= increment_max)
{
state.rand_a[1] += increment[0];
} else {
state.rand_a[1] += increment[0];
state.rand_a[0] += 1;
}
}
for(int i = 0; i < RAND_A_LENGTH; i++)
{
uuid[UNIX_TS_LENGTH + i] = state.rand_a[i];
}
// get the rand_b bytes
get_random_bytes(state.rand_b, RAND_B_LENGTH);
for(int i = 0; i < RAND_B_LENGTH; i++)
{
uuid[UNIX_TS_LENGTH + RAND_A_LENGTH + i] = state.rand_b[i];
}
// save the last timestamp
state.unix_ts = unix_ts;
// set version and variant
uuid[6] = 0x70 | (uuid[6] & 0x0f); // version 7
uuid[8] = 0x80 | (uuid[8] & 0x3f); // variant 2
}
void create_uuid7_stateful_method2_type1(uuid_t uuid)
{
// get the timestamp bytes
uint64_t unix_ts = get_milliseconds();
for(int i = 0; i < UNIX_TS_LENGTH; i++)
{
uuid[UNIX_TS_LENGTH - 1 - i] = (uint8_t) (unix_ts >> (8 * i));
}
// get the random bytes
if (unix_ts > state.unix_ts) {
get_random_bytes(state.rand_a, RAND_A_LENGTH);
get_random_bytes(state.rand_b, RAND_B_LENGTH);
} else {
// increment rand_b bytes from right to left
for (int i = RAND_B_LENGTH - 1; i >= 0; i--) {
if (++state.rand_b[i] != 0x00) {
break;
}
}
}
for(int i = 0; i < RAND_A_LENGTH; i++)
{
uuid[UNIX_TS_LENGTH + i] = state.rand_a[i];
}
for(int i = 0; i < RAND_B_LENGTH; i++)
{
uuid[UNIX_TS_LENGTH + RAND_A_LENGTH + i] = state.rand_b[i];
}
// save the last timestamp
state.unix_ts = unix_ts;
// set version and variant
uuid[6] = 0x70 | (uuid[6] & 0x0f); // version 7
uuid[8] = 0x80 | (uuid[8] & 0x3f); // variant 2
}
void create_uuid7_stateful_method2_type2(uuid_t uuid)
{
// get the timestamp bytes
uint64_t unix_ts = get_milliseconds();
for(int i = 0; i < UNIX_TS_LENGTH; i++)
{
uuid[UNIX_TS_LENGTH - 1 - i] = (uint8_t) (unix_ts >> (8 * i));
}
// get the random bytes
if (unix_ts > state.unix_ts) {
get_random_bytes(state.rand_a, RAND_A_LENGTH);
get_random_bytes(state.rand_b, RAND_B_LENGTH);
} else {
// get random byte
uint8_t increment[1];
get_random_bytes(increment, 1);
// set increment between 1 and 255
const uint8_t increment_max = 0xff;
increment[0] = increment[0] % increment_max + 1;
// increment rand_b bytes from right to left
if ((uint32_t) state.rand_b[7] + (uint32_t) increment[0] <= increment_max) {
state.rand_b[7] += increment[0];
} else {
state.rand_b[7] += increment[0];
for (int i = RAND_B_LENGTH - 2; i >= 0; i--) {
if (++state.rand_b[i] != 0x00) {
break;
}
}
}
}
for(int i = 0; i < RAND_A_LENGTH; i++)
{
uuid[UNIX_TS_LENGTH + i] = state.rand_a[i];
}
for(int i = 0; i < RAND_B_LENGTH; i++)
{
uuid[UNIX_TS_LENGTH + RAND_A_LENGTH + i] = state.rand_b[i];
}
// save the last timestamp
state.unix_ts = unix_ts;
// set version and variant
uuid[6] = 0x70 | (uuid[6] & 0x0f); // version 7
uuid[8] = 0x80 | (uuid[8] & 0x3f); // variant 2
}
void print_uuid(uuid_t uuid)
{
static const char hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
int s = 0;
char str[36 + 1];
for(int i = 0; i < UUID_T_LENGTH; i++)
{
if(i == 4 || i == 6 || i == 8 || i == 10)
{
str[s++] = '-';
}
str[s++] = hex[uuid[i] >> 4];
str[s++] = hex[uuid[i] & 0x0f];
}
str[s++] ='\0';
printf("%s\n", str);
}
int main()
{
printf("\n");
printf("Stateless UUID v7:\n\n");
for(int i = 0; i < 10; i++) {
uuid_t uuid;
create_uuid7_stateless(uuid);
print_uuid(uuid);
}
printf(" ^^^^ ^^^^ ^^^^^^^^^^^^ <- always random\n\n");
printf("Stateful UUID v7 using Method 1 and Type A:\n\n");
for(int i = 0; i < 10; i++) {
uuid_t uuid;
create_uuid7_stateful_method1_type1(uuid);
print_uuid(uuid);
}
printf(" ^^^^ ^^^^^^^^^^^^ <- always random\n");
printf(" ^^^^ <- plus 1\n\n");
printf("Stateful UUID v7 using Method 1 and Type B:\n\n");
for(int i = 0; i < 10; i++) {
uuid_t uuid;
create_uuid7_stateful_method1_type2(uuid);
print_uuid(uuid);
}
printf(" ^^^^ ^^^^^^^^^^^^ <- always random\n");
printf(" ^^^^ <- plus n, where 1 <= n <= 255\n\n");
printf("Stateful UUID v7 using Method 2 and Type A:\n\n");
for(int i = 0; i < 10; i++) {
uuid_t uuid;
create_uuid7_stateful_method2_type1(uuid);
print_uuid(uuid);
}
printf(" ^^^^ ^^^^ ^^^^^^^^^^^^ <- plus 1\n\n");
printf("Stateful UUID v7 using Method 2 and Type B:\n\n");
for(int i = 0; i < 10; i++) {
uuid_t uuid;
create_uuid7_stateful_method2_type2(uuid);
print_uuid(uuid);
}
printf(" ^^^^ ^^^^ ^^^^^^^^^^^^ <- plus n, where 1 <= n <= 255\n\n");
}
/*
## OUTPUT:
Stateless UUID v7:
017fb8c8-040e-747d-b7b0-4484f54bdcf3
017fb8c8-040e-767f-9e95-260c79ad415d
017fb8c8-040e-7a67-a735-b2a6c1a226e3
017fb8c8-040e-7d89-b4da-de1d91276d32
017fb8c8-040e-7337-9317-4b8acf8113a6
017fb8c8-040e-72f7-bc9b-bbcfbf73782b
017fb8c8-040e-7e00-b5ae-3518d2bcb3fa
017fb8c8-040e-7c7a-8cd2-04264d191231
017fb8c8-040e-72d7-9117-e155c3fe2ed1
017fb8c8-040e-7b47-beff-3ac66dbfe08a
^^^^ ^^^^ ^^^^^^^^^^^^ <- always random
Stateful UUID v7 using Method 1 and Type A:
017fb8c8-040e-7635-b82d-6c1a912cc929
017fb8c8-040e-7636-81bf-2beb1b820814
017fb8c8-040e-7637-965f-a62f7582fcb5
017fb8c8-040e-7638-ad73-b13aec618618
017fb8c8-040e-7639-9406-e0a03537c37a
017fb8c8-040e-763a-a2e3-5413d99b84d4
017fb8c8-040e-763b-b362-2fe9d7946abe
017fb8c8-040e-763c-9f77-efd19f534e6e
017fb8c8-040f-7a47-b67c-2f449248fbf3
017fb8c8-040f-7a48-a8db-8aa3594b1edc
^^^^ ^^^^^^^^^^^^ <- always random
^^^^ <- plus 1
Stateful UUID v7 using Method 1 and Type B:
017fb8c8-040f-7aa8-b5e3-5dc7dc6e4c2a
017fb8c8-040f-7b30-96f8-f5c225eef8ba
017fb8c8-040f-7b93-8743-940cbf19a388
017fb8c8-040f-7c5e-b21d-f0d079cbc05d
017fb8c8-040f-7ca1-bc08-7c8d4e43e8b4
017fb8c8-040f-7cb0-974d-49932443f329
017fb8c8-040f-7cc9-865f-9d2262b4eee3
017fb8c8-040f-7dae-b084-b5e5a03289a5
017fb8c8-040f-7e77-a839-a3f4c3802a07
017fb8c8-040f-7f4d-913c-8ce5c548298b
^^^^ ^^^^^^^^^^^^ <- always random
^^^^ <- plus n, where 1 <= n <= 255
Stateful UUID v7 using Method 2 and Type A:
017fb8c8-040f-7f4d-913c-8ce5c548298c
017fb8c8-040f-7f4d-913c-8ce5c548298d
017fb8c8-040f-7f4d-913c-8ce5c548298e
017fb8c8-040f-7f4d-913c-8ce5c548298f
017fb8c8-040f-7f4d-913c-8ce5c5482990
017fb8c8-040f-7f4d-913c-8ce5c5482991
017fb8c8-040f-7f4d-913c-8ce5c5482992
017fb8c8-040f-7f4d-913c-8ce5c5482993
017fb8c8-040f-7f4d-913c-8ce5c5482994
017fb8c8-040f-7f4d-913c-8ce5c5482995
^^^^ ^^^^ ^^^^^^^^^^^^ <- plus 1
Stateful UUID v7 using Method 2 and Type B:
017fb8c8-040f-7f4d-913c-8ce5c54829e9
017fb8c8-040f-7f4d-913c-8ce5c5482a03
017fb8c8-040f-7f4d-913c-8ce5c5482a71
017fb8c8-040f-7f4d-913c-8ce5c5482ac2
017fb8c8-040f-7f4d-913c-8ce5c5482b7b
017fb8c8-040f-7f4d-913c-8ce5c5482bbf
017fb8c8-040f-7f4d-913c-8ce5c5482bd6
017fb8c8-040f-7f4d-913c-8ce5c5482c0d
017fb8c8-040f-7f4d-913c-8ce5c5482c32
017fb8c8-040f-7f4d-913c-8ce5c5482cdd
^^^^ ^^^^ ^^^^^^^^^^^^ <- plus n, where 1 <= n <= 255
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment