LiosK / uuidv7-h

uuidv7.h - Single-file C/C++ UUIDv7 Library
Apache License 2.0
10 stars 2 forks source link

uuidv7.h - Single-file C/C++ UUIDv7 Library

Examples:

#include "uuidv7.h"

#include <stdio.h>
#include <sys/random.h>
#include <time.h>

int main(void) {
  // use high-level APIs that require concrete `uuidv7_new()` implementation
  char text[37];
  for (int i = 0; i < 8; i++) {
    uuidv7_new_string(text);
    puts(text);
  }

  // generate a UUIDv7 with the current Unix time using the low-level APIs
  struct timespec tp;
  clock_gettime(CLOCK_REALTIME, &tp);
  uint64_t unix_ts_ms = (uint64_t)tp.tv_sec * 1000 + tp.tv_nsec / 1000000;

  uint8_t rand_bytes[10];
  getentropy(rand_bytes, 10);

  uint8_t uuid[16];
  uuidv7_generate(uuid, unix_ts_ms, rand_bytes, NULL);
  uuidv7_to_string(uuid, text);
  puts(text);

  // generate another while guaranteeing ascending order of UUIDs
  clock_gettime(CLOCK_REALTIME, &tp);
  unix_ts_ms = (uint64_t)tp.tv_sec * 1000 + tp.tv_nsec / 1000000;
  getentropy(rand_bytes, 10);

  int status = uuidv7_generate(uuid, unix_ts_ms, rand_bytes, uuid);
  if (status == UUIDV7_STATUS_CLOCK_ROLLBACK)
    return 1; // error: clock moved backward by more than 10 seconds
  uuidv7_to_string(uuid, text);
  puts(text);

  return 0; // success
}

/**
 * Generates a new UUIDv7 with the current Unix time.
 *
 * `uuidv7.h` provides the primitive `uuidv7_generate()` function only. Users
 * have to integrate a real-time clock, cryptographically strong random number
 * generator, and shared state storage available in the target platform.
 *
 * @warning This example uses static variables and is NOT thread-safe.
 */
int uuidv7_new(uint8_t *uuid_out) {
  static uint8_t uuid_prev[16] = {0};
  static uint8_t rand_bytes[10] = {0};
  static int n_rand_consumed = 10;

  struct timespec tp;
  clock_gettime(CLOCK_REALTIME, &tp);
  uint64_t unix_ts_ms = (uint64_t)tp.tv_sec * 1000 + tp.tv_nsec / 1000000;

  getentropy(rand_bytes, n_rand_consumed);
  int8_t status = uuidv7_generate(uuid_prev, unix_ts_ms, rand_bytes, uuid_prev);
  n_rand_consumed = uuidv7_status_n_rand_consumed(status);

  for (int i = 0; i < 16; i++)
    uuid_out[i] = uuid_prev[i];
  return status;
}

See RFC 9562.

Primary function

static inline int8_t uuidv7_generate(uint8_t *uuid_out, uint64_t unix_ts_ms,
                                     const uint8_t *rand_bytes,
                                     const uint8_t *uuid_prev);

Generates a new UUIDv7 from the given Unix time, random bytes, and previous UUID.

See API reference for the full list of provided functions.

Field and bit layout

This implementation produces identifiers with the following bit layout:

 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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                          unix_ts_ms                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          unix_ts_ms           |  ver  |        counter        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|var|                        counter                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                             rand                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Where:

The 42-bit counter is sufficiently large, so you do not usually need to worry about overflow, but in an extremely rare circumstance where it overflows, this library increments the unix_ts_ms field to continue instant monotonic generation. As a result, the generated ID may have a greater unix_ts_ms value than that passed as the argument. (See also Why so large counter? (42bits)).

UUIDv7, by design, relies on the system clock to guarantee the monotonically increasing order of generated IDs. A generator may not be able to produce a monotonic sequence if the system clock goes backwards. This library ignores a rollback of provided unix_ts_ms and reuses the one in uuid_prev unless the rollback is considered significant (namely, more than ten seconds). If such a significant rollback takes place, this library ignores the uuid_prev argument and thus breaks the increasing order of generated IDs.

License

Licensed under the Apache License, Version 2.0.