File Coverage

Horus.xs
Criterion Covered Total %
statement 456 594 76.7
branch 148 342 43.2
condition n/a
subroutine n/a
pod n/a
total 604 936 64.5


line stmt bran cond sub pod time code
1             #include "horus.h"
2              
3             /* ── Helper: create an SV from a UUID in the given format ──────── */
4              
5 5126           static SV * horus_uuid_to_sv(pTHX_ const unsigned char *uuid, int fmt) {
6 5126           int len = horus_format_length((horus_format_t)fmt);
7 5126           SV *sv = newSV(len + 1);
8 5126           char *buf = SvPVX(sv);
9 5126           horus_format_uuid(buf, uuid, (horus_format_t)fmt);
10 5126           buf[len] = '\0';
11 5126           SvCUR_set(sv, len);
12 5126           SvPOK_on(sv);
13 5126           return sv;
14             }
15              
16             /* ── Helper: parse namespace UUID string to binary ─────────────── */
17              
18 10           static int horus_parse_ns(pTHX_ SV *ns_sv, unsigned char *ns_out) {
19             STRLEN ns_len;
20 10           const char *ns_str = SvPV(ns_sv, ns_len);
21 10           return horus_parse_uuid(ns_out, ns_str, ns_len);
22             }
23              
24             /* ══════════════════════════════════════════════════════════════════
25             * Custom ops - bypass XS subroutine dispatch overhead (5.14+)
26             * ══════════════════════════════════════════════════════════════════ */
27              
28             #if PERL_VERSION >= 14
29              
30             /* ── Macro: generate ck_* check function ─────────────────────────
31             * Each ck function replaces the entersub op_ppaddr with our pp_* */
32              
33             #define HORUS_CK(name) \
34             static OP *horus_ck_##name(pTHX_ OP *o, GV *namegv, SV *protosv) { \
35             PERL_UNUSED_ARG(namegv); PERL_UNUSED_ARG(protosv); \
36             o->op_ppaddr = pp_horus_##name; return o; \
37             }
38              
39             /* ── XOP descriptors (forward declarations) ──────────────────── */
40              
41             /* Format constants */
42             static XOP horus_xop_fmt_str, horus_xop_fmt_hex, horus_xop_fmt_braces,
43             horus_xop_fmt_urn, horus_xop_fmt_base64, horus_xop_fmt_base32,
44             horus_xop_fmt_crockford, horus_xop_fmt_binary,
45             horus_xop_fmt_upper_str, horus_xop_fmt_upper_hex;
46              
47             /* Namespace constants */
48             static XOP horus_xop_ns_dns, horus_xop_ns_url, horus_xop_ns_oid, horus_xop_ns_x500;
49              
50             /* Generators */
51             static XOP horus_xop_uuid_v1, horus_xop_uuid_v2, horus_xop_uuid_v3,
52             horus_xop_uuid_v4, horus_xop_uuid_v5, horus_xop_uuid_v6,
53             horus_xop_uuid_v7, horus_xop_uuid_v8,
54             horus_xop_uuid_nil, horus_xop_uuid_max;
55              
56             /* Batch */
57             static XOP horus_xop_uuid_v4_bulk;
58              
59             /* Utilities */
60             static XOP horus_xop_uuid_parse, horus_xop_uuid_validate,
61             horus_xop_uuid_version, horus_xop_uuid_variant,
62             horus_xop_uuid_cmp, horus_xop_uuid_convert,
63             horus_xop_uuid_time, horus_xop_uuid_is_nil, horus_xop_uuid_is_max;
64              
65             /* ── pp_* : Format constant ops ──────────────────────────────── */
66              
67             #define PP_CONST_IV(name, val) \
68             static OP *pp_horus_##name(pTHX) { \
69             dSP; I32 markix = POPMARK; SP = PL_stack_base + markix; \
70             XPUSHs(sv_2mortal(newSViv(val))); PUTBACK; return NORMAL; \
71             } \
72             HORUS_CK(name)
73              
74 8 50         PP_CONST_IV(fmt_str, HORUS_FMT_STR)
  4            
  4            
75 16 50         PP_CONST_IV(fmt_hex, HORUS_FMT_HEX)
  8            
  8            
76 8 50         PP_CONST_IV(fmt_braces, HORUS_FMT_BRACES)
  4            
  4            
77 8 50         PP_CONST_IV(fmt_urn, HORUS_FMT_URN)
  4            
  4            
78 8 50         PP_CONST_IV(fmt_base64, HORUS_FMT_BASE64)
  4            
  4            
79 2 50         PP_CONST_IV(fmt_base32, HORUS_FMT_BASE32)
  1            
  1            
80 2 50         PP_CONST_IV(fmt_crockford, HORUS_FMT_CROCKFORD)
  1            
  1            
81 10 50         PP_CONST_IV(fmt_binary, HORUS_FMT_BINARY)
  5            
  5            
82 10 50         PP_CONST_IV(fmt_upper_str, HORUS_FMT_UPPER_STR)
  5            
  5            
83 6 50         PP_CONST_IV(fmt_upper_hex, HORUS_FMT_UPPER_HEX)
  3            
  3            
84              
85             /* ── pp_* : Namespace constant ops ───────────────────────────── */
86              
87             #define PP_CONST_PV(name, str, slen) \
88             static OP *pp_horus_##name(pTHX) { \
89             dSP; I32 markix = POPMARK; SP = PL_stack_base + markix; \
90             XPUSHs(sv_2mortal(newSVpvn(str, slen))); PUTBACK; return NORMAL; \
91             } \
92             HORUS_CK(name)
93              
94 16 50         PP_CONST_PV(ns_dns, "6ba7b810-9dad-11d1-80b4-00c04fd430c8", 36)
  8            
  8            
95 4 50         PP_CONST_PV(ns_url, "6ba7b811-9dad-11d1-80b4-00c04fd430c8", 36)
  2            
  2            
96 0 0         PP_CONST_PV(ns_oid, "6ba7b812-9dad-11d1-80b4-00c04fd430c8", 36)
  0            
  0            
97 0 0         PP_CONST_PV(ns_x500, "6ba7b814-9dad-11d1-80b4-00c04fd430c8", 36)
  0            
  0            
98              
99             /* ── pp_* : Generator ops (optional format arg) ──────────────── */
100              
101             /* uuid_v4 - hottest path, no state needed */
102 1018           static OP *pp_horus_uuid_v4(pTHX) {
103 1018           dSP;
104 1018           I32 markix = POPMARK;
105 1018           I32 ax = markix + 1;
106 1018           I32 items = SP - PL_stack_base - markix - 1;
107 1018           int fmt = HORUS_FMT_STR;
108             unsigned char uuid[16];
109              
110 1018 100         if (items > 0) fmt = SvIV(PL_stack_base[ax]);
111              
112 1018           horus_uuid_v4(uuid);
113              
114 1018           SP = PL_stack_base + markix;
115 1018 50         XPUSHs(sv_2mortal(horus_uuid_to_sv(aTHX_ uuid, fmt)));
116 1018           PUTBACK;
117 1018           return NORMAL;
118             }
119 12           HORUS_CK(uuid_v4)
120              
121             /* uuid_v1 - time-based, needs MY_CXT */
122 113           static OP *pp_horus_uuid_v1(pTHX) {
123 113           dSP;
124             dMY_CXT;
125 113           I32 markix = POPMARK;
126 113           I32 ax = markix + 1;
127 113           I32 items = SP - PL_stack_base - markix - 1;
128 113           int fmt = HORUS_FMT_STR;
129             unsigned char uuid[16];
130              
131 113 100         if (items > 0) fmt = SvIV(PL_stack_base[ax]);
132              
133 113           horus_uuid_v1(uuid, &MY_CXT.v1_state);
134              
135 113           SP = PL_stack_base + markix;
136 113 50         XPUSHs(sv_2mortal(horus_uuid_to_sv(aTHX_ uuid, fmt)));
137 113           PUTBACK;
138 113           return NORMAL;
139             }
140 7           HORUS_CK(uuid_v1)
141              
142             /* uuid_v6 - reordered time, needs MY_CXT */
143 122           static OP *pp_horus_uuid_v6(pTHX) {
144 122           dSP;
145             dMY_CXT;
146 122           I32 markix = POPMARK;
147 122           I32 ax = markix + 1;
148 122           I32 items = SP - PL_stack_base - markix - 1;
149 122           int fmt = HORUS_FMT_STR;
150             unsigned char uuid[16];
151              
152 122 50         if (items > 0) fmt = SvIV(PL_stack_base[ax]);
153              
154 122           horus_uuid_v6(uuid, &MY_CXT.v1_state, &MY_CXT.v6_state);
155              
156 122           SP = PL_stack_base + markix;
157 122 50         XPUSHs(sv_2mortal(horus_uuid_to_sv(aTHX_ uuid, fmt)));
158 122           PUTBACK;
159 122           return NORMAL;
160             }
161 4           HORUS_CK(uuid_v6)
162              
163             /* uuid_v7 - unix epoch, needs MY_CXT */
164 2112           static OP *pp_horus_uuid_v7(pTHX) {
165 2112           dSP;
166             dMY_CXT;
167 2112           I32 markix = POPMARK;
168 2112           I32 ax = markix + 1;
169 2112           I32 items = SP - PL_stack_base - markix - 1;
170 2112           int fmt = HORUS_FMT_STR;
171             unsigned char uuid[16];
172              
173 2112 100         if (items > 0) fmt = SvIV(PL_stack_base[ax]);
174              
175 2112           horus_uuid_v7(uuid, &MY_CXT.v7_state);
176              
177 2112           SP = PL_stack_base + markix;
178 2112 50         XPUSHs(sv_2mortal(horus_uuid_to_sv(aTHX_ uuid, fmt)));
179 2112           PUTBACK;
180 2112           return NORMAL;
181             }
182 8           HORUS_CK(uuid_v7)
183              
184             /* uuid_nil */
185 15           static OP *pp_horus_uuid_nil(pTHX) {
186 15           dSP;
187 15           I32 markix = POPMARK;
188 15           I32 ax = markix + 1;
189 15           I32 items = SP - PL_stack_base - markix - 1;
190 15           int fmt = HORUS_FMT_STR;
191             unsigned char uuid[16];
192              
193 15 100         if (items > 0) fmt = SvIV(PL_stack_base[ax]);
194              
195 15           horus_uuid_nil(uuid);
196              
197 15           SP = PL_stack_base + markix;
198 15 50         XPUSHs(sv_2mortal(horus_uuid_to_sv(aTHX_ uuid, fmt)));
199 15           PUTBACK;
200 15           return NORMAL;
201             }
202 8           HORUS_CK(uuid_nil)
203              
204             /* uuid_max */
205 14           static OP *pp_horus_uuid_max(pTHX) {
206 14           dSP;
207 14           I32 markix = POPMARK;
208 14           I32 ax = markix + 1;
209 14           I32 items = SP - PL_stack_base - markix - 1;
210 14           int fmt = HORUS_FMT_STR;
211             unsigned char uuid[16];
212              
213 14 100         if (items > 0) fmt = SvIV(PL_stack_base[ax]);
214              
215 14           horus_uuid_max(uuid);
216              
217 14           SP = PL_stack_base + markix;
218 14 50         XPUSHs(sv_2mortal(horus_uuid_to_sv(aTHX_ uuid, fmt)));
219 14           PUTBACK;
220 14           return NORMAL;
221             }
222 7           HORUS_CK(uuid_max)
223              
224             /* ── pp_* : Multi-arg generator ops ──────────────────────────── */
225              
226             /* uuid_v2(domain, id?, fmt?) */
227 2           static OP *pp_horus_uuid_v2(pTHX) {
228 2           dSP;
229             dMY_CXT;
230 2           I32 markix = POPMARK;
231 2           I32 ax = markix + 1;
232 2           I32 items = SP - PL_stack_base - markix - 1;
233 2           int fmt = HORUS_FMT_STR;
234             int domain;
235             uint32_t local_id;
236             unsigned char uuid[16];
237              
238 2 50         if (items < 1) croak("uuid_v2 requires at least a domain argument");
239              
240 2           domain = SvIV(PL_stack_base[ax]);
241              
242 2 50         if (items >= 2 && SvOK(PL_stack_base[ax + 1])) {
    0          
243 0           local_id = (uint32_t)SvUV(PL_stack_base[ax + 1]);
244             } else {
245 2 100         if (domain == 0) local_id = (uint32_t)getuid();
246 1 50         else if (domain == 1) local_id = (uint32_t)getgid();
247 0           else local_id = 0;
248             }
249              
250 2 50         if (items >= 3) fmt = SvIV(PL_stack_base[ax + 2]);
251              
252 2           horus_uuid_v2(uuid, &MY_CXT.v1_state, domain, local_id);
253              
254 2           SP = PL_stack_base + markix;
255 2 50         XPUSHs(sv_2mortal(horus_uuid_to_sv(aTHX_ uuid, fmt)));
256 2           PUTBACK;
257 2           return NORMAL;
258             }
259 2           HORUS_CK(uuid_v2)
260              
261             /* uuid_v3(ns, name, fmt?) */
262 5           static OP *pp_horus_uuid_v3(pTHX) {
263 5           dSP;
264 5           I32 markix = POPMARK;
265 5           I32 ax = markix + 1;
266 5           I32 items = SP - PL_stack_base - markix - 1;
267 5           int fmt = HORUS_FMT_STR;
268             unsigned char ns_bytes[16], uuid[16];
269             STRLEN name_len;
270             const char *name_str;
271              
272 5 50         if (items < 2) croak("uuid_v3 requires namespace and name arguments");
273 5 50         if (items > 2) fmt = SvIV(PL_stack_base[ax + 2]);
274              
275 5 50         if (!horus_parse_ns(aTHX_ PL_stack_base[ax], ns_bytes))
276 0           croak("Horus: invalid namespace UUID");
277              
278 5           name_str = SvPV(PL_stack_base[ax + 1], name_len);
279 5           horus_uuid_v3(uuid, ns_bytes, (const unsigned char *)name_str, name_len);
280              
281 5           SP = PL_stack_base + markix;
282 5 50         XPUSHs(sv_2mortal(horus_uuid_to_sv(aTHX_ uuid, fmt)));
283 5           PUTBACK;
284 5           return NORMAL;
285             }
286 5           HORUS_CK(uuid_v3)
287              
288             /* uuid_v5(ns, name, fmt?) */
289 5           static OP *pp_horus_uuid_v5(pTHX) {
290 5           dSP;
291 5           I32 markix = POPMARK;
292 5           I32 ax = markix + 1;
293 5           I32 items = SP - PL_stack_base - markix - 1;
294 5           int fmt = HORUS_FMT_STR;
295             unsigned char ns_bytes[16], uuid[16];
296             STRLEN name_len;
297             const char *name_str;
298              
299 5 50         if (items < 2) croak("uuid_v5 requires namespace and name arguments");
300 5 50         if (items > 2) fmt = SvIV(PL_stack_base[ax + 2]);
301              
302 5 50         if (!horus_parse_ns(aTHX_ PL_stack_base[ax], ns_bytes))
303 0           croak("Horus: invalid namespace UUID");
304              
305 5           name_str = SvPV(PL_stack_base[ax + 1], name_len);
306 5           horus_uuid_v5(uuid, ns_bytes, (const unsigned char *)name_str, name_len);
307              
308 5           SP = PL_stack_base + markix;
309 5 50         XPUSHs(sv_2mortal(horus_uuid_to_sv(aTHX_ uuid, fmt)));
310 5           PUTBACK;
311 5           return NORMAL;
312             }
313 5           HORUS_CK(uuid_v5)
314              
315             /* uuid_v8(custom_data, fmt?) */
316 1           static OP *pp_horus_uuid_v8(pTHX) {
317 1           dSP;
318 1           I32 markix = POPMARK;
319 1           I32 ax = markix + 1;
320 1           I32 items = SP - PL_stack_base - markix - 1;
321 1           int fmt = HORUS_FMT_STR;
322             unsigned char uuid[16];
323             STRLEN data_len;
324             const char *data_str;
325              
326 1 50         if (items < 1) croak("uuid_v8 requires custom data argument");
327 1 50         if (items > 1) fmt = SvIV(PL_stack_base[ax + 1]);
328              
329 1           data_str = SvPV(PL_stack_base[ax], data_len);
330 1 50         if (data_len < 16) croak("Horus: uuid_v8 requires 16 bytes of custom data");
331              
332 1           horus_uuid_v8(uuid, (const unsigned char *)data_str);
333              
334 1           SP = PL_stack_base + markix;
335 1 50         XPUSHs(sv_2mortal(horus_uuid_to_sv(aTHX_ uuid, fmt)));
336 1           PUTBACK;
337 1           return NORMAL;
338             }
339 1           HORUS_CK(uuid_v8)
340              
341             /* ── pp_* : Batch op ─────────────────────────────────────────── */
342              
343             /* uuid_v4_bulk(count, fmt?) - returns list */
344 3           static OP *pp_horus_uuid_v4_bulk(pTHX) {
345 3           dSP;
346 3           I32 markix = POPMARK;
347 3           I32 ax = markix + 1;
348 3           I32 items = SP - PL_stack_base - markix - 1;
349 3           int fmt = HORUS_FMT_STR;
350             int count, i;
351              
352 3 50         if (items < 1) croak("uuid_v4_bulk requires count argument");
353              
354 3           count = SvIV(PL_stack_base[ax]);
355 3 100         if (items > 1) fmt = SvIV(PL_stack_base[ax + 1]);
356              
357 3           SP = PL_stack_base + markix;
358              
359 3 50         if (count <= 0) {
360 0           PUTBACK;
361 0           return NORMAL;
362             }
363              
364 3 50         EXTEND(SP, count);
    50          
365              
366 3 100         if (count <= 256) {
367 11 100         for (i = 0; i < count; i++) {
368             unsigned char uuid[16];
369 10           horus_uuid_v4(uuid);
370 10           PUSHs(sv_2mortal(horus_uuid_to_sv(aTHX_ uuid, fmt)));
371             }
372             } else {
373             unsigned char *buf;
374 2           Newx(buf, (STRLEN)count * 16, unsigned char);
375 2           horus_random_bulk(buf, (size_t)count * 16);
376 1502 100         for (i = 0; i < count; i++) {
377 1500           unsigned char *uuid = buf + i * 16;
378 1500           horus_stamp_version_variant(uuid, 4);
379 1500           PUSHs(sv_2mortal(horus_uuid_to_sv(aTHX_ uuid, fmt)));
380             }
381 2           Safefree(buf);
382             }
383              
384 3           PUTBACK;
385 3           return NORMAL;
386             }
387 3           HORUS_CK(uuid_v4_bulk)
388              
389             /* ── pp_* : Utility ops ──────────────────────────────────────── */
390              
391             /* uuid_parse(input) -> binary SV */
392 50           static OP *pp_horus_uuid_parse(pTHX) {
393 50           dSP;
394 50           I32 markix = POPMARK;
395 50           I32 ax = markix + 1;
396 50           I32 items = SP - PL_stack_base - markix - 1;
397             unsigned char uuid[16];
398             STRLEN in_len;
399             const char *in_str;
400              
401 50 50         if (items < 1) croak("uuid_parse requires 1 argument");
402              
403 50           in_str = SvPV(PL_stack_base[ax], in_len);
404 50 100         if (horus_parse_uuid(uuid, in_str, in_len) != HORUS_PARSE_OK)
405 1           croak("Horus: cannot parse UUID string");
406              
407 49           SP = PL_stack_base + markix;
408 49 50         XPUSHs(sv_2mortal(newSVpvn((const char *)uuid, 16)));
409 49           PUTBACK;
410 49           return NORMAL;
411             }
412 11           HORUS_CK(uuid_parse)
413              
414             /* uuid_validate(input) -> 1/0 */
415 17           static OP *pp_horus_uuid_validate(pTHX) {
416 17           dSP;
417 17           I32 markix = POPMARK;
418 17           I32 ax = markix + 1;
419 17           I32 items = SP - PL_stack_base - markix - 1;
420             STRLEN in_len;
421             const char *in_str;
422              
423 17 50         if (items < 1) croak("uuid_validate requires 1 argument");
424              
425 17           in_str = SvPV(PL_stack_base[ax], in_len);
426              
427 17           SP = PL_stack_base + markix;
428 17 50         XPUSHs(sv_2mortal(newSViv(horus_uuid_validate(in_str, in_len))));
429 17           PUTBACK;
430 17           return NORMAL;
431             }
432 17           HORUS_CK(uuid_validate)
433              
434             /* uuid_version(input) -> int */
435 15           static OP *pp_horus_uuid_version(pTHX) {
436 15           dSP;
437 15           I32 markix = POPMARK;
438 15           I32 ax = markix + 1;
439 15           I32 items = SP - PL_stack_base - markix - 1;
440             unsigned char uuid[16];
441             STRLEN in_len;
442             const char *in_str;
443              
444 15 50         if (items < 1) croak("uuid_version requires 1 argument");
445              
446 15           in_str = SvPV(PL_stack_base[ax], in_len);
447 15 50         if (horus_parse_uuid(uuid, in_str, in_len) != HORUS_PARSE_OK)
448 0           croak("Horus: cannot parse UUID string");
449              
450 15           SP = PL_stack_base + markix;
451 15 50         XPUSHs(sv_2mortal(newSViv(horus_uuid_version_bin(uuid))));
452 15           PUTBACK;
453 15           return NORMAL;
454             }
455 15           HORUS_CK(uuid_version)
456              
457             /* uuid_variant(input) -> int */
458 8           static OP *pp_horus_uuid_variant(pTHX) {
459 8           dSP;
460 8           I32 markix = POPMARK;
461 8           I32 ax = markix + 1;
462 8           I32 items = SP - PL_stack_base - markix - 1;
463             unsigned char uuid[16];
464             STRLEN in_len;
465             const char *in_str;
466              
467 8 50         if (items < 1) croak("uuid_variant requires 1 argument");
468              
469 8           in_str = SvPV(PL_stack_base[ax], in_len);
470 8 50         if (horus_parse_uuid(uuid, in_str, in_len) != HORUS_PARSE_OK)
471 0           croak("Horus: cannot parse UUID string");
472              
473 8           SP = PL_stack_base + markix;
474 8 50         XPUSHs(sv_2mortal(newSViv(horus_uuid_variant_bin(uuid))));
475 8           PUTBACK;
476 8           return NORMAL;
477             }
478 8           HORUS_CK(uuid_variant)
479              
480             /* uuid_cmp(a, b) -> -1/0/1 */
481 5           static OP *pp_horus_uuid_cmp(pTHX) {
482 5           dSP;
483 5           I32 markix = POPMARK;
484 5           I32 ax = markix + 1;
485 5           I32 items = SP - PL_stack_base - markix - 1;
486             unsigned char a[16], b[16];
487             STRLEN a_len, b_len;
488             const char *a_str, *b_str;
489             int cmp;
490              
491 5 50         if (items < 2) croak("uuid_cmp requires 2 arguments");
492              
493 5           a_str = SvPV(PL_stack_base[ax], a_len);
494 5           b_str = SvPV(PL_stack_base[ax + 1], b_len);
495              
496 5 50         if (horus_parse_uuid(a, a_str, a_len) != HORUS_PARSE_OK)
497 0           croak("Horus: cannot parse first UUID");
498 5 50         if (horus_parse_uuid(b, b_str, b_len) != HORUS_PARSE_OK)
499 0           croak("Horus: cannot parse second UUID");
500              
501 5           cmp = horus_uuid_cmp_bin(a, b);
502              
503 5           SP = PL_stack_base + markix;
504 5 50         XPUSHs(sv_2mortal(newSViv((cmp < 0) ? -1 : (cmp > 0) ? 1 : 0)));
    100          
505 5           PUTBACK;
506 5           return NORMAL;
507             }
508 5           HORUS_CK(uuid_cmp)
509              
510             /* uuid_convert(input, fmt) -> formatted string */
511 57           static OP *pp_horus_uuid_convert(pTHX) {
512 57           dSP;
513 57           I32 markix = POPMARK;
514 57           I32 ax = markix + 1;
515 57           I32 items = SP - PL_stack_base - markix - 1;
516             unsigned char uuid[16];
517             STRLEN in_len;
518             const char *in_str;
519             int fmt;
520              
521 57 50         if (items < 2) croak("uuid_convert requires input and format arguments");
522              
523 57           in_str = SvPV(PL_stack_base[ax], in_len);
524 57           fmt = SvIV(PL_stack_base[ax + 1]);
525              
526 57 50         if (horus_parse_uuid(uuid, in_str, in_len) != HORUS_PARSE_OK)
527 0           croak("Horus: cannot parse UUID string");
528              
529 57           SP = PL_stack_base + markix;
530 57 50         XPUSHs(sv_2mortal(horus_uuid_to_sv(aTHX_ uuid, fmt)));
531 57           PUTBACK;
532 57           return NORMAL;
533             }
534 18           HORUS_CK(uuid_convert)
535              
536             /* uuid_time(input) -> NV epoch seconds */
537 6           static OP *pp_horus_uuid_time(pTHX) {
538 6           dSP;
539 6           I32 markix = POPMARK;
540 6           I32 ax = markix + 1;
541 6           I32 items = SP - PL_stack_base - markix - 1;
542             unsigned char uuid[16];
543             STRLEN in_len;
544             const char *in_str;
545              
546 6 50         if (items < 1) croak("uuid_time requires 1 argument");
547              
548 6           in_str = SvPV(PL_stack_base[ax], in_len);
549 6 50         if (horus_parse_uuid(uuid, in_str, in_len) != HORUS_PARSE_OK)
550 0           croak("Horus: cannot parse UUID string");
551              
552 6           SP = PL_stack_base + markix;
553 6 50         XPUSHs(sv_2mortal(newSVnv(horus_uuid_extract_time(uuid))));
554 6           PUTBACK;
555 6           return NORMAL;
556             }
557 6           HORUS_CK(uuid_time)
558              
559             /* uuid_is_nil(input) -> 1/0 */
560 4           static OP *pp_horus_uuid_is_nil(pTHX) {
561 4           dSP;
562 4           I32 markix = POPMARK;
563 4           I32 ax = markix + 1;
564 4           I32 items = SP - PL_stack_base - markix - 1;
565             unsigned char uuid[16];
566             STRLEN in_len;
567             const char *in_str;
568 4           int result = 0;
569              
570 4 50         if (items < 1) croak("uuid_is_nil requires 1 argument");
571              
572 4           in_str = SvPV(PL_stack_base[ax], in_len);
573 4 50         if (horus_parse_uuid(uuid, in_str, in_len) == HORUS_PARSE_OK)
574 4           result = horus_uuid_is_nil_bin(uuid);
575              
576 4           SP = PL_stack_base + markix;
577 4 50         XPUSHs(sv_2mortal(newSViv(result)));
578 4           PUTBACK;
579 4           return NORMAL;
580             }
581 4           HORUS_CK(uuid_is_nil)
582              
583             /* uuid_is_max(input) -> 1/0 */
584 4           static OP *pp_horus_uuid_is_max(pTHX) {
585 4           dSP;
586 4           I32 markix = POPMARK;
587 4           I32 ax = markix + 1;
588 4           I32 items = SP - PL_stack_base - markix - 1;
589             unsigned char uuid[16];
590             STRLEN in_len;
591             const char *in_str;
592 4           int result = 0;
593              
594 4 50         if (items < 1) croak("uuid_is_max requires 1 argument");
595              
596 4           in_str = SvPV(PL_stack_base[ax], in_len);
597 4 50         if (horus_parse_uuid(uuid, in_str, in_len) == HORUS_PARSE_OK)
598 4           result = horus_uuid_is_max_bin(uuid);
599              
600 4           SP = PL_stack_base + markix;
601 4 50         XPUSHs(sv_2mortal(newSViv(result)));
602 4           PUTBACK;
603 4           return NORMAL;
604             }
605 4           HORUS_CK(uuid_is_max)
606              
607             /* ── Macro: XOP + call checker registration ──────────────────── */
608              
609             #define HORUS_REG_XOP(c_name, desc) \
610             XopENTRY_set(&horus_xop_##c_name, xop_name, "horus_" #c_name); \
611             XopENTRY_set(&horus_xop_##c_name, xop_desc, desc); \
612             Perl_custom_op_register(aTHX_ pp_horus_##c_name, &horus_xop_##c_name);
613              
614             #define HORUS_REG_CK(perl_name, c_name) { \
615             CV *cv = get_cv("Horus::" perl_name, 0); \
616             if (cv) cv_set_call_checker(cv, horus_ck_##c_name, (SV *)cv); \
617             }
618              
619 19           static void horus_register_custom_ops(pTHX) {
620             /* Register XOP descriptors */
621 19           HORUS_REG_XOP(fmt_str, "UUID format: hyphenated")
622 19           HORUS_REG_XOP(fmt_hex, "UUID format: hex")
623 19           HORUS_REG_XOP(fmt_braces, "UUID format: braces")
624 19           HORUS_REG_XOP(fmt_urn, "UUID format: URN")
625 19           HORUS_REG_XOP(fmt_base64, "UUID format: base64")
626 19           HORUS_REG_XOP(fmt_base32, "UUID format: base32")
627 19           HORUS_REG_XOP(fmt_crockford, "UUID format: Crockford")
628 19           HORUS_REG_XOP(fmt_binary, "UUID format: binary")
629 19           HORUS_REG_XOP(fmt_upper_str, "UUID format: upper hyphenated")
630 19           HORUS_REG_XOP(fmt_upper_hex, "UUID format: upper hex")
631              
632 19           HORUS_REG_XOP(ns_dns, "UUID namespace: DNS")
633 19           HORUS_REG_XOP(ns_url, "UUID namespace: URL")
634 19           HORUS_REG_XOP(ns_oid, "UUID namespace: OID")
635 19           HORUS_REG_XOP(ns_x500, "UUID namespace: X500")
636              
637 19           HORUS_REG_XOP(uuid_v1, "generate UUID v1")
638 19           HORUS_REG_XOP(uuid_v2, "generate UUID v2")
639 19           HORUS_REG_XOP(uuid_v3, "generate UUID v3")
640 19           HORUS_REG_XOP(uuid_v4, "generate UUID v4")
641 19           HORUS_REG_XOP(uuid_v5, "generate UUID v5")
642 19           HORUS_REG_XOP(uuid_v6, "generate UUID v6")
643 19           HORUS_REG_XOP(uuid_v7, "generate UUID v7")
644 19           HORUS_REG_XOP(uuid_v8, "generate UUID v8")
645 19           HORUS_REG_XOP(uuid_nil, "generate NIL UUID")
646 19           HORUS_REG_XOP(uuid_max, "generate MAX UUID")
647              
648 19           HORUS_REG_XOP(uuid_v4_bulk, "generate UUID v4 batch")
649              
650 19           HORUS_REG_XOP(uuid_parse, "parse UUID string")
651 19           HORUS_REG_XOP(uuid_validate, "validate UUID string")
652 19           HORUS_REG_XOP(uuid_version, "extract UUID version")
653 19           HORUS_REG_XOP(uuid_variant, "extract UUID variant")
654 19           HORUS_REG_XOP(uuid_cmp, "compare two UUIDs")
655 19           HORUS_REG_XOP(uuid_convert, "convert UUID format")
656 19           HORUS_REG_XOP(uuid_time, "extract UUID timestamp")
657 19           HORUS_REG_XOP(uuid_is_nil, "check UUID is NIL")
658 19           HORUS_REG_XOP(uuid_is_max, "check UUID is MAX")
659              
660             /* Wire call checkers to intercept at compile time */
661 19 50         HORUS_REG_CK("UUID_FMT_STR", fmt_str)
662 19 50         HORUS_REG_CK("UUID_FMT_HEX", fmt_hex)
663 19 50         HORUS_REG_CK("UUID_FMT_BRACES", fmt_braces)
664 19 50         HORUS_REG_CK("UUID_FMT_URN", fmt_urn)
665 19 50         HORUS_REG_CK("UUID_FMT_BASE64", fmt_base64)
666 19 50         HORUS_REG_CK("UUID_FMT_BASE32", fmt_base32)
667 19 50         HORUS_REG_CK("UUID_FMT_CROCKFORD", fmt_crockford)
668 19 50         HORUS_REG_CK("UUID_FMT_BINARY", fmt_binary)
669 19 50         HORUS_REG_CK("UUID_FMT_UPPER_STR", fmt_upper_str)
670 19 50         HORUS_REG_CK("UUID_FMT_UPPER_HEX", fmt_upper_hex)
671              
672 19 50         HORUS_REG_CK("UUID_NS_DNS", ns_dns)
673 19 50         HORUS_REG_CK("UUID_NS_URL", ns_url)
674 19 50         HORUS_REG_CK("UUID_NS_OID", ns_oid)
675 19 50         HORUS_REG_CK("UUID_NS_X500", ns_x500)
676              
677 19 50         HORUS_REG_CK("uuid_v1", uuid_v1)
678 19 50         HORUS_REG_CK("uuid_v2", uuid_v2)
679 19 50         HORUS_REG_CK("uuid_v3", uuid_v3)
680 19 50         HORUS_REG_CK("uuid_v4", uuid_v4)
681 19 50         HORUS_REG_CK("uuid_v5", uuid_v5)
682 19 50         HORUS_REG_CK("uuid_v6", uuid_v6)
683 19 50         HORUS_REG_CK("uuid_v7", uuid_v7)
684 19 50         HORUS_REG_CK("uuid_v8", uuid_v8)
685 19 50         HORUS_REG_CK("uuid_nil", uuid_nil)
686 19 50         HORUS_REG_CK("uuid_max", uuid_max)
687              
688 19 50         HORUS_REG_CK("uuid_v4_bulk", uuid_v4_bulk)
689              
690 19 50         HORUS_REG_CK("uuid_parse", uuid_parse)
691 19 50         HORUS_REG_CK("uuid_validate", uuid_validate)
692 19 50         HORUS_REG_CK("uuid_version", uuid_version)
693 19 50         HORUS_REG_CK("uuid_variant", uuid_variant)
694 19 50         HORUS_REG_CK("uuid_cmp", uuid_cmp)
695 19 50         HORUS_REG_CK("uuid_convert", uuid_convert)
696 19 50         HORUS_REG_CK("uuid_time", uuid_time)
697 19 50         HORUS_REG_CK("uuid_is_nil", uuid_is_nil)
698 19 50         HORUS_REG_CK("uuid_is_max", uuid_is_max)
699 19           }
700              
701             #endif /* PERL_VERSION >= 14 */
702              
703             /* ══════════════════════════════════════════════════════════════════
704             * XS module definition - XSUBs serve as fallbacks for Perl < 5.14
705             * ══════════════════════════════════════════════════════════════════ */
706              
707             MODULE = Horus PACKAGE = Horus
708              
709             BOOT:
710             {
711             MY_CXT_INIT;
712 19           memset(&MY_CXT.v1_state, 0, sizeof(horus_v1_state_t));
713 19           memset(&MY_CXT.v6_state, 0, sizeof(horus_v6_state_t));
714 19           memset(&MY_CXT.v7_state, 0, sizeof(horus_v7_state_t));
715 19           horus_pool_refill();
716             #if PERL_VERSION >= 14
717 19           horus_register_custom_ops(aTHX);
718             #endif
719             }
720              
721             #ifdef USE_ITHREADS
722              
723             void
724             CLONE(...)
725             CODE:
726             MY_CXT_CLONE;
727             memset(&MY_CXT.v1_state, 0, sizeof(horus_v1_state_t));
728             memset(&MY_CXT.v6_state, 0, sizeof(horus_v6_state_t));
729             memset(&MY_CXT.v7_state, 0, sizeof(horus_v7_state_t));
730              
731             #endif
732              
733             int
734             UUID_FMT_STR()
735             CODE:
736 0 0         RETVAL = HORUS_FMT_STR;
737             OUTPUT:
738             RETVAL
739              
740             int
741             UUID_FMT_HEX()
742             CODE:
743 0 0         RETVAL = HORUS_FMT_HEX;
744             OUTPUT:
745             RETVAL
746              
747             int
748             UUID_FMT_BRACES()
749             CODE:
750 0 0         RETVAL = HORUS_FMT_BRACES;
751             OUTPUT:
752             RETVAL
753              
754             int
755             UUID_FMT_URN()
756             CODE:
757 0 0         RETVAL = HORUS_FMT_URN;
758             OUTPUT:
759             RETVAL
760              
761             int
762             UUID_FMT_BASE64()
763             CODE:
764 0 0         RETVAL = HORUS_FMT_BASE64;
765             OUTPUT:
766             RETVAL
767              
768             int
769             UUID_FMT_BASE32()
770             CODE:
771 0 0         RETVAL = HORUS_FMT_BASE32;
772             OUTPUT:
773             RETVAL
774              
775             int
776             UUID_FMT_CROCKFORD()
777             CODE:
778 0 0         RETVAL = HORUS_FMT_CROCKFORD;
779             OUTPUT:
780             RETVAL
781              
782             int
783             UUID_FMT_BINARY()
784             CODE:
785 0 0         RETVAL = HORUS_FMT_BINARY;
786             OUTPUT:
787             RETVAL
788              
789             int
790             UUID_FMT_UPPER_STR()
791             CODE:
792 0 0         RETVAL = HORUS_FMT_UPPER_STR;
793             OUTPUT:
794             RETVAL
795              
796             int
797             UUID_FMT_UPPER_HEX()
798             CODE:
799 0 0         RETVAL = HORUS_FMT_UPPER_HEX;
800             OUTPUT:
801             RETVAL
802              
803              
804             SV *
805             UUID_NS_DNS()
806             CODE:
807 0           RETVAL = newSVpvn("6ba7b810-9dad-11d1-80b4-00c04fd430c8", 36);
808             OUTPUT:
809             RETVAL
810              
811             SV *
812             UUID_NS_URL()
813             CODE:
814 0           RETVAL = newSVpvn("6ba7b811-9dad-11d1-80b4-00c04fd430c8", 36);
815             OUTPUT:
816             RETVAL
817              
818             SV *
819             UUID_NS_OID()
820             CODE:
821 0           RETVAL = newSVpvn("6ba7b812-9dad-11d1-80b4-00c04fd430c8", 36);
822             OUTPUT:
823             RETVAL
824              
825             SV *
826             UUID_NS_X500()
827             CODE:
828 0           RETVAL = newSVpvn("6ba7b814-9dad-11d1-80b4-00c04fd430c8", 36);
829             OUTPUT:
830             RETVAL
831              
832              
833             SV *
834             uuid_v1(fmt = HORUS_FMT_STR)
835             int fmt
836             CODE:
837             {
838             dMY_CXT;
839             unsigned char uuid[16];
840 0           horus_uuid_v1(uuid, &MY_CXT.v1_state);
841 0           RETVAL = horus_uuid_to_sv(aTHX_ uuid, fmt);
842             }
843             OUTPUT:
844             RETVAL
845              
846             SV *
847             uuid_v2(domain, id = 0, fmt = HORUS_FMT_STR)
848             int domain
849             unsigned int id
850             int fmt
851             CODE:
852             {
853             dMY_CXT;
854             unsigned char uuid[16];
855             uint32_t local_id;
856 0 0         if (items < 2 || !SvOK(ST(1))) {
    0          
857 0 0         if (domain == 0)
858 0           local_id = (uint32_t)getuid();
859 0 0         else if (domain == 1)
860 0           local_id = (uint32_t)getgid();
861             else
862 0           local_id = 0;
863             } else {
864 0           local_id = (uint32_t)id;
865             }
866 0           horus_uuid_v2(uuid, &MY_CXT.v1_state, domain, local_id);
867 0           RETVAL = horus_uuid_to_sv(aTHX_ uuid, fmt);
868             }
869             OUTPUT:
870             RETVAL
871              
872             SV *
873             uuid_v3(ns_uuid, name, fmt = HORUS_FMT_STR)
874             SV *ns_uuid
875             SV *name
876             int fmt
877             CODE:
878             {
879             unsigned char ns_bytes[16];
880             unsigned char uuid[16];
881             STRLEN name_len;
882             const char *name_str;
883              
884 0 0         if (!horus_parse_ns(aTHX_ ns_uuid, ns_bytes))
885 0           croak("Horus: invalid namespace UUID");
886              
887 0           name_str = SvPV(name, name_len);
888 0           horus_uuid_v3(uuid, ns_bytes, (const unsigned char *)name_str, name_len);
889 0           RETVAL = horus_uuid_to_sv(aTHX_ uuid, fmt);
890             }
891             OUTPUT:
892             RETVAL
893              
894             SV *
895             uuid_v4(fmt = HORUS_FMT_STR)
896             int fmt
897             CODE:
898             {
899             unsigned char uuid[16];
900 0           horus_uuid_v4(uuid);
901 0           RETVAL = horus_uuid_to_sv(aTHX_ uuid, fmt);
902             }
903             OUTPUT:
904             RETVAL
905              
906             SV *
907             uuid_v5(ns_uuid, name, fmt = HORUS_FMT_STR)
908             SV *ns_uuid
909             SV *name
910             int fmt
911             CODE:
912             {
913             unsigned char ns_bytes[16];
914             unsigned char uuid[16];
915             STRLEN name_len;
916             const char *name_str;
917              
918 0 0         if (!horus_parse_ns(aTHX_ ns_uuid, ns_bytes))
919 0           croak("Horus: invalid namespace UUID");
920              
921 0           name_str = SvPV(name, name_len);
922 0           horus_uuid_v5(uuid, ns_bytes, (const unsigned char *)name_str, name_len);
923 0           RETVAL = horus_uuid_to_sv(aTHX_ uuid, fmt);
924             }
925             OUTPUT:
926             RETVAL
927              
928             SV *
929             uuid_v6(fmt = HORUS_FMT_STR)
930             int fmt
931             CODE:
932             {
933             dMY_CXT;
934             unsigned char uuid[16];
935 0           horus_uuid_v6(uuid, &MY_CXT.v1_state, &MY_CXT.v6_state);
936 0           RETVAL = horus_uuid_to_sv(aTHX_ uuid, fmt);
937             }
938             OUTPUT:
939             RETVAL
940              
941             SV *
942             uuid_v7(fmt = HORUS_FMT_STR)
943             int fmt
944             CODE:
945             {
946             dMY_CXT;
947             unsigned char uuid[16];
948 0           horus_uuid_v7(uuid, &MY_CXT.v7_state);
949 0           RETVAL = horus_uuid_to_sv(aTHX_ uuid, fmt);
950             }
951             OUTPUT:
952             RETVAL
953              
954             SV *
955             uuid_v8(custom_data, fmt = HORUS_FMT_STR)
956             SV *custom_data
957             int fmt
958             CODE:
959             {
960             unsigned char uuid[16];
961             STRLEN data_len;
962 0           const char *data_str = SvPV(custom_data, data_len);
963              
964 0 0         if (data_len < 16)
965 0           croak("Horus: uuid_v8 requires 16 bytes of custom data");
966              
967 0           horus_uuid_v8(uuid, (const unsigned char *)data_str);
968 0           RETVAL = horus_uuid_to_sv(aTHX_ uuid, fmt);
969             }
970             OUTPUT:
971             RETVAL
972              
973             SV *
974             uuid_nil(fmt = HORUS_FMT_STR)
975             int fmt
976             CODE:
977             {
978             unsigned char uuid[16];
979 0           horus_uuid_nil(uuid);
980 0           RETVAL = horus_uuid_to_sv(aTHX_ uuid, fmt);
981             }
982             OUTPUT:
983             RETVAL
984              
985             SV *
986             uuid_max(fmt = HORUS_FMT_STR)
987             int fmt
988             CODE:
989             {
990             unsigned char uuid[16];
991 0           horus_uuid_max(uuid);
992 0           RETVAL = horus_uuid_to_sv(aTHX_ uuid, fmt);
993             }
994             OUTPUT:
995             RETVAL
996              
997             void
998             uuid_v4_bulk(count, fmt = HORUS_FMT_STR)
999             int count
1000             int fmt
1001             PPCODE:
1002             {
1003             int i;
1004 0 0         if (count <= 0)
1005 0           XSRETURN_EMPTY;
1006              
1007 0 0         if (count <= 256) {
1008 0 0         for (i = 0; i < count; i++) {
1009             unsigned char uuid[16];
1010 0           horus_uuid_v4(uuid);
1011 0 0         mXPUSHs(horus_uuid_to_sv(aTHX_ uuid, fmt));
1012             }
1013             } else {
1014             unsigned char *buf;
1015 0           Newx(buf, (STRLEN)count * 16, unsigned char);
1016 0           horus_random_bulk(buf, (size_t)count * 16);
1017              
1018 0 0         for (i = 0; i < count; i++) {
1019 0           unsigned char *uuid = buf + i * 16;
1020 0           horus_stamp_version_variant(uuid, 4);
1021 0 0         mXPUSHs(horus_uuid_to_sv(aTHX_ uuid, fmt));
1022             }
1023              
1024 0           Safefree(buf);
1025             }
1026 0           XSRETURN(count);
1027             }
1028              
1029             SV *
1030             uuid_parse(input)
1031             SV *input
1032             CODE:
1033             {
1034             unsigned char uuid[16];
1035             STRLEN in_len;
1036 0           const char *in_str = SvPV(input, in_len);
1037              
1038 0 0         if (horus_parse_uuid(uuid, in_str, in_len) != HORUS_PARSE_OK)
1039 0           croak("Horus: cannot parse UUID string");
1040              
1041 0           RETVAL = newSVpvn((const char *)uuid, 16);
1042             }
1043             OUTPUT:
1044             RETVAL
1045              
1046             int
1047             uuid_validate(input)
1048             SV *input
1049             CODE:
1050             {
1051             STRLEN in_len;
1052 0           const char *in_str = SvPV(input, in_len);
1053 0           RETVAL = horus_uuid_validate(in_str, in_len);
1054             }
1055             OUTPUT:
1056             RETVAL
1057              
1058             int
1059             uuid_version(input)
1060             SV *input
1061             CODE:
1062             {
1063             unsigned char uuid[16];
1064             STRLEN in_len;
1065 0           const char *in_str = SvPV(input, in_len);
1066              
1067 0 0         if (horus_parse_uuid(uuid, in_str, in_len) != HORUS_PARSE_OK)
1068 0           croak("Horus: cannot parse UUID string");
1069              
1070 0           RETVAL = horus_uuid_version_bin(uuid);
1071             }
1072             OUTPUT:
1073             RETVAL
1074              
1075             int
1076             uuid_variant(input)
1077             SV *input
1078             CODE:
1079             {
1080             unsigned char uuid[16];
1081             STRLEN in_len;
1082 0           const char *in_str = SvPV(input, in_len);
1083              
1084 0 0         if (horus_parse_uuid(uuid, in_str, in_len) != HORUS_PARSE_OK)
1085 0           croak("Horus: cannot parse UUID string");
1086              
1087 0           RETVAL = horus_uuid_variant_bin(uuid);
1088             }
1089             OUTPUT:
1090             RETVAL
1091              
1092             int
1093             uuid_cmp(uuid_a, uuid_b)
1094             SV *uuid_a
1095             SV *uuid_b
1096             CODE:
1097             {
1098             unsigned char a[16], b[16];
1099             STRLEN a_len, b_len;
1100 0           const char *a_str = SvPV(uuid_a, a_len);
1101 0           const char *b_str = SvPV(uuid_b, b_len);
1102             int cmp;
1103              
1104 0 0         if (horus_parse_uuid(a, a_str, a_len) != HORUS_PARSE_OK)
1105 0           croak("Horus: cannot parse first UUID");
1106 0 0         if (horus_parse_uuid(b, b_str, b_len) != HORUS_PARSE_OK)
1107 0           croak("Horus: cannot parse second UUID");
1108              
1109 0           cmp = horus_uuid_cmp_bin(a, b);
1110 0 0         RETVAL = (cmp < 0) ? -1 : (cmp > 0) ? 1 : 0;
    0          
1111             }
1112             OUTPUT:
1113             RETVAL
1114              
1115             SV *
1116             uuid_convert(input, fmt)
1117             SV *input
1118             int fmt
1119             CODE:
1120             {
1121             unsigned char uuid[16];
1122             STRLEN in_len;
1123 0           const char *in_str = SvPV(input, in_len);
1124              
1125 0 0         if (horus_parse_uuid(uuid, in_str, in_len) != HORUS_PARSE_OK)
1126 0           croak("Horus: cannot parse UUID string");
1127              
1128 0           RETVAL = horus_uuid_to_sv(aTHX_ uuid, fmt);
1129             }
1130             OUTPUT:
1131             RETVAL
1132              
1133             NV
1134             uuid_time(input)
1135             SV *input
1136             CODE:
1137             {
1138             unsigned char uuid[16];
1139             STRLEN in_len;
1140 0           const char *in_str = SvPV(input, in_len);
1141              
1142 0 0         if (horus_parse_uuid(uuid, in_str, in_len) != HORUS_PARSE_OK)
1143 0           croak("Horus: cannot parse UUID string");
1144              
1145 0           RETVAL = horus_uuid_extract_time(uuid);
1146             }
1147             OUTPUT:
1148             RETVAL
1149              
1150             int
1151             uuid_is_nil(input)
1152             SV *input
1153             CODE:
1154             {
1155             unsigned char uuid[16];
1156             STRLEN in_len;
1157 0           const char *in_str = SvPV(input, in_len);
1158              
1159 0 0         if (horus_parse_uuid(uuid, in_str, in_len) != HORUS_PARSE_OK)
1160 0           RETVAL = 0;
1161             else
1162 0           RETVAL = horus_uuid_is_nil_bin(uuid);
1163             }
1164             OUTPUT:
1165             RETVAL
1166              
1167             int
1168             uuid_is_max(input)
1169             SV *input
1170             CODE:
1171             {
1172             unsigned char uuid[16];
1173             STRLEN in_len;
1174 0           const char *in_str = SvPV(input, in_len);
1175              
1176 0 0         if (horus_parse_uuid(uuid, in_str, in_len) != HORUS_PARSE_OK)
1177 0           RETVAL = 0;
1178             else
1179 0           RETVAL = horus_uuid_is_max_bin(uuid);
1180             }
1181             OUTPUT:
1182             RETVAL
1183              
1184              
1185             SV *
1186             new(class, ...)
1187             const char *class
1188             CODE:
1189             {
1190 2           HV *self = newHV();
1191             int i;
1192 2           int default_fmt = HORUS_FMT_STR;
1193 2           int default_ver = 4;
1194              
1195 4 100         for (i = 1; i + 1 < items; i += 2) {
1196             STRLEN klen;
1197 2           const char *key = SvPV(ST(i), klen);
1198 2           SV *val = ST(i + 1);
1199              
1200 2 100         if (klen == 6 && memcmp(key, "format", 6) == 0)
    50          
1201 1           default_fmt = SvIV(val);
1202 1 50         else if (klen == 7 && memcmp(key, "version", 7) == 0)
    50          
1203 1           default_ver = SvIV(val);
1204             }
1205              
1206 2           (void)hv_store(self, "format", 6, newSViv(default_fmt), 0);
1207 2           (void)hv_store(self, "version", 7, newSViv(default_ver), 0);
1208              
1209 2           RETVAL = sv_bless(newRV_noinc((SV *)self), gv_stashpv(class, GV_ADD));
1210             }
1211             OUTPUT:
1212             RETVAL
1213              
1214             SV *
1215             generate(self)
1216             SV *self
1217             CODE:
1218             {
1219             dMY_CXT;
1220             HV *hv;
1221             SV **svp;
1222             int fmt, ver;
1223             unsigned char uuid[16];
1224              
1225 2 50         if (!SvROK(self) || SvTYPE(SvRV(self)) != SVt_PVHV)
    50          
1226 0           croak("Horus: generate called on non-object");
1227 2           hv = (HV *)SvRV(self);
1228              
1229 2           svp = hv_fetch(hv, "format", 6, 0);
1230 2 50         fmt = svp ? SvIV(*svp) : HORUS_FMT_STR;
1231              
1232 2           svp = hv_fetch(hv, "version", 7, 0);
1233 2 50         ver = svp ? SvIV(*svp) : 4;
1234              
1235 2           switch (ver) {
1236 0           case 1: horus_uuid_v1(uuid, &MY_CXT.v1_state); break;
1237 1           case 4: horus_uuid_v4(uuid); break;
1238 0           case 6: horus_uuid_v6(uuid, &MY_CXT.v1_state, &MY_CXT.v6_state); break;
1239 1           case 7: horus_uuid_v7(uuid, &MY_CXT.v7_state); break;
1240 0           default:
1241 0           croak("Horus: generate() supports versions 1, 4, 6, 7 (use functional API for v2/v3/v5/v8)");
1242             }
1243              
1244 2           RETVAL = horus_uuid_to_sv(aTHX_ uuid, fmt);
1245             }
1246             OUTPUT:
1247             RETVAL
1248              
1249             void
1250             bulk(self, count)
1251             SV *self
1252             int count
1253             PPCODE:
1254             {
1255             dMY_CXT;
1256             HV *hv;
1257             SV **svp;
1258             int fmt, ver, i;
1259              
1260 2 50         if (!SvROK(self) || SvTYPE(SvRV(self)) != SVt_PVHV)
    50          
1261 0           croak("Horus: bulk called on non-object");
1262 2           hv = (HV *)SvRV(self);
1263              
1264 2           svp = hv_fetch(hv, "format", 6, 0);
1265 2 50         fmt = svp ? SvIV(*svp) : HORUS_FMT_STR;
1266              
1267 2           svp = hv_fetch(hv, "version", 7, 0);
1268 2 50         ver = svp ? SvIV(*svp) : 4;
1269              
1270 2 50         if (count <= 0)
1271 0           XSRETURN_EMPTY;
1272              
1273 2 50         EXTEND(SP, count);
    50          
1274              
1275 2 100         if (ver == 4 && count > 256) {
    50          
1276             unsigned char *buf;
1277 0           Newx(buf, (STRLEN)count * 16, unsigned char);
1278 0           horus_random_bulk(buf, (size_t)count * 16);
1279 0 0         for (i = 0; i < count; i++) {
1280 0           unsigned char *uuid = buf + i * 16;
1281 0           horus_stamp_version_variant(uuid, 4);
1282 0 0         mXPUSHs(horus_uuid_to_sv(aTHX_ uuid, fmt));
1283             }
1284 0           Safefree(buf);
1285             } else {
1286 152 100         for (i = 0; i < count; i++) {
1287             unsigned char uuid[16];
1288 150           switch (ver) {
1289 0           case 1: horus_uuid_v1(uuid, &MY_CXT.v1_state); break;
1290 100           case 4: horus_uuid_v4(uuid); break;
1291 0           case 6: horus_uuid_v6(uuid, &MY_CXT.v1_state, &MY_CXT.v6_state); break;
1292 50           case 7: horus_uuid_v7(uuid, &MY_CXT.v7_state); break;
1293 0           default:
1294 0           croak("Horus: bulk() supports versions 1, 4, 6, 7");
1295             }
1296 150 50         mXPUSHs(horus_uuid_to_sv(aTHX_ uuid, fmt));
1297             }
1298             }
1299              
1300 2           XSRETURN(count);
1301             }