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

*/