File Coverage

Horus.xs
Criterion Covered Total %
statement 394 557 70.7
branch 352 828 42.5
condition n/a
subroutine n/a
pod n/a
total 746 1385 53.8


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