Skip to content

Instantly share code, notes, and snippets.

@LiosK
Last active November 17, 2023 13:05
Show Gist options
  • Save LiosK/d33fe43d2940e6a0a23d4d9f371a81d1 to your computer and use it in GitHub Desktop.
Save LiosK/d33fe43d2940e6a0a23d4d9f371a81d1 to your computer and use it in GitHub Desktop.
Experimental stateless UUIDv7 implementation as of RFC draft 02
/**
* 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