File Coverage

include/horus_uuid.h
Criterion Covered Total %
statement 140 145 96.5
branch 20 24 83.3
condition n/a
subroutine n/a
pod n/a
total 160 169 94.6


line stmt bran cond sub pod time code
1             #ifndef HORUS_UUID_H
2             #define HORUS_UUID_H
3              
4             /*
5             * horus_uuid.h - Core UUID generation: v1-v8, NIL, MAX
6             *
7             * All generators write into a 16-byte output buffer.
8             * Version and variant bits are stamped last.
9             */
10              
11             #include
12             #include
13             #include /* getuid, getgid */
14              
15             /* ── Version/variant stamping ───────────────────────────────────── */
16              
17 5040           static inline void horus_stamp_version_variant(unsigned char *uuid, int version) {
18 5040           uuid[6] = (uuid[6] & 0x0F) | ((unsigned char)(version) << 4);
19 5040           uuid[8] = (uuid[8] & 0x3F) | 0x80;
20 5040           }
21              
22             /* ── NIL UUID ───────────────────────────────────────────────────── */
23              
24 15           static inline void horus_uuid_nil(unsigned char *out) {
25 15           memset(out, 0x00, 16);
26 15           }
27              
28             /* ── MAX UUID ───────────────────────────────────────────────────── */
29              
30 14           static inline void horus_uuid_max(unsigned char *out) {
31 14           memset(out, 0xFF, 16);
32 14           }
33              
34             /* ── v4: Random ─────────────────────────────────────────────────── */
35              
36 1129           static inline void horus_uuid_v4(unsigned char *out) {
37 1129           horus_random_bytes(out, 16);
38 1129           horus_stamp_version_variant(out, 4);
39 1129           }
40              
41             /* ── v1: Time-based (Gregorian) ─────────────────────────────────── */
42              
43             typedef struct {
44             unsigned char node[6];
45             uint16_t clock_seq;
46             uint64_t last_time;
47             int initialized;
48             } horus_v1_state_t;
49              
50 6           static inline void horus_v1_init_state(horus_v1_state_t *state) {
51 6           horus_random_bytes(state->node, 6);
52 6           state->node[0] |= 0x01; /* multicast bit per RFC 9562 */
53             {
54             unsigned char cs[2];
55 6           horus_random_bytes(cs, 2);
56 6           state->clock_seq = ((uint16_t)(cs[0] & 0x3F) << 8) | (uint16_t)cs[1];
57             }
58 6           state->last_time = 0;
59 6           state->initialized = 1;
60 6           }
61              
62 113           static inline void horus_uuid_v1(unsigned char *out, horus_v1_state_t *state) {
63             uint64_t ts;
64             uint32_t time_low;
65             uint16_t time_mid, time_hi;
66              
67 113 100         if (!state->initialized)
68 4           horus_v1_init_state(state);
69              
70 113           ts = horus_gregorian_100ns();
71              
72 113 50         if (ts <= state->last_time) {
73 0           state->clock_seq = (state->clock_seq + 1) & 0x3FFF;
74             }
75 113           state->last_time = ts;
76              
77 113           time_low = (uint32_t)(ts & 0xFFFFFFFF);
78 113           time_mid = (uint16_t)((ts >> 32) & 0xFFFF);
79 113           time_hi = (uint16_t)((ts >> 48) & 0x0FFF);
80              
81             /* time_low: bytes 0-3 (big-endian) */
82 113           out[0] = (unsigned char)(time_low >> 24);
83 113           out[1] = (unsigned char)(time_low >> 16);
84 113           out[2] = (unsigned char)(time_low >> 8);
85 113           out[3] = (unsigned char)(time_low);
86             /* time_mid: bytes 4-5 */
87 113           out[4] = (unsigned char)(time_mid >> 8);
88 113           out[5] = (unsigned char)(time_mid);
89             /* time_hi_and_version: bytes 6-7 */
90 113           out[6] = (unsigned char)(time_hi >> 8);
91 113           out[7] = (unsigned char)(time_hi);
92             /* clock_seq_hi_and_variant: byte 8 */
93 113           out[8] = (unsigned char)(state->clock_seq >> 8);
94             /* clock_seq_low: byte 9 */
95 113           out[9] = (unsigned char)(state->clock_seq);
96             /* node: bytes 10-15 */
97 113           memcpy(out + 10, state->node, 6);
98              
99 113           horus_stamp_version_variant(out, 1);
100 113           }
101              
102             /* ── v2: DCE Security ───────────────────────────────────────────── */
103              
104 2           static inline void horus_uuid_v2(unsigned char *out, horus_v1_state_t *state,
105             int domain, uint32_t id) {
106             uint64_t ts;
107             uint16_t time_mid, time_hi;
108              
109 2 100         if (!state->initialized)
110 1           horus_v1_init_state(state);
111              
112 2           ts = horus_gregorian_100ns();
113 2           state->last_time = ts;
114              
115 2           time_mid = (uint16_t)((ts >> 32) & 0xFFFF);
116 2           time_hi = (uint16_t)((ts >> 48) & 0x0FFF);
117              
118             /* Replace time_low with the identifier */
119 2           out[0] = (unsigned char)(id >> 24);
120 2           out[1] = (unsigned char)(id >> 16);
121 2           out[2] = (unsigned char)(id >> 8);
122 2           out[3] = (unsigned char)(id);
123             /* time_mid: bytes 4-5 */
124 2           out[4] = (unsigned char)(time_mid >> 8);
125 2           out[5] = (unsigned char)(time_mid);
126             /* time_hi_and_version: bytes 6-7 */
127 2           out[6] = (unsigned char)(time_hi >> 8);
128 2           out[7] = (unsigned char)(time_hi);
129             /* clock_seq_hi replaced with local domain */
130 2           out[8] = (unsigned char)(domain & 0xFF);
131             /* clock_seq_low */
132 2           out[9] = (unsigned char)(state->clock_seq & 0xFF);
133             /* node: bytes 10-15 */
134 2           memcpy(out + 10, state->node, 6);
135              
136 2           horus_stamp_version_variant(out, 2);
137 2           }
138              
139             /* ── v3: MD5 namespace ──────────────────────────────────────────── */
140              
141 5           static inline void horus_uuid_v3(unsigned char *out,
142             const unsigned char *ns_bytes,
143             const unsigned char *name, size_t name_len) {
144             horus_md5_ctx ctx;
145             unsigned char digest[16];
146              
147 5           horus_md5_init(&ctx);
148 5           horus_md5_update(&ctx, ns_bytes, 16);
149 5           horus_md5_update(&ctx, name, name_len);
150 5           horus_md5_final(digest, &ctx);
151              
152 5           memcpy(out, digest, 16);
153 5           horus_stamp_version_variant(out, 3);
154 5           }
155              
156             /* ── v5: SHA-1 namespace ────────────────────────────────────────── */
157              
158 5           static inline void horus_uuid_v5(unsigned char *out,
159             const unsigned char *ns_bytes,
160             const unsigned char *name, size_t name_len) {
161             horus_sha1_ctx ctx;
162             unsigned char digest[20];
163              
164 5           horus_sha1_init(&ctx);
165 5           horus_sha1_update(&ctx, ns_bytes, 16);
166 5           horus_sha1_update(&ctx, name, name_len);
167 5           horus_sha1_final(digest, &ctx);
168              
169 5           memcpy(out, digest, 16); /* first 16 bytes of SHA-1 */
170 5           horus_stamp_version_variant(out, 5);
171 5           }
172              
173             /* ── v6: Reordered time ─────────────────────────────────────────── */
174              
175             typedef struct {
176             unsigned char last[16]; /* last emitted v6 UUID for monotonic guarantee */
177             int has_last;
178             } horus_v6_state_t;
179              
180 122           static inline void horus_uuid_v6(unsigned char *out, horus_v1_state_t *v1state,
181             horus_v6_state_t *v6state) {
182             uint64_t ts;
183              
184 122 100         if (!v1state->initialized)
185 1           horus_v1_init_state(v1state);
186              
187 122           ts = horus_gregorian_100ns();
188              
189             /* v6 reorders: most significant bits first for sorting
190             * time_high (bits 59..28 of timestamp) -> bytes 0-3
191             * time_mid (bits 27..16) -> bytes 4-5
192             * time_low (bits 15..4) -> byte 6 low nibble + byte 7 */
193              
194 122           out[0] = (unsigned char)(ts >> 52);
195 122           out[1] = (unsigned char)(ts >> 44);
196 122           out[2] = (unsigned char)(ts >> 36);
197 122           out[3] = (unsigned char)(ts >> 28);
198 122           out[4] = (unsigned char)(ts >> 20);
199 122           out[5] = (unsigned char)(ts >> 12);
200 122           out[6] = (unsigned char)((ts >> 4) & 0x0F);
201 122           out[7] = (unsigned char)((ts << 4) & 0xF0);
202             /* Fill remaining bits: clock_seq and node */
203 122           out[8] = (unsigned char)(v1state->clock_seq >> 8);
204 122           out[9] = (unsigned char)(v1state->clock_seq);
205 122           memcpy(out + 10, v1state->node, 6);
206              
207 122           horus_stamp_version_variant(out, 6);
208              
209             /* Monotonic guarantee: if new UUID <= last, increment last and use that */
210 122 100         if (v6state->has_last && memcmp(out, v6state->last, 16) <= 0) {
    100          
211 111           int i, carry = 1;
212 111           memcpy(out, v6state->last, 16);
213             /* Increment bytes 8-15 (after version/variant fields) */
214 223 50         for (i = 15; i >= 8 && carry; i--) {
    100          
215 112           int val = (int)out[i] + carry;
216 112           out[i] = (unsigned char)(val & 0xFF);
217 112           carry = val >> 8;
218             }
219             /* Re-stamp variant (byte 8 top 2 bits) in case carry corrupted it */
220 111           out[8] = (out[8] & 0x3F) | 0x80;
221             }
222              
223 122           memcpy(v6state->last, out, 16);
224 122           v6state->has_last = 1;
225 122           }
226              
227             /* ── v7: Unix epoch time with monotonic counter ─────────────────── */
228              
229             typedef struct {
230             uint64_t last_ms;
231             unsigned char last_rand[8]; /* bytes 8-15 of the last UUID (after variant) */
232             } horus_v7_state_t;
233              
234 2163           static inline void horus_uuid_v7(unsigned char *out, horus_v7_state_t *state) {
235 2163           uint64_t ms = horus_unix_epoch_ms();
236              
237             /* 48-bit timestamp: bytes 0-5 */
238 2163           out[0] = (unsigned char)(ms >> 40);
239 2163           out[1] = (unsigned char)(ms >> 32);
240 2163           out[2] = (unsigned char)(ms >> 24);
241 2163           out[3] = (unsigned char)(ms >> 16);
242 2163           out[4] = (unsigned char)(ms >> 8);
243 2163           out[5] = (unsigned char)(ms);
244              
245 2163 100         if (ms == state->last_ms) {
246             /* Same millisecond: increment the random portion for monotonicity */
247             int i;
248 2144           int carry = 1;
249             /* Increment bytes 8-15 (stored in last_rand) */
250 4296 50         for (i = 7; i >= 0 && carry; i--) {
    100          
251 2152           int val = (int)state->last_rand[i] + carry;
252 2152           state->last_rand[i] = (unsigned char)(val & 0xFF);
253 2152           carry = val >> 8;
254             }
255             /* rand_a: bytes 6-7 (12 bits after version) */
256 2144 50         if (carry) {
257             /* Overflow: increment rand_a portion */
258             unsigned char r[2];
259 0           horus_random_bytes(r, 2);
260 0           out[6] = r[0];
261 0           out[7] = r[1];
262 0           horus_random_bytes(state->last_rand, 8);
263             } else {
264 2144           out[6] = state->last_rand[0];
265 2144           out[7] = state->last_rand[1];
266             }
267 2144           memcpy(out + 8, state->last_rand + 2, 6);
268             } else {
269             /* New millisecond: fresh random */
270             unsigned char r[10]; /* 2 for rand_a + 8 for rand_b */
271 19           horus_random_bytes(r, 10);
272 19           out[6] = r[0];
273 19           out[7] = r[1];
274 19           memcpy(out + 8, r + 2, 6);
275              
276             /* Save state */
277 19           state->last_ms = ms;
278 19           memcpy(state->last_rand, r, 8);
279             }
280              
281 2163           horus_stamp_version_variant(out, 7);
282              
283             /* Update saved state to reflect version/variant stamping */
284 2163           state->last_rand[0] = out[6];
285 2163           state->last_rand[1] = out[7];
286 2163           memcpy(state->last_rand + 2, out + 8, 6);
287 2163           }
288              
289             /* ── v8: Custom ─────────────────────────────────────────────────── */
290              
291 1           static inline void horus_uuid_v8(unsigned char *out,
292             const unsigned char *custom_data) {
293 1           memcpy(out, custom_data, 16);
294 1           horus_stamp_version_variant(out, 8);
295 1           }
296              
297             #endif /* HORUS_UUID_H */