-
-
Save niwinz/e6add8fd1dd436a2c0245f6d2963e87d to your computer and use it in GitHub Desktop.
UUID v7 for C
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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