scru128/c
scru128.h
Go to the documentation of this file.
1/**
2 * @file scru128.h
3 *
4 * SCRU128: Sortable, Clock and Random number-based Unique identifier
5 *
6 * @version v0.4.3
7 * @copyright Licensed under the Apache License, Version 2.0
8 * @see https://github.com/scru128/c
9 */
10/*
11 * Copyright 2022-2023 The scru128/c Developers.
12 *
13 * Licensed under the Apache License, Version 2.0 (the "License");
14 * you may not use this file except in compliance with the License.
15 * You may obtain a copy of the License at
16 *
17 * http://www.apache.org/licenses/LICENSE-2.0
18 *
19 * Unless required by applicable law or agreed to in writing, software
20 * distributed under the License is distributed on an "AS IS" BASIS,
21 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 * See the License for the specific language governing permissions and
23 * limitations under the License.
24 */
25
26#ifndef SCRU128_H_AVJRBJQI
27#define SCRU128_H_AVJRBJQI
28
29#include <stdint.h>
30
31/**
32 * The size in bytes of a SCRU128 ID in the binary representation (16 bytes).
33 */
34#define SCRU128_LEN (16)
35
36/**
37 * The size in bytes of a SCRU128 ID in the string representation (25 digits +
38 * NUL).
39 */
40#define SCRU128_STR_LEN (26)
41
42/**
43 * @name Status codes returned by generator functions
44 *
45 * @{
46 */
47
48/**
49 * Indicates that the latest `timestamp` was used because it was greater than
50 * the previous one.
51 */
52#define SCRU128_GENERATOR_STATUS_NEW_TIMESTAMP (1)
53
54/**
55 * Indicates that `counter_lo` was incremented because the latest `timestamp`
56 * was no greater than the previous one.
57 */
58#define SCRU128_GENERATOR_STATUS_COUNTER_LO_INC (2)
59
60/**
61 * Indicates that `counter_hi` was incremented because `counter_lo` reached its
62 * maximum value.
63 */
64#define SCRU128_GENERATOR_STATUS_COUNTER_HI_INC (3)
65
66/**
67 * Indicates that the previous `timestamp` was incremented because `counter_hi`
68 * reached its maximum value.
69 */
70#define SCRU128_GENERATOR_STATUS_TIMESTAMP_INC (4)
71
72/**
73 * Indicates that the generator was reinitialized and the monotonic order of
74 * generated IDs was broken because the latest `timestamp` was significantly
75 * smaller than the previous one.
76 */
77#define SCRU128_GENERATOR_STATUS_ROLLBACK_RESET (5)
78
79/** Indicates that the previous generation failed. */
80#define SCRU128_GENERATOR_STATUS_ERROR (-1)
81
82/**
83 * Indicates that the previous generation was aborted because the latest
84 * `timestamp` was significantly smaller than the previous one.
85 */
86#define SCRU128_GENERATOR_STATUS_ROLLBACK_ABORT (-2)
87
88/** @} */
89
90/**
91 * Represents a SCRU128 ID generator that encapsulates the monotonic counter and
92 * other internal states.
93 *
94 * A new generator must be initialized by `scru128_generator_init()` before use.
95 */
96typedef struct Scru128Generator {
97 /** @private */
98 uint64_t _timestamp;
99
100 /** @private */
101 uint32_t _counter_hi;
102
103 /** @private */
104 uint32_t _counter_lo;
105
106 /**
107 * The timestamp at the last renewal of `counter_hi` field.
108 *
109 * @private
110 */
111 uint64_t _ts_counter_hi;
113
114/** @private */
115static const uint64_t SCRU128_MAX_TIMESTAMP = 0xffffffffffff;
116
117/** @private */
118static const uint32_t SCRU128_MAX_COUNTER_HI = 0xffffff;
119
120/** @private */
121static const uint32_t SCRU128_MAX_COUNTER_LO = 0xffffff;
122
123#ifdef __cplusplus
124extern "C" {
125#endif
126
127/**
128 * @name Identifier-related functions
129 *
130 * This library represents a binary SCRU128 ID as a 16-byte `uint8_t` array
131 * containing a 128-bit unsigned integer in the big-endian (network) byte order.
132 *
133 * @{
134 */
135
136/**
137 * Creates a SCRU128 ID from field values.
138 *
139 * @param id_out A 16-byte byte array where the created SCRU128 ID is stored.
140 * @param timestamp A 48-bit `timestamp` field value.
141 * @param counter_hi A 24-bit `counter_hi` field value.
142 * @param counter_lo A 24-bit `counter_lo` field value.
143 * @param entropy A 32-bit `entropy` field value.
144 * @return Zero on success or a non-zero integer if any argument is out of the
145 * value range of the field.
146 */
147static inline int scru128_from_fields(uint8_t *id_out, uint64_t timestamp,
148 uint32_t counter_hi, uint32_t counter_lo,
149 uint32_t entropy) {
150 if (timestamp > SCRU128_MAX_TIMESTAMP ||
151 counter_hi > SCRU128_MAX_COUNTER_HI ||
152 counter_lo > SCRU128_MAX_COUNTER_LO) {
153 return -1;
154 }
155
156 id_out[0] = timestamp >> 40;
157 id_out[1] = timestamp >> 32;
158 id_out[2] = timestamp >> 24;
159 id_out[3] = timestamp >> 16;
160 id_out[4] = timestamp >> 8;
161 id_out[5] = timestamp;
162 id_out[6] = counter_hi >> 16;
163 id_out[7] = counter_hi >> 8;
164 id_out[8] = counter_hi;
165 id_out[9] = counter_lo >> 16;
166 id_out[10] = counter_lo >> 8;
167 id_out[11] = counter_lo;
168 id_out[12] = entropy >> 24;
169 id_out[13] = entropy >> 16;
170 id_out[14] = entropy >> 8;
171 id_out[15] = entropy;
172 return 0;
173}
174
175/**
176 * Copies a SCRU128 ID from `id_src` to `id_dst`.
177 *
178 * @param id_dst A 16-byte byte array where the copied SCRU128 ID is stored.
179 * @param id_src A 16-byte big-endian byte array that represents a SCRU128 ID.
180 */
181static inline void scru128_copy(uint8_t *id_dst, const uint8_t *id_src) {
182 for (int_fast8_t i = 0; i < SCRU128_LEN; i++) {
183 id_dst[i] = id_src[i];
184 }
185}
186
187/**
188 * Creates a SCRU128 ID from a 25-digit string representation.
189 *
190 * @param id_out A 16-byte byte array where the created SCRU128 ID is stored.
191 * @param str A null-terminated character array containing the 25-digit string
192 * representation.
193 * @return Zero on success or a non-zero integer if `str` is not a valid string
194 * representation.
195 */
196static inline int scru128_from_str(uint8_t *id_out, const char *str) {
197 uint8_t src[25];
198 for (int_fast8_t i = 0; i < 25; i++) {
199 char c = str[i];
200 // clang-format off
201 src[i] = ( c == '0' ? 0 : c == '1' ? 1 : c == '2' ? 2 : c == '3' ? 3
202 : c == '4' ? 4 : c == '5' ? 5 : c == '6' ? 6 : c == '7' ? 7
203 : c == '8' ? 8 : c == '9' ? 9 : c == 'A' ? 10 : c == 'B' ? 11
204 : c == 'C' ? 12 : c == 'D' ? 13 : c == 'E' ? 14 : c == 'F' ? 15
205 : c == 'G' ? 16 : c == 'H' ? 17 : c == 'I' ? 18 : c == 'J' ? 19
206 : c == 'K' ? 20 : c == 'L' ? 21 : c == 'M' ? 22 : c == 'N' ? 23
207 : c == 'O' ? 24 : c == 'P' ? 25 : c == 'Q' ? 26 : c == 'R' ? 27
208 : c == 'S' ? 28 : c == 'T' ? 29 : c == 'U' ? 30 : c == 'V' ? 31
209 : c == 'W' ? 32 : c == 'X' ? 33 : c == 'Y' ? 34 : c == 'Z' ? 35
210 : c == 'a' ? 10 : c == 'b' ? 11 : c == 'c' ? 12 : c == 'd' ? 13
211 : c == 'e' ? 14 : c == 'f' ? 15 : c == 'g' ? 16 : c == 'h' ? 17
212 : c == 'i' ? 18 : c == 'j' ? 19 : c == 'k' ? 20 : c == 'l' ? 21
213 : c == 'm' ? 22 : c == 'n' ? 23 : c == 'o' ? 24 : c == 'p' ? 25
214 : c == 'q' ? 26 : c == 'r' ? 27 : c == 's' ? 28 : c == 't' ? 29
215 : c == 'u' ? 30 : c == 'v' ? 31 : c == 'w' ? 32 : c == 'x' ? 33
216 : c == 'y' ? 34 : c == 'z' ? 35 : 0xff);
217 // clang-format on
218 if (src[i] == 0xff) {
219 return -1; // invalid digit
220 }
221 }
222 if (str[25] != 0) {
223 return -1; // invalid length
224 }
225
226 for (int_fast8_t i = 0; i < SCRU128_LEN; i++) {
227 id_out[i] = 0;
228 }
229
230 int_fast8_t min_index = 99; // any number greater than size of output array
231 for (int_fast8_t i = -5; i < 25; i += 10) {
232 // implement Base36 using 10-digit words
233 uint64_t carry = 0;
234 for (int_fast8_t j = i < 0 ? 0 : i; j < i + 10; j++) {
235 carry = (carry * 36) + src[j];
236 }
237
238 // iterate over output array from right to left while carry != 0 but at
239 // least up to place already filled
240 int_fast8_t j = 15;
241 for (; carry > 0 || j > min_index; j--) {
242 if (j < 0) {
243 return -1; // out of 128-bit value range
244 }
245 carry += (uint64_t)id_out[j] * 3656158440062976; // 36^10
246 id_out[j] = (uint8_t)carry;
247 carry = carry >> 8;
248 }
249 min_index = j;
250 }
251 return 0;
252}
253
254/**
255 * Returns the 48-bit `timestamp` field value of a SCRU128 ID.
256 *
257 * @param id A 16-byte big-endian byte array that represents a SCRU128 ID.
258 */
259static inline uint64_t scru128_timestamp(const uint8_t *id) {
260 return (uint64_t)id[0] << 40 | (uint64_t)id[1] << 32 | (uint64_t)id[2] << 24 |
261 (uint64_t)id[3] << 16 | (uint64_t)id[4] << 8 | (uint64_t)id[5];
262}
263
264/**
265 * Returns the 24-bit `counter_hi` field value of a SCRU128 ID.
266 *
267 * @param id A 16-byte big-endian byte array that represents a SCRU128 ID.
268 */
269static inline uint32_t scru128_counter_hi(const uint8_t *id) {
270 return (uint32_t)id[6] << 16 | (uint32_t)id[7] << 8 | (uint32_t)id[8];
271}
272
273/**
274 * Returns the 24-bit `counter_lo` field value of a SCRU128 ID.
275 *
276 * @param id A 16-byte big-endian byte array that represents a SCRU128 ID.
277 */
278static inline uint32_t scru128_counter_lo(const uint8_t *id) {
279 return (uint32_t)id[9] << 16 | (uint32_t)id[10] << 8 | (uint32_t)id[11];
280}
281
282/**
283 * Returns the 32-bit `entropy` field value of a SCRU128 ID.
284 *
285 * @param id A 16-byte big-endian byte array that represents a SCRU128 ID.
286 */
287static inline uint32_t scru128_entropy(const uint8_t *id) {
288 return (uint32_t)id[12] << 24 | (uint32_t)id[13] << 16 |
289 (uint32_t)id[14] << 8 | (uint32_t)id[15];
290}
291
292/**
293 * Returns the 25-digit canonical string representation of a SCRU128 ID.
294 *
295 * @param id A 16-byte big-endian byte array that represents a SCRU128 ID.
296 * @param str_out A 26-byte character array where the returned string is stored.
297 * The returned array is a 26-byte null-terminated string consisting of 25
298 * `[0-9a-z]` characters and null.
299 */
300static inline void scru128_to_str(const uint8_t *id, char *str_out) {
301 static const char DIGITS[] = "0123456789abcdefghijklmnopqrstuvwxyz";
302
303 // zero-fill 25 elements to use in process and 26th as NUL char
304 for (int_fast8_t i = 0; i < 26; i++) {
305 str_out[i] = 0;
306 }
307
308 int_fast8_t min_index = 99; // any number greater than size of output array
309 for (int_fast8_t i = -5; i < SCRU128_LEN; i += 7) {
310 // implement Base36 using 56-bit words
311 uint64_t carry = 0;
312 for (int_fast8_t j = i < 0 ? 0 : i; j < i + 7; j++) {
313 carry = (carry << 8) | id[j];
314 }
315
316 // iterate over output array from right to left while carry != 0 but at
317 // least up to place already filled
318 int_fast8_t j = 24;
319 for (; carry > 0 || j > min_index; j--) {
320 carry += (uint64_t)str_out[j] << 56;
321 str_out[j] = carry % 36;
322 carry = carry / 36;
323 }
324 min_index = j;
325 }
326
327 for (int_fast8_t i = 0; i < 25; i++) {
328 str_out[i] = DIGITS[(unsigned char)str_out[i]];
329 }
330}
331
332/**
333 * Returns a negative integer, zero, or positive integer if `id_lft` is less
334 * than, equal to, or greater than `id_rgt`, respectively.
335 *
336 * @param id_lft A 16-byte big-endian byte array that represents a SCRU128 ID.
337 * @param id_rgt A 16-byte big-endian byte array that represents a SCRU128 ID.
338 */
339static inline int scru128_compare(const uint8_t *id_lft,
340 const uint8_t *id_rgt) {
341 for (int_fast8_t i = 0; i < SCRU128_LEN; i++) {
342 if (id_lft[i] != id_rgt[i]) {
343 return id_lft[i] < id_rgt[i] ? -1 : 1;
344 }
345 }
346 return 0;
347}
348
349/** @} */
350
351/**
352 * @name Generator-related functions
353 *
354 * @{
355 */
356
357/** Initializes a generator struct `g`. */
359 g->_timestamp = 0;
360 g->_counter_hi = 0;
361 g->_counter_lo = 0;
362 g->_ts_counter_hi = 0;
363}
364
365/**
366 * Generates a new SCRU128 ID with the given `timestamp` and random number
367 * generator, or returns an error upon significant timestamp rollback.
368 *
369 * This function returns a monotonically increasing ID by reusing the previous
370 * `timestamp` even if the one provided is smaller than the immediately
371 * preceding ID's. However, when such a clock rollback is considered significant
372 * (by more than `rollback_allowance` milliseconds), this function aborts and
373 * returns `SCRU128_GENERATOR_STATUS_ROLLBACK_ABORT` immediately.
374 *
375 * See `scru128_generate_or_reset_core()` for the other mode of generation.
376 *
377 * @param g A generator state object used to generate an ID.
378 * @param id_out A 16-byte byte array where the generated SCRU128 ID is stored.
379 * @param timestamp A 48-bit `timestamp` field value.
380 * @param arc4random A function pointer to `arc4random()` or a compatible
381 * function that returns a (cryptographically strong) random number in the range
382 * of 32-bit unsigned integer.
383 * @param rollback_allowance The amount of `timestamp` rollback that is
384 * considered significant. A suggested value is `10000` (milliseconds).
385 * @return One of `SCRU128_GENERATOR_STATUS_*` codes that describes the
386 * characteristics of generated ID. A negative return code reports an error.
387 * @attention This function is NOT thread-safe. The generator `g` should be
388 * protected from concurrent accesses using a mutex or other synchronization
389 * mechanism to avoid race conditions.
390 */
391static inline int8_t
393 uint64_t timestamp, uint32_t (*arc4random)(void),
394 uint64_t rollback_allowance) {
395 if (timestamp == 0 || timestamp > SCRU128_MAX_TIMESTAMP) {
397 } else if (rollback_allowance > SCRU128_MAX_TIMESTAMP) {
399 }
400
402 if (timestamp > g->_timestamp) {
403 g->_timestamp = timestamp;
404 g->_counter_lo = (*arc4random)() & SCRU128_MAX_COUNTER_LO;
405 } else if (timestamp + rollback_allowance >= g->_timestamp) {
406 // go on with previous timestamp if new one is not much smaller
407 g->_counter_lo++;
409 if (g->_counter_lo > SCRU128_MAX_COUNTER_LO) {
410 g->_counter_lo = 0;
411 g->_counter_hi++;
413 if (g->_counter_hi > SCRU128_MAX_COUNTER_HI) {
414 g->_counter_hi = 0;
415 // increment timestamp at counter overflow
416 g->_timestamp++;
417 g->_counter_lo = (*arc4random)() & SCRU128_MAX_COUNTER_LO;
419 }
420 }
421 } else {
422 // abort if clock went backwards to unbearable extent
424 }
425
426 if (g->_timestamp - g->_ts_counter_hi >= 1000 || g->_ts_counter_hi == 0) {
427 g->_ts_counter_hi = g->_timestamp;
428 g->_counter_hi = (*arc4random)() & SCRU128_MAX_COUNTER_HI;
429 }
430
431 if (scru128_from_fields(id_out, g->_timestamp, g->_counter_hi, g->_counter_lo,
432 (*arc4random)()) == 0) {
433 return status;
434 } else {
436 }
437}
438
439/**
440 * Generates a new SCRU128 ID with the given `timestamp` and random number
441 * generator, or resets the generator upon significant timestamp rollback.
442 *
443 * This function returns a monotonically increasing ID by reusing the previous
444 * `timestamp` even if the one provided is smaller than the immediately
445 * preceding ID's. However, when such a clock rollback is considered significant
446 * (by more than `rollback_allowance` milliseconds), this function resets the
447 * generator and returns a new ID based on the given `timestamp`, breaking the
448 * increasing order of IDs.
449 *
450 * See `scru128_generate_or_abort_core()` for the other mode of generation.
451 *
452 * @param g A generator state object used to generate an ID.
453 * @param id_out A 16-byte byte array where the generated SCRU128 ID is stored.
454 * @param timestamp A 48-bit `timestamp` field value.
455 * @param arc4random A function pointer to `arc4random()` or a compatible
456 * function that returns a (cryptographically strong) random number in the range
457 * of 32-bit unsigned integer.
458 * @param rollback_allowance The amount of `timestamp` rollback that is
459 * considered significant. A suggested value is `10000` (milliseconds).
460 * @return One of `SCRU128_GENERATOR_STATUS_*` codes that describes the
461 * characteristics of generated ID. A negative return code reports an error.
462 * @attention This function is NOT thread-safe. The generator `g` should be
463 * protected from concurrent accesses using a mutex or other synchronization
464 * mechanism to avoid race conditions.
465 */
466static inline int8_t
468 uint64_t timestamp, uint32_t (*arc4random)(void),
469 uint64_t rollback_allowance) {
470 int8_t status = scru128_generate_or_abort_core(
471 g, id_out, timestamp, arc4random, rollback_allowance);
473 // reset state and resume
474 g->_timestamp = 0;
475 g->_ts_counter_hi = 0;
476 scru128_generate_or_abort_core(g, id_out, timestamp, arc4random,
477 rollback_allowance);
479 }
480 return status;
481}
482
483/** @} */
484
485/**
486 * @name High-level generator APIs that require platform integration
487 *
488 * @{
489 */
490
491/**
492 * Generates a new SCRU128 ID from the current `timestamp`.
493 *
494 * @param g A generator state object used to generate an ID.
495 * @param id_out A 16-byte byte array where the generated SCRU128 ID is stored.
496 * @return One of `SCRU128_GENERATOR_STATUS_*` codes that describes the
497 * characteristics of generated ID. A negative return code reports an error.
498 * @note This single-file library does not provide a concrete implementation of
499 * this function, so users have to implement it to enable high-level generator
500 * APIs (if necessary) by integrating the low-level generator primitives
501 * provided by the library with the real-time clock and random number generator
502 * available in the system.
503 * @attention The thread-safety of this function is implementation-dependent,
504 * but it is usually NOT thread-safe. The generator `g` should be protected from
505 * concurrent accesses using a mutex or other synchronization mechanism to avoid
506 * race conditions.
507 */
508int scru128_generate(Scru128Generator *g, uint8_t *id_out);
509
510/**
511 * Generates a new SCRU128 ID encoded in the 25-digit canonical string
512 * representation.
513 *
514 * @param g A generator state object used to generate an ID.
515 * @param str_out A 26-byte character array where the returned string is stored.
516 * The returned array is a 26-byte null-terminated string consisting of 25
517 * `[0-9a-z]` characters and null.
518 * @return The return value of `scru128_generate()`.
519 * @note Provide a concrete implementation of `scru128_generate()` to enable
520 * this function.
521 * @attention See `scru128_generate()` for the thread-safety consideration.
522 */
523static inline int scru128_generate_string(Scru128Generator *g, char *str_out) {
524 uint8_t id[SCRU128_LEN];
525 int status = scru128_generate(g, id);
526 if (status >= 0) {
527 scru128_to_str(id, str_out);
528 }
529 return status;
530}
531
532/** @} */
533
534#ifdef __cplusplus
535} /* extern "C" { */
536#endif
537
538#endif /* #ifndef SCRU128_H_AVJRBJQI */
int scru128_generate(Scru128Generator *g, uint8_t *id_out)
Generates a new SCRU128 ID from the current timestamp.
static int scru128_from_fields(uint8_t *id_out, uint64_t timestamp, uint32_t counter_hi, uint32_t counter_lo, uint32_t entropy)
Creates a SCRU128 ID from field values.
Definition scru128.h:147
#define SCRU128_GENERATOR_STATUS_ROLLBACK_ABORT
Indicates that the previous generation was aborted because the latest timestamp was significantly sma...
Definition scru128.h:86
static uint32_t scru128_entropy(const uint8_t *id)
Returns the 32-bit entropy field value of a SCRU128 ID.
Definition scru128.h:287
static int8_t scru128_generate_or_reset_core(Scru128Generator *g, uint8_t *id_out, uint64_t timestamp, uint32_t(*arc4random)(void), uint64_t rollback_allowance)
Generates a new SCRU128 ID with the given timestamp and random number generator, or resets the genera...
Definition scru128.h:467
static int scru128_compare(const uint8_t *id_lft, const uint8_t *id_rgt)
Returns a negative integer, zero, or positive integer if id_lft is less than, equal to,...
Definition scru128.h:339
#define SCRU128_GENERATOR_STATUS_TIMESTAMP_INC
Indicates that the previous timestamp was incremented because counter_hi reached its maximum value.
Definition scru128.h:70
static uint32_t scru128_counter_hi(const uint8_t *id)
Returns the 24-bit counter_hi field value of a SCRU128 ID.
Definition scru128.h:269
#define SCRU128_GENERATOR_STATUS_ROLLBACK_RESET
Indicates that the generator was reinitialized and the monotonic order of generated IDs was broken be...
Definition scru128.h:77
static void scru128_copy(uint8_t *id_dst, const uint8_t *id_src)
Copies a SCRU128 ID from id_src to id_dst.
Definition scru128.h:181
#define SCRU128_GENERATOR_STATUS_COUNTER_LO_INC
Indicates that counter_lo was incremented because the latest timestamp was no greater than the previo...
Definition scru128.h:58
#define SCRU128_GENERATOR_STATUS_ERROR
Indicates that the previous generation failed.
Definition scru128.h:80
#define SCRU128_LEN
The size in bytes of a SCRU128 ID in the binary representation (16 bytes).
Definition scru128.h:34
static int scru128_generate_string(Scru128Generator *g, char *str_out)
Generates a new SCRU128 ID encoded in the 25-digit canonical string representation.
Definition scru128.h:523
#define SCRU128_GENERATOR_STATUS_COUNTER_HI_INC
Indicates that counter_hi was incremented because counter_lo reached its maximum value.
Definition scru128.h:64
static int scru128_from_str(uint8_t *id_out, const char *str)
Creates a SCRU128 ID from a 25-digit string representation.
Definition scru128.h:196
static uint32_t scru128_counter_lo(const uint8_t *id)
Returns the 24-bit counter_lo field value of a SCRU128 ID.
Definition scru128.h:278
static void scru128_to_str(const uint8_t *id, char *str_out)
Returns the 25-digit canonical string representation of a SCRU128 ID.
Definition scru128.h:300
static uint64_t scru128_timestamp(const uint8_t *id)
Returns the 48-bit timestamp field value of a SCRU128 ID.
Definition scru128.h:259
#define SCRU128_GENERATOR_STATUS_NEW_TIMESTAMP
Indicates that the latest timestamp was used because it was greater than the previous one.
Definition scru128.h:52
static void scru128_generator_init(Scru128Generator *g)
Initializes a generator struct g.
Definition scru128.h:358
static int8_t scru128_generate_or_abort_core(Scru128Generator *g, uint8_t *id_out, uint64_t timestamp, uint32_t(*arc4random)(void), uint64_t rollback_allowance)
Generates a new SCRU128 ID with the given timestamp and random number generator, or returns an error ...
Definition scru128.h:392
Represents a SCRU128 ID generator that encapsulates the monotonic counter and other internal states.
Definition scru128.h:96