File Coverage

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