UUID v7 for C
March 24, 2022 ยท View on GitHub
#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
*/