Last active
November 17, 2023 13:05
-
-
Save LiosK/d33fe43d2940e6a0a23d4d9f371a81d1 to your computer and use it in GitHub Desktop.
Experimental stateless UUIDv7 implementation as of RFC draft 02
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
/** | |
* uuidv7.c - Experimental stateless UUIDv7 implementation as of RFC draft 02 | |
* | |
* Required: `clock_gettime()`, `arc4random_buf()`, and ~15-microseconds or | |
* finer system clock resolution. | |
* | |
* This implementation employs 52-bit timestamp (consisting of 36-bit whole | |
* seconds and 16-bit sub-second fraction) and 70-bit cryptographically strong | |
* random integer in the following layout. It does not guarantee the monotonic | |
* order of the generated IDs within the same timestamp (i.e. within ~15 | |
* microseconds). | |
* | |
* ``` | |
* 0 1 2 3 | |
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
* | unixts | | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
* |unixts | subsec | ver |subsec | rand | | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
* |var| rand | | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
* | rand | | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
* ``` | |
* | |
* Copyright 2022 LiosK | |
* | |
* Licensed under the Apache License, Version 2.0 | |
*/ | |
#include <assert.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <time.h> | |
/** | |
* @param out uint8_t id[16] | |
*/ | |
void uuidv7_generate(uint8_t *out) { | |
struct timespec tp; | |
clock_gettime(CLOCK_REALTIME, &tp); | |
uint64_t timestamp52 = | |
((uint64_t)tp.tv_sec << 16) | (((uint64_t)tp.tv_nsec << 16) / 1000000000); | |
out[6] = 0x70 | (timestamp52 & 0x0F); // set version bits | |
timestamp52 >>= 4; | |
for (int i = 0; i < 6; i++) { | |
out[5 - i] = (uint8_t)timestamp52; | |
timestamp52 >>= 8; | |
} | |
arc4random_buf(&out[7], sizeof(uint8_t) * 9); | |
out[8] = 0x80 | (out[8] & 0x3F); // set variant bits | |
} | |
/** | |
* @param id uint8_t id[16] | |
* @param out char text[37] | |
*/ | |
void uuidv7_to_string(const uint8_t *id, char *out) { | |
static const char digits[16] = "0123456789abcdef"; | |
char *p = out; | |
for (int i = 0; i < 16; i++) { | |
if (i == 4 || i == 6 || i == 8 || i == 10) { | |
*p++ = '-'; | |
} | |
*p++ = digits[id[i] >> 4]; | |
*p++ = digits[id[i] & 0xF]; | |
} | |
*p = '\0'; | |
} | |
static long get_clock_resolution() { | |
struct timespec tp; | |
int error = clock_getres(CLOCK_REALTIME, &tp); | |
return error == 0 ? tp.tv_nsec : 1000000000; | |
} | |
int main() { | |
uint8_t id[16]; | |
char text[37]; | |
assert(get_clock_resolution() <= (1000000000 >> 16)); | |
uuidv7_generate(id); | |
uuidv7_to_string(id, text); | |
puts(text); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment