File Coverage

XS.xs
Criterion Covered Total %
statement 633 872 72.5
branch 345 610 56.5
condition n/a
subroutine n/a
pod n/a
total 978 1482 65.9


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT /* we want efficiency */
2              
3             #include "EXTERN.h"
4             #include "XSUB.h"
5             #include "perl.h"
6             #include "ppport.h"
7             #include
8             #include
9              
10             #define SMILE_HDR0 0x3A
11             #define SMILE_HDR1 0x29
12             #define SMILE_HDR2 0x0A
13              
14             /* header byte #3:
15             * bits 0: shared names (default true if header missing)
16             * bit 1: shared short string values (default false if header missing)
17             * bit 2: raw binary (unused here)
18             */
19             #define SMILE_HDR3 (0x01 | 0x02) /* enable shared names + shared string values */
20              
21             #define MAX_SHARED_NAMES 1024
22             #define MAX_SHARED_VALUES 1024
23              
24             typedef struct {
25             const unsigned char *p;
26             const unsigned char *end;
27             } smile_reader_t;
28              
29             typedef struct {
30             int shared_names;
31             int shared_values;
32             int canonical;
33             SV *seen_names_map; /* HV* */
34             SV *seen_values_map; /* HV* */
35             int seen_name_count;
36             int seen_value_count;
37             } smile_enc_ctx_t;
38              
39             typedef struct {
40             int shared_names;
41             int shared_values;
42             int use_bigint;
43             int json_bool;
44             SV *seen_names[MAX_SHARED_NAMES]; /* SV* strings */
45             SV *seen_values[MAX_SHARED_VALUES];
46             int seen_name_count;
47             int seen_value_count;
48             } smile_dec_ctx_t;
49              
50 4           static void smile_croak(pTHX_ const char *msg) { croak("%s", msg); }
51              
52             static SV *json_true_cache = NULL;
53             static SV *json_false_cache = NULL;
54              
55 10           static void init_json_bool_cache(pTHX) {
56 10 50         if (json_true_cache && json_false_cache)
    0          
57 0           return;
58 10 50         if (!eval_pv("require JSON::PP; 1", TRUE))
59 0           return;
60              
61 10           dSP;
62 10           ENTER;
63 10           SAVETMPS;
64              
65 10 50         PUSHMARK(SP);
66 10           PUTBACK;
67 10           call_pv("JSON::PP::true", G_SCALAR);
68 10           SPAGAIN;
69 10           json_true_cache = newSVsv(POPs);
70 10           PUTBACK;
71              
72 10 50         PUSHMARK(SP);
73 10           PUTBACK;
74 10           call_pv("JSON::PP::false", G_SCALAR);
75 10           SPAGAIN;
76 10           json_false_cache = newSVsv(POPs);
77 10           PUTBACK;
78              
79 10 50         FREETMPS;
80 10           LEAVE;
81             }
82              
83 190132           static unsigned char rd_u8(pTHX_ smile_reader_t *r) {
84 190132 50         if (r->p >= r->end)
85 0           smile_croak(aTHX_ "Unexpected EOF");
86 190132           return *r->p++;
87             }
88 4051           static void rd_bytes(pTHX_ smile_reader_t *r, unsigned char *dst, size_t n) {
89 4051 50         if ((size_t)(r->end - r->p) < n)
90 0           smile_croak(aTHX_ "Unexpected EOF");
91 4051           memcpy(dst, r->p, n);
92 4051           r->p += n;
93 4051           }
94              
95 8594           static uint64_t rd_vint_u(pTHX_ smile_reader_t *r) {
96 8594           uint64_t v = 0;
97 13333           for (;;) {
98 21927           unsigned char b = rd_u8(aTHX_ r);
99 21927 100         if (b & 0x80) {
100 8594           v = (v << 6) | (uint64_t)(b & 0x3F);
101 8594           return v;
102             }
103 13333           v = (v << 7) | (uint64_t)(b & 0x7F);
104             }
105             }
106 11865           static int64_t zigzag_decode_u64(uint64_t u) {
107 11865           return (int64_t)((u >> 1) ^ (uint64_t)-(int64_t)(u & 1));
108             }
109 15           static uint64_t zigzag_encode_i64(int64_t v) { return ((uint64_t)v << 1) ^ (uint64_t)(v >> 63); }
110              
111 97           static void wr_u8(pTHX_ SV *out, unsigned char b) {
112 97           STRLEN cur = SvCUR(out);
113 97 100         if (SvLEN(out) <= cur + 1)
114 5 50         (void)SvGROW(out, cur + 2);
    50          
115 97           char *dst = SvPVX(out);
116 97           dst[cur] = (char)b;
117 97           cur++;
118 97           dst[cur] = '\0';
119 97           SvCUR_set(out, cur);
120 97           }
121 43           static void wr_bytes(pTHX_ SV *out, const unsigned char *p, size_t n) {
122 43 50         if (n == 0)
123 0           return;
124 43           STRLEN cur = SvCUR(out);
125 43 100         if (SvLEN(out) <= cur + (STRLEN)n)
126 3 50         (void)SvGROW(out, cur + (STRLEN)n + 1);
    50          
127 43           char *dst = SvPVX(out);
128 43           memcpy(dst + cur, p, n);
129 43           cur += (STRLEN)n;
130 43           dst[cur] = '\0';
131 43           SvCUR_set(out, cur);
132             }
133              
134 0           static void wr_vint_u(pTHX_ SV *out, uint64_t u) {
135             unsigned char tmp[16];
136 0           size_t start = sizeof(tmp);
137              
138 0           tmp[--start] = (unsigned char)((u & 0x3F) | 0x80);
139 0           u >>= 6;
140 0 0         while (u) {
141 0           tmp[--start] = (unsigned char)(u & 0x7F);
142 0           u >>= 7;
143             }
144 0           wr_bytes(aTHX_ out, tmp + start, sizeof(tmp) - start);
145 0           }
146 0           static void wr_vint_i(pTHX_ SV *out, int64_t v) { wr_vint_u(aTHX_ out, zigzag_encode_i64(v)); }
147              
148 0           static void wr_safe7_binary(pTHX_ SV *out, const unsigned char *src, size_t raw_len) {
149 0           size_t total_bits = raw_len * 8;
150 0           size_t groups = (total_bits + 6) / 7;
151 0           size_t bitpos = 0;
152 0 0         for (size_t g = 0; g < groups; g++) {
153 0           size_t remain = total_bits - bitpos;
154 0 0         int take = (remain >= 7) ? 7 : (int)remain;
155 0           unsigned char out7 = 0;
156 0 0         for (int j = 0; j < take; j++) {
157 0           size_t absbit = bitpos + (size_t)j;
158 0           size_t by = absbit / 8;
159 0           size_t bi = absbit % 8;
160 0           unsigned char bit = (unsigned char)((src[by] >> (7 - bi)) & 1u);
161 0 0         out7 |= (unsigned char)(bit << (take == 7 ? (6 - j) : (take - 1 - j)));
162             }
163 0           wr_u8(aTHX_ out, out7);
164 0           bitpos += (size_t)take;
165             }
166 0           }
167              
168 2           static SV *rd_safe7_binary(pTHX_ smile_reader_t *r, size_t raw_len) {
169 2           SV *sv = newSVpvn("", 0);
170 2 50         SvGROW(sv, (STRLEN)raw_len + 1);
    50          
171 2           unsigned char *dst = (unsigned char *)SvPVX(sv);
172 2           memset(dst, 0, raw_len + 1);
173              
174 2           size_t total_bits = raw_len * 8;
175 2           size_t groups = (total_bits + 6) / 7;
176 2           size_t bitpos = 0;
177              
178 32 100         for (size_t g = 0; g < groups; g++) {
179 30           size_t remain = total_bits - bitpos;
180 30 100         int take = (remain >= 7) ? 7 : (int)remain;
181 30           unsigned char in7 = rd_u8(aTHX_ r) & 0x7F;
182 238 100         for (int j = 0; j < take; j++) {
183 208           unsigned char bit =
184 208 100         (unsigned char)((in7 >> (take == 7 ? (6 - j) : (take - 1 - j))) & 1u);
185 208           size_t absbit = bitpos + (size_t)j;
186 208           size_t by = absbit / 8;
187 208           size_t bi = absbit % 8;
188 208           dst[by] |= (unsigned char)(bit << (7 - bi));
189             }
190 30           bitpos += (size_t)take;
191             }
192              
193 2           SvCUR_set(sv, (STRLEN)raw_len);
194 2           return sv;
195             }
196              
197 4075           static int valid_backref_index(int index) {
198             (void)index;
199 4075           return 1;
200             }
201              
202 40           static int is_ascii_bytes(const unsigned char *p, size_t n) {
203 109 100         for (size_t i = 0; i < n; i++)
204 69 50         if (p[i] & 0x80)
205 0           return 0;
206 40           return 1;
207             }
208              
209             /* --- float/double --- */
210 0           static void wr_ieee_as_7bit_groups(pTHX_ SV *out, const unsigned char *raw, int nbits) {
211 0           int groups = (nbits + 6) / 7;
212 0           int pad = groups * 7 - nbits;
213 0           int take = 7 - pad;
214              
215 0           uint64_t bits = 0;
216 0 0         for (int i = 0; i < (nbits / 8); i++)
217 0           bits = (bits << 8) | (uint64_t)raw[i];
218              
219 0           int pos = nbits;
220              
221 0           unsigned char g0 = 0;
222 0 0         if (take > 0) {
223 0 0         uint64_t mask = (take == 64) ? ~0ULL : ((1ULL << take) - 1ULL);
224 0           g0 = (unsigned char)((bits >> (pos - take)) & mask);
225 0           pos -= take;
226             }
227 0           wr_u8(aTHX_ out, (unsigned char)(g0 & 0x7F));
228              
229 0 0         while (pos > 0) {
230 0           unsigned char g = (unsigned char)((bits >> (pos - 7)) & 0x7FULL);
231 0           pos -= 7;
232 0           wr_u8(aTHX_ out, g);
233             }
234 0           }
235 5133           static NV rd_ieee_from_7bit_groups(pTHX_ smile_reader_t *r, int nbits) {
236 5133           int groups = (nbits + 6) / 7;
237 5133           int pad = groups * 7 - nbits;
238 5133           int take0 = 7 - pad;
239              
240             unsigned char raw[8];
241 5133           memset(raw, 0, sizeof(raw));
242              
243 5133           int bitpos = 0;
244              
245 5133           unsigned char g0 = rd_u8(aTHX_ r) & 0x7F;
246 10266 100         for (int j = 0; j < take0; j++) {
247 5133           unsigned char bit = (unsigned char)((g0 >> (take0 - 1 - j)) & 1u);
248 5133           int by = bitpos / 8;
249 5133           int bi = bitpos % 8;
250 5133           raw[by] |= (unsigned char)(bit << (7 - bi));
251 5133           bitpos++;
252             }
253              
254 51330 100         for (int i = 1; i < groups; i++) {
255 46197           unsigned char g = rd_u8(aTHX_ r) & 0x7F;
256 369576 100         for (int j = 0; j < 7; j++) {
257 323379 50         if (bitpos >= nbits)
258 0           break;
259 323379           unsigned char bit = (unsigned char)((g >> (6 - j)) & 1u);
260 323379           int by = bitpos / 8;
261 323379           int bi = bitpos % 8;
262 323379           raw[by] |= (unsigned char)(bit << (7 - bi));
263 323379           bitpos++;
264             }
265             }
266              
267 5133 50         if (nbits == 32) {
268 0           uint32_t u32 = 0;
269 0 0         for (int i = 0; i < 4; i++)
270 0           u32 = (u32 << 8) | (uint32_t)raw[i];
271 0           float f = 0.0f;
272 0           memcpy(&f, &u32, sizeof(f));
273 0           return (NV)f;
274             }
275              
276 5133           uint64_t u64 = 0;
277 46197 100         for (int i = 0; i < 8; i++)
278 41064           u64 = (u64 << 8) | (uint64_t)raw[i];
279 5133           double d = 0.0;
280 5133           memcpy(&d, &u64, sizeof(d));
281 5133           return (NV)d;
282             }
283              
284             /* --- TO_JSON --- */
285 21           static SV *maybe_to_json(pTHX_ SV *sv) {
286 21 50         if (!SvROK(sv))
287 0           return sv;
288 21           SV *rv = SvRV(sv);
289 21 100         if (!SvOBJECT(rv))
290 20           return sv;
291              
292 1 50         if (gv_fetchmethod_autoload(SvSTASH(rv), "TO_JSON", 0)) {
293 1           dSP;
294 1           ENTER;
295 1           SAVETMPS;
296 1 50         PUSHMARK(SP);
297 1 50         XPUSHs(sv);
298 1           PUTBACK;
299 1           int count = call_method("TO_JSON", G_SCALAR);
300 1           SPAGAIN;
301 1 50         if (count != 1) {
302 0           PUTBACK;
303 0 0         FREETMPS;
304 0           LEAVE;
305 0           croak("TO_JSON must return a single value");
306             }
307 1           SV *ret = POPs;
308 1           SV *copy = newSVsv(ret);
309 1           PUTBACK;
310 1 50         FREETMPS;
311 1           LEAVE;
312 1           return copy;
313             }
314 0           return sv;
315             }
316              
317             /* --- encoding shared maps --- */
318 22           static int enc_lookup_shared(pTHX_ HV *map, SV *sv_key, int *out_index) {
319 22           HE *he = hv_fetch_ent(map, sv_key, 0, 0);
320 22 100         if (!he)
321 21           return 0;
322 1           SV *v = HeVAL(he);
323 1 50         if (!SvOK(v))
324 0           return 0;
325 1           *out_index = (int)SvIV(v);
326 1           return 1;
327             }
328 21           static void enc_store_shared(pTHX_ HV *map, SV *sv_key, int index) {
329 21           hv_store_ent(map, sv_key, newSViv((IV)index), 0);
330 21           }
331              
332 14           static int enc_lookup_shared_ascii(pTHX_ HV *map, const unsigned char *key,
333             STRLEN len, int *out_index) {
334 14           SV **svp = hv_fetch(map, (const char *)key, (I32)len, 0);
335 14 100         if (!svp || !SvOK(*svp))
    50          
336 8           return 0;
337 6           *out_index = (int)SvIV(*svp);
338 6           return 1;
339             }
340              
341 8           static void enc_store_shared_ascii(pTHX_ HV *map, const unsigned char *key,
342             STRLEN len, int index) {
343 8           (void)hv_store(map, (const char *)key, (I32)len, newSViv((IV)index), 0);
344 8           }
345              
346             /* --- string encoders (value mode) --- */
347 10           static void wr_short_or_long_string_value_bytes(pTHX_ SV *out, const unsigned char *s,
348             STRLEN len, int ascii,
349             int *out_is_short,
350             SV **out_short_key) {
351 10           *out_is_short = (len <= 64) ? 1 : 0;
352 10           *out_short_key = NULL;
353              
354 10 100         if (len == 0) {
355 1           wr_u8(aTHX_ out, 0x20);
356 1           return;
357             }
358              
359 9 50         if (ascii) {
360 9 50         if (len <= 32) {
361 9           wr_u8(aTHX_ out, (unsigned char)(0x40 + (len - 1)));
362 9           wr_bytes(aTHX_ out, s, (size_t)len);
363 0 0         } else if (len <= 64) {
364 0           wr_u8(aTHX_ out, (unsigned char)(0x60 + (len - 33)));
365 0           wr_bytes(aTHX_ out, s, (size_t)len);
366             } else {
367 0           wr_u8(aTHX_ out, 0xE0);
368 0           wr_bytes(aTHX_ out, s, (size_t)len);
369 0           wr_u8(aTHX_ out, 0xFC);
370             }
371             } else {
372 0 0         if (len >= 2 && len <= 33) {
    0          
373 0           wr_u8(aTHX_ out, (unsigned char)(0x80 + (len - 2)));
374 0           wr_bytes(aTHX_ out, s, (size_t)len);
375 0 0         } else if (len >= 34 && len <= 65) {
    0          
376 0           wr_u8(aTHX_ out, (unsigned char)(0xA0 + (len - 34)));
377 0           wr_bytes(aTHX_ out, s, (size_t)len);
378             } else {
379 0           wr_u8(aTHX_ out, 0xE4);
380 0           wr_bytes(aTHX_ out, s, (size_t)len);
381 0           wr_u8(aTHX_ out, 0xFC);
382             }
383             }
384              
385 9 50         if (*out_is_short) {
386             /* stable key for hash lookup: byte string of UTF-8 payload */
387 9           *out_short_key = newSVpvn((const char *)s, len);
388 9 50         if (!ascii)
389 0           SvUTF8_on(*out_short_key);
390             }
391             }
392              
393 2           static void wr_short_or_long_string_value(pTHX_ SV *out, SV *sv, int *out_is_short,
394             SV **out_short_key) {
395 2           STRLEN len = 0;
396 2           SV *tmp = sv_mortalcopy(sv);
397              
398 2 50         if (SvUTF8(tmp))
399 0           (void)SvPVutf8(tmp, len);
400 2           const unsigned char *s = (const unsigned char *)SvPVbyte(tmp, len);
401 2           int ascii = is_ascii_bytes(s, (size_t)len);
402              
403 2           wr_short_or_long_string_value_bytes(aTHX_ out, s, len, ascii, out_is_short,
404             out_short_key);
405 2           }
406              
407             /* --- key encoder (key mode) --- */
408 24           static void wr_key_name(pTHX_ SV *out, SV *keysv, int *out_is_trackable, SV **out_key_for_map) {
409 24           STRLEN len = 0;
410 24           SV *tmp = sv_mortalcopy(keysv);
411              
412 24 50         if (SvUTF8(tmp))
413 0           (void)SvPVutf8(tmp, len);
414 24           const unsigned char *s = (const unsigned char *)SvPVbyte(tmp, len);
415              
416 24           *out_is_trackable = 1;
417 24           *out_key_for_map = NULL;
418              
419 24 50         if (len == 0) {
420 0           wr_u8(aTHX_ out, 0x20);
421 0           goto mkkey;
422             }
423              
424 24           int ascii = is_ascii_bytes(s, (size_t)len);
425             {
426 24 50         if (ascii && len <= 64) {
    50          
427 24           wr_u8(aTHX_ out, (unsigned char)(0x80 + (len - 1))); /* short ASCII key */
428 24           wr_bytes(aTHX_ out, s, (size_t)len);
429 24           goto mkkey;
430             }
431 0 0         if (!ascii && len >= 2 && len <= 57) {
    0          
    0          
432 0           wr_u8(aTHX_ out, (unsigned char)(0xC0 + (len - 2))); /* short Unicode key */
433 0           wr_bytes(aTHX_ out, s, (size_t)len);
434 0           goto mkkey;
435             }
436             /* long */
437 0           wr_u8(aTHX_ out, 0x34);
438 0           wr_bytes(aTHX_ out, s, (size_t)len);
439 0           wr_u8(aTHX_ out, 0xFC);
440             }
441              
442 24           mkkey:
443 24           *out_key_for_map = newSVpvn((const char *)s, len);
444 24 50         if (!ascii)
445 0           SvUTF8_on(*out_key_for_map);
446 24           }
447              
448             /* --- forward decls --- */
449             static void encode_value(pTHX_ SV *out, smile_enc_ctx_t *ctx, SV *sv);
450             static SV *decode_value(pTHX_ smile_reader_t *r, smile_dec_ctx_t *ctx);
451              
452 16           static void encode_string_value(pTHX_ SV *out, smile_enc_ctx_t *ctx, SV *sv) {
453 16 100         if (ctx->shared_values) {
454 14           int is_short = 0;
455 14           SV *vkey = NULL;
456 14           HV *shared_map = (HV *)SvRV(ctx->seen_values_map);
457              
458 14           STRLEN len = 0;
459 14           SV *tmp = sv_mortalcopy(sv);
460 14 50         if (SvUTF8(tmp))
461 0           (void)SvPVutf8(tmp, len);
462 14           const unsigned char *s = (const unsigned char *)SvPVbyte(tmp, len);
463 14           is_short = (len <= 64) ? 1 : 0;
464 14           int ascii = is_ascii_bytes(s, (size_t)len);
465              
466 22 50         if (is_short && ascii) {
    50          
467 14           int index = -1;
468 14 100         if (enc_lookup_shared_ascii(aTHX_ shared_map, s, len, &index) &&
469 6 50         index >= 0) {
470 6 50         if (index < 31) {
471 6           wr_u8(aTHX_ out, (unsigned char)(index + 1));
472 6           return;
473             }
474 0 0         if (index < 1024) {
475 0           wr_u8(aTHX_ out, (unsigned char)(0xEC | ((index >> 8) & 0x03)));
476 0           wr_u8(aTHX_ out, (unsigned char)(index & 0xFF));
477 0           return;
478             }
479             }
480 0 0         } else if (is_short) {
481 0           vkey = newSVpvn((const char *)s, len);
482 0 0         if (!ascii)
483 0           SvUTF8_on(vkey);
484              
485 0           int index = -1;
486 0 0         if (enc_lookup_shared(aTHX_ shared_map, vkey, &index) && index >= 0) {
    0          
487 0 0         if (index < 31) {
488 0           wr_u8(aTHX_ out, (unsigned char)(index + 1));
489 0           SvREFCNT_dec(vkey);
490 0           return;
491             }
492 0 0         if (index < 1024) {
493 0           wr_u8(aTHX_ out, (unsigned char)(0xEC | ((index >> 8) & 0x03)));
494 0           wr_u8(aTHX_ out, (unsigned char)(index & 0xFF));
495 0           SvREFCNT_dec(vkey);
496 0           return;
497             }
498             }
499             }
500              
501 8           int was_short = 0;
502 8           SV *k2 = NULL;
503 8           wr_short_or_long_string_value_bytes(aTHX_ out, s, len, ascii, &was_short, &k2);
504 8 50         if (was_short) {
505 8           int idx = ctx->seen_value_count;
506 8 50         if (idx < MAX_SHARED_VALUES) {
507 8 50         if (valid_backref_index(idx)) {
508 8 50         if (ascii)
509 8           enc_store_shared_ascii(aTHX_ shared_map, s, len, idx);
510             else
511 0           enc_store_shared(aTHX_ shared_map, k2, idx);
512             }
513 8           ctx->seen_value_count = idx + 1;
514             } else {
515 0           hv_clear(shared_map);
516 0           ctx->seen_value_count = 0;
517 0 0         if (valid_backref_index(0)) {
518 0 0         if (ascii)
519 0           enc_store_shared_ascii(aTHX_ shared_map, s, len, 0);
520             else
521 0           enc_store_shared(aTHX_ shared_map, k2, 0);
522             }
523 0           ctx->seen_value_count = 1;
524             }
525             }
526              
527 8 50         if (vkey)
528 0           SvREFCNT_dec(vkey);
529 8 100         if (k2)
530 7           SvREFCNT_dec(k2);
531 8           return;
532             }
533              
534             {
535 2           int dummy = 0;
536 2           SV *k = NULL;
537 2           wr_short_or_long_string_value(aTHX_ out, sv, &dummy, &k);
538 2 50         if (k)
539 2           SvREFCNT_dec(k);
540             }
541             }
542              
543             /* --- number encoder --- */
544 15           static void encode_number(pTHX_ SV *out, SV *sv) {
545 15 50         if (SvUOK(sv) && !SvNOK(sv)) {
    0          
546 0           uint64_t uv = (uint64_t)SvUV(sv);
547 0 0         if (uv <= (uint64_t)INT64_MAX) {
548 0           int64_t v = (int64_t)uv;
549 0 0         if (v >= -16 && v <= 15) {
    0          
550 0           unsigned char zz = (unsigned char)(zigzag_encode_i64(v) & 0x1F);
551 0           wr_u8(aTHX_ out, (unsigned char)(0xC0 | zz));
552 0           return;
553             }
554 0 0         if (v >= INT32_MIN && v <= INT32_MAX) {
    0          
555 0           wr_u8(aTHX_ out, 0x24);
556 0           wr_vint_i(aTHX_ out, v);
557 0           return;
558             }
559 0           wr_u8(aTHX_ out, 0x25);
560 0           wr_vint_i(aTHX_ out, v);
561 0           return;
562             }
563              
564             unsigned char be[9];
565 0           size_t n = 8;
566 0 0         for (int i = 7; i >= 0; i--) {
567 0           be[i] = (unsigned char)(uv & 0xFF);
568 0           uv >>= 8;
569             }
570 0 0         if (be[0] & 0x80) {
571 0           memmove(be + 1, be, 8);
572 0           be[0] = 0x00;
573 0           n = 9;
574             }
575              
576 0           wr_u8(aTHX_ out, 0x26);
577 0           wr_vint_u(aTHX_ out, (uint64_t)n);
578 0           wr_safe7_binary(aTHX_ out, be, n);
579 0           return;
580             }
581              
582 15 50         if (SvIOK(sv) && !SvNOK(sv)) {
    50          
583 15           int64_t v = (int64_t)SvIV(sv);
584              
585 15 50         if (v >= -16 && v <= 15) {
    50          
586 15           unsigned char zz = (unsigned char)(zigzag_encode_i64(v) & 0x1F);
587 15           wr_u8(aTHX_ out, (unsigned char)(0xC0 | zz));
588 15           return;
589             }
590 0 0         if (v >= INT32_MIN && v <= INT32_MAX) {
    0          
591 0           wr_u8(aTHX_ out, 0x24);
592 0           wr_vint_i(aTHX_ out, v);
593 0           return;
594             }
595 0           wr_u8(aTHX_ out, 0x25);
596 0           wr_vint_i(aTHX_ out, v);
597 0           return;
598             }
599              
600 0           wr_u8(aTHX_ out, 0x29); /* double */
601 0           double d = (double)SvNV(sv);
602 0           uint64_t u = 0;
603 0           memcpy(&u, &d, sizeof(u));
604              
605             unsigned char be[8];
606 0 0         for (int i = 7; i >= 0; i--) {
607 0           be[i] = (unsigned char)(u & 0xFF);
608 0           u >>= 8;
609             }
610 0           wr_ieee_as_7bit_groups(aTHX_ out, be, 64);
611             }
612              
613             /* --- booleans --- */
614 52           static int is_json_boolean_sv(pTHX_ SV *sv, int *out_bool) {
615 52 100         if (!SvROK(sv))
616 31           return 0;
617 21           SV *rv = SvRV(sv);
618 21 100         if (!SvOBJECT(rv))
619 20           return 0;
620              
621 1 50         const char *pkg = HvNAME(SvSTASH(rv));
    50          
    50          
    0          
    50          
    50          
622 1 50         if (!pkg)
623 0           return 0;
624              
625 1 50         if (strEQ(pkg, "JSON::PP::Boolean") || strEQ(pkg, "JSON::XS::Boolean") ||
    50          
626 1 50         strEQ(pkg, "Cpanel::JSON::XS::Boolean")) {
627 0           *out_bool = SvTRUE(sv) ? 1 : 0;
628 0           return 1;
629             }
630 1           return 0;
631             }
632              
633             /* --- hash/array encoder --- */
634 25           static void encode_hash_entry(pTHX_ SV *out, smile_enc_ctx_t *ctx, SV *k, SV *v) {
635             /* shared name? */
636 25 100         if (ctx->shared_names) {
637 22           int index = -1;
638 22 100         if (enc_lookup_shared(aTHX_(HV *) SvRV(ctx->seen_names_map), k, &index) && index >= 0) {
    50          
639 1 50         if (index < 64) {
640 1           wr_u8(aTHX_ out, (unsigned char)(0x40 | (index & 0x3F))); /* short shared key */
641 1           encode_value(aTHX_ out, ctx, v);
642 1           return;
643             }
644 0 0         if (index < 1024) {
645 0           wr_u8(aTHX_ out, (unsigned char)(0x30 | ((index >> 8) & 0x03))); /* long shared key */
646 0           wr_u8(aTHX_ out, (unsigned char)(index & 0xFF));
647 0           encode_value(aTHX_ out, ctx, v);
648 0           return;
649             }
650             }
651             }
652              
653 24           int trackable = 0;
654 24           SV *kkey = NULL;
655 24           wr_key_name(aTHX_ out, k, &trackable, &kkey);
656              
657 24 100         if (ctx->shared_names && trackable) {
    50          
658 21           int idx = ctx->seen_name_count;
659 21 50         if (idx < MAX_SHARED_NAMES) {
660 21 50         if (valid_backref_index(idx)) {
661 21           enc_store_shared(aTHX_(HV *) SvRV(ctx->seen_names_map), kkey, idx);
662             }
663 21           ctx->seen_name_count = idx + 1;
664             } else {
665             /* reset */
666 0           hv_clear((HV *)SvRV(ctx->seen_names_map));
667 0           ctx->seen_name_count = 0;
668 0 0         if (valid_backref_index(0))
669 0           enc_store_shared(aTHX_(HV *) SvRV(ctx->seen_names_map), kkey, 0);
670 0           ctx->seen_name_count = 1;
671             }
672             }
673 24 50         if (kkey)
674 24           SvREFCNT_dec(kkey);
675              
676 24           encode_value(aTHX_ out, ctx, v);
677             }
678              
679 14           static void encode_hash(pTHX_ SV *out, smile_enc_ctx_t *ctx, HV *hv) {
680 14           wr_u8(aTHX_ out, 0xFA); /* START_OBJECT */
681              
682 14 100         if (!ctx->canonical) {
683 13           hv_iterinit(hv);
684             HE *he;
685 35 100         while ((he = hv_iternext(hv)) != NULL) {
686 22           SV *k = hv_iterkeysv(he);
687 22           SV *v = hv_iterval(hv, he);
688 22           encode_hash_entry(aTHX_ out, ctx, k, v);
689             }
690             } else {
691 1 50         SSize_t count = HvUSEDKEYS(hv);
692 1 50         if (count > 0) {
693             typedef struct {
694             SV *k;
695             SV *v;
696             } smile_hash_entry_t;
697              
698             smile_hash_entry_t *entries;
699 1 50         Newx(entries, (size_t)count, smile_hash_entry_t);
700              
701 1           hv_iterinit(hv);
702             HE *he;
703 1           SSize_t used = 0;
704 4 100         while ((he = hv_iternext(hv)) != NULL) {
705 3           entries[used].k = newSVsv(hv_iterkeysv(he));
706 3           entries[used].v = hv_iterval(hv, he);
707 3           used++;
708             }
709              
710 3 100         for (SSize_t i = 1; i < used; i++) {
711 2           smile_hash_entry_t current = entries[i];
712 2           SSize_t j = i;
713 3 100         while (j > 0 && sv_cmp(current.k, entries[j - 1].k) < 0) {
    100          
714 1           entries[j] = entries[j - 1];
715 1           j--;
716             }
717 2           entries[j] = current;
718             }
719              
720 4 100         for (SSize_t i = 0; i < used; i++) {
721 3           encode_hash_entry(aTHX_ out, ctx, entries[i].k, entries[i].v);
722 3           SvREFCNT_dec(entries[i].k);
723             }
724              
725 1           Safefree(entries);
726             }
727             }
728              
729 14           wr_u8(aTHX_ out, 0xFB); /* END_OBJECT */
730 14           }
731              
732 6           static void encode_array(pTHX_ SV *out, smile_enc_ctx_t *ctx, AV *av) {
733 6           wr_u8(aTHX_ out, 0xF8); /* START_ARRAY */
734 6           SSize_t n = av_len(av);
735 20 100         for (SSize_t i = 0; i <= n; i++) {
736 14           SV **svp = av_fetch(av, i, 0);
737 14 50         SV *v = svp ? *svp : &PL_sv_undef;
738 14           encode_value(aTHX_ out, ctx, v);
739             }
740 6           wr_u8(aTHX_ out, 0xF9); /* END_ARRAY */
741 6           }
742              
743 53           static void encode_value(pTHX_ SV *out, smile_enc_ctx_t *ctx, SV *sv) {
744 53 50         if (!sv || !SvOK(sv)) {
    100          
745 1           wr_u8(aTHX_ out, 0x21);
746 53           return;
747             }
748              
749 52           int b = 0;
750 52 50         if (is_json_boolean_sv(aTHX_ sv, &b)) {
751 0 0         wr_u8(aTHX_ out, b ? 0x23 : 0x22);
752 0           return;
753             }
754              
755 52 100         if (SvROK(sv)) {
756 21           SV *maybe = maybe_to_json(aTHX_ sv);
757 21 100         if (maybe != sv) {
758 1           SV *mortal = sv_2mortal(maybe);
759 1           encode_value(aTHX_ out, ctx, mortal);
760 1           return;
761             }
762              
763 20           SV *rv = SvRV(sv);
764 20 100         if (SvTYPE(rv) == SVt_PVAV) {
765 6           encode_array(aTHX_ out, ctx, (AV *)rv);
766 6           return;
767             }
768 14 50         if (SvTYPE(rv) == SVt_PVHV) {
769 14           encode_hash(aTHX_ out, ctx, (HV *)rv);
770 14           return;
771             }
772 0 0         if (SvTYPE(rv) == SVt_PV) {
773 0           encode_string_value(aTHX_ out, ctx, rv);
774 0           return;
775             }
776              
777 0 0         if (SvTYPE(rv) == SVt_IV || SvTYPE(rv) == SVt_NV || SvTYPE(rv) == SVt_PVIV ||
    0          
    0          
778 0 0         SvTYPE(rv) == SVt_PVNV) {
779 0           encode_number(aTHX_ out, rv);
780 0           return;
781             }
782              
783 0           croak("Unsupported reference type for Smile encoding");
784             }
785              
786 31 50         if (SvOK(sv) && SvPOK(sv) && !SvIOK(sv) && !SvNOK(sv)) {
    100          
    50          
    50          
787 16           encode_string_value(aTHX_ out, ctx, sv);
788 16           return;
789             }
790              
791 15 50         if (SvIOK(sv) || SvNOK(sv)) {
    0          
792 15           encode_number(aTHX_ out, sv);
793 15           return;
794             }
795              
796             {
797 0           encode_string_value(aTHX_ out, ctx, sv);
798             }
799             }
800              
801             /* --- decoding helpers --- */
802 4051           static SV *rd_string_len(pTHX_ smile_reader_t *r, size_t len, int set_utf8) {
803             /* newSV(len) + SvPOK_on can leave PVX in an unexpected state on some builds.
804             * Allocate as a PV explicitly, then grow.
805             */
806 4051           SV *sv = newSVpvn("", 0);
807 4051 50         SvGROW(sv, (STRLEN)len + 1);
    100          
808 4051           unsigned char *dst = (unsigned char *)SvPVX(sv);
809 4051           rd_bytes(aTHX_ r, dst, len);
810 4051           dst[len] = '\0';
811 4051           SvCUR_set(sv, (STRLEN)len);
812 4051 100         if (set_utf8)
813 17           SvUTF8_on(sv);
814 4051           return sv;
815             }
816 471           static SV *rd_string_eos(pTHX_ smile_reader_t *r, int ascii_hint) {
817 471           const unsigned char *start = r->p;
818 471           const unsigned char *stop = (const unsigned char *)memchr(start, 0xFC, (size_t)(r->end - start));
819 471 50         if (!stop)
820 0           smile_croak(aTHX_ "Unexpected EOF");
821              
822 471           SV *sv = newSVpvn((const char *)start, (STRLEN)(stop - start));
823 471           r->p = stop + 1;
824              
825 471 100         if (!ascii_hint)
826 18           SvUTF8_on(sv);
827 471           return sv;
828             }
829 5133           static SV *nv_to_sv(pTHX_ NV n) {
830 5133 100         NV absn = n < 0 ? -n : n;
831 5133 100         if (absn >= 9.0e15) {
832 1477 100         if (n >= (NV)IV_MIN && n <= (NV)IV_MAX) {
    100          
833 185           IV iv = (IV)n;
834 185 50         if ((NV)iv == n)
835 185           return newSViv(iv);
836             }
837              
838 1292 100         if (n >= 0.0 && n <= (NV)UV_MAX) {
    100          
839 13           UV uv = (UV)n;
840 13 50         if ((NV)uv == n)
841 13           return newSVuv(uv);
842             }
843             }
844              
845 4935           return newSVnv(n);
846             }
847              
848 5           static SV *make_json_boolean(pTHX_ int b, int use_json_bool) {
849 5 50         if (use_json_bool && json_true_cache && json_false_cache)
    50          
    50          
850 5 100         return newSVsv( b ? json_true_cache : json_false_cache );
851 0 0         return b ? &PL_sv_yes : &PL_sv_no;
852             }
853              
854 718           static void dec_track_name(pTHX_ smile_dec_ctx_t *ctx, SV *name) {
855 718 50         if (!ctx->shared_names)
856 0           return;
857 718           int idx = ctx->seen_name_count;
858 718 50         if (idx >= MAX_SHARED_NAMES) {
859 0 0         for (int i = 0; i < ctx->seen_name_count; i++)
860 0           SvREFCNT_dec(ctx->seen_names[i]);
861 0           ctx->seen_name_count = 0;
862 0           idx = 0;
863             }
864 718 50         if (valid_backref_index(idx)) {
865 718           ctx->seen_names[idx] = name;
866 718           SvREFCNT_inc_simple_void_NN(name);
867             } else {
868 0           ctx->seen_names[idx] = &PL_sv_undef;
869 0           SvREFCNT_inc(ctx->seen_names[idx]);
870             }
871 718           ctx->seen_name_count = idx + 1;
872             }
873 3333           static void dec_track_value(pTHX_ smile_dec_ctx_t *ctx, SV *val) {
874 3333 100         if (!ctx->shared_values)
875 5           return;
876 3328           STRLEN len = 0;
877 3328           (void)SvPVbyte(val, len);
878 3328 50         if (len > 64)
879 0           return;
880              
881 3328           int idx = ctx->seen_value_count;
882 3328 100         if (idx >= MAX_SHARED_VALUES) {
883 1025 100         for (int i = 0; i < ctx->seen_value_count; i++)
884 1024           SvREFCNT_dec(ctx->seen_values[i]);
885 1           ctx->seen_value_count = 0;
886 1           idx = 0;
887             }
888 3328 50         if (valid_backref_index(idx)) {
889 3328           ctx->seen_values[idx] = val;
890 3328           SvREFCNT_inc_simple_void_NN(val);
891             } else {
892 0           ctx->seen_values[idx] = &PL_sv_undef;
893 0           SvREFCNT_inc(ctx->seen_values[idx]);
894             }
895 3328           ctx->seen_value_count = idx + 1;
896             }
897              
898 732           static SV *decode_array(pTHX_ smile_reader_t *r, smile_dec_ctx_t *ctx) {
899 732           AV *av = newAV();
900 19524           for (;;) {
901 20256 50         if (r->p >= r->end)
902 0           smile_croak(aTHX_ "Unexpected EOF in array");
903 20256           unsigned char peek = *r->p;
904 20256 100         if (peek == 0xF9) {
905 732           r->p++;
906 732           break;
907             }
908 19524           SV *v = decode_value(aTHX_ r, ctx);
909 19524           av_push(av, v);
910             }
911 732           return newRV_noinc((SV *)av);
912             }
913              
914 10588           static SV *decode_object(pTHX_ smile_reader_t *r, smile_dec_ctx_t *ctx) {
915 10588           HV *hv = newHV();
916              
917 33607           for (;;) {
918 44195           unsigned char t = rd_u8(aTHX_ r);
919              
920 44195 100         if (t == 0xFB)
921 10588           break; /* END_OBJECT */
922              
923 33607           SV *key = NULL;
924              
925 33607 50         if (t == 0x20) {
926 0           key = newSVpvn("", 0);
927 33607 100         } else if (t >= 0x40 && t <= 0x7F) {
    100          
928             /* short shared name ref: low 6 bits */
929 23005           int index = (int)(t & 0x3F);
930 23005 50         if (!ctx->shared_names || index >= ctx->seen_name_count)
    50          
931 0           smile_croak(aTHX_ "Invalid shared name reference");
932 23005           key = ctx->seen_names[index];
933 23005           SvREFCNT_inc_simple_void_NN(key);
934 11319 100         } else if (t >= 0x80 && t <= 0xBF) {
    100          
935 717           size_t len = (size_t)(t - 0x80) + 1;
936 717           key = rd_string_len(aTHX_ r, len, 0);
937 717           dec_track_name(aTHX_ ctx, key);
938 9886 100         } else if (t >= 0xC0 && t <= 0xF7) {
    50          
939 1           size_t len = (size_t)(t - 0xC0) + 2;
940 1           key = rd_string_len(aTHX_ r, len, 1);
941 1           dec_track_name(aTHX_ ctx, key);
942 9884 50         } else if (t >= 0x30 && t <= 0x33) {
    50          
943 9884           int lo = rd_u8(aTHX_ r);
944 9884           int index = ((t & 0x03) << 8) | lo;
945 9884 50         if (!ctx->shared_names || index >= ctx->seen_name_count)
    50          
946 0           smile_croak(aTHX_ "Invalid long shared name reference");
947 9884           key = ctx->seen_names[index];
948 9884           SvREFCNT_inc_simple_void_NN(key);
949 0 0         } else if (t == 0x34) {
950 0           key = rd_string_eos(aTHX_ r, 0);
951 0           dec_track_name(aTHX_ ctx, key);
952             } else {
953 0           smile_croak(aTHX_ "Invalid key token");
954             }
955              
956 33607           SV *val = decode_value(aTHX_ r, ctx);
957 33607           hv_store_ent(hv, key, val, 0);
958 33607           SvREFCNT_dec(key);
959             }
960              
961 10588           return newRV_noinc((SV *)hv);
962             }
963              
964 53160           static SV *decode_value(pTHX_ smile_reader_t *r, smile_dec_ctx_t *ctx) {
965 53160           unsigned char t = rd_u8(aTHX_ r);
966              
967 53160 50         if (t == 0xFF)
968 0           return &PL_sv_undef;
969              
970 53160 100         if (t == 0x20)
971 4           return newSVpvn("", 0);
972 53156 100         if (t == 0x21)
973 6           return &PL_sv_undef;
974 53150 100         if (t == 0x22)
975 2           return make_json_boolean(aTHX_ 0, ctx->json_bool);
976 53148 100         if (t == 0x23)
977 3           return make_json_boolean(aTHX_ 1, ctx->json_bool);
978              
979 53145 100         if (t == 0xF8)
980 732           return decode_array(aTHX_ r, ctx);
981 52413 100         if (t == 0xFA)
982 10588           return decode_object(aTHX_ r, ctx);
983              
984             /* shared string value refs
985             *
986             * Return a copy here, not the cached SV itself. Values that land in
987             * Perl hashes/arrays must remain independently mutable.
988             */
989 41825 50         if (t >= 0x01 && t <= 0x1F) {
    100          
990 11415           int index = (int)t - 1;
991 11415 50         if (!ctx->shared_values || index >= ctx->seen_value_count)
    50          
992 0           smile_croak(aTHX_ "Invalid shared string value reference");
993 11415           return newSVsv(ctx->seen_values[index]);
994             }
995 30410 100         if ((t & 0xFC) == 0xEC) {
996 9606           int lo = rd_u8(aTHX_ r);
997 9606           int index = ((t & 0x03) << 8) | lo;
998 9606 50         if (!ctx->shared_values || index >= ctx->seen_value_count)
    50          
999 0           smile_croak(aTHX_ "Invalid long shared string value reference");
1000 9606           return newSVsv(ctx->seen_values[index]);
1001             }
1002              
1003             /* tiny/short strings */
1004 20804 100         if ((t & 0xE0) == 0x40) {
1005 3047           size_t len = (size_t)(t & 0x1F) + 1;
1006 3047           SV *sv = rd_string_len(aTHX_ r, len, 0);
1007 3047           dec_track_value(aTHX_ ctx, sv);
1008 3047           return sv;
1009             }
1010 17757 100         if ((t & 0xE0) == 0x60) {
1011 270           size_t len = (size_t)(t & 0x1F) + 33;
1012 270           SV *sv = rd_string_len(aTHX_ r, len, 0);
1013 270           dec_track_value(aTHX_ ctx, sv);
1014 270           return sv;
1015             }
1016 17487 100         if ((t & 0xE0) == 0x80) {
1017 14           size_t len = (size_t)(t & 0x1F) + 2;
1018 14           SV *sv = rd_string_len(aTHX_ r, len, 1);
1019 14           dec_track_value(aTHX_ ctx, sv);
1020 14           return sv;
1021             }
1022 17473 100         if ((t & 0xE0) == 0xA0) {
1023 2           size_t len = (size_t)(t & 0x1F) + 34;
1024 2           SV *sv = rd_string_len(aTHX_ r, len, 1);
1025 2           dec_track_value(aTHX_ ctx, sv);
1026 2           return sv;
1027             }
1028              
1029             /* small int */
1030 17471 100         if ((t & 0xE0) == 0xC0) {
1031 3273           uint64_t zz = (uint64_t)(t & 0x1F);
1032 3273           int64_t v = zigzag_decode_u64(zz);
1033 3273           return newSViv((IV)v);
1034             }
1035              
1036 14198 100         if (t == 0x24 || t == 0x25) {
    50          
1037 8592           uint64_t u = rd_vint_u(aTHX_ r);
1038 8592           int64_t v = zigzag_decode_u64(u);
1039 8592 50         if (t == 0x24 && (v < INT32_MIN || v > INT32_MAX))
    50          
    50          
1040 0           smile_croak(aTHX_ "Invalid int32 VInt (out of range)");
1041 8592           return newSViv((IV)v);
1042             }
1043              
1044 5606 100         if (t == 0x26) {
1045 2           uint64_t raw_len_u = rd_vint_u(aTHX_ r);
1046 2 50         if (raw_len_u == 0)
1047 0           smile_croak(aTHX_ "Invalid BigInteger length");
1048 2 50         if (raw_len_u > 1024)
1049 0           smile_croak(aTHX_ "BigInteger too large");
1050 2           size_t raw_len = (size_t)raw_len_u;
1051 2           SV *bytes = rd_safe7_binary(aTHX_ r, raw_len);
1052 2           STRLEN n = 0;
1053 2           const unsigned char *bp = (const unsigned char *)SvPVbyte(bytes, n);
1054              
1055 2           int negative = (bp[0] & 0x80) ? 1 : 0;
1056 2 50         if (n <= 8 || (n == 9 && !negative && bp[0] == 0x00)) {
    50          
    0          
    0          
1057 0           const unsigned char *p = bp;
1058 0 0         if (n == 9) {
1059 0           p = bp + 1;
1060 0           n = 8;
1061             }
1062 0           uint64_t uv = 0;
1063 0 0         for (STRLEN i = 0; i < n; i++)
1064 0           uv = (uv << 8) | (uint64_t)p[i];
1065 0           SvREFCNT_dec(bytes);
1066 0 0         if (!negative) {
1067 0 0         if (uv <= (uint64_t)IV_MAX)
1068 2           return newSViv((IV)uv);
1069 0           return newSVuv((UV)uv);
1070             }
1071 0           int64_t sv = (int64_t)uv;
1072 0           return newSViv((IV)sv);
1073             }
1074              
1075             /* fallback for very large integers: return decimal string via Math::BigInt if available */
1076 2 100         if (ctx->use_bigint && eval_pv("require Math::BigInt; 1", TRUE)) {
    50          
1077 1 50         SV *hex = newSVpvn(negative ? "-0x" : "0x", negative ? 3 : 2);
    50          
1078 14 100         for (STRLEN i = 0; i < n; i++) {
1079 13           sv_catpvf(hex, "%02x", (unsigned int)bp[i]);
1080             }
1081 1           dSP;
1082 1           ENTER;
1083 1           SAVETMPS;
1084 1 50         PUSHMARK(SP);
1085 1 50         XPUSHs(sv_2mortal(newSVpv("Math::BigInt", 0)));
1086 1 50         XPUSHs(sv_2mortal(hex));
1087 1           PUTBACK;
1088 1           call_method("from_hex", G_SCALAR);
1089 1           SPAGAIN;
1090 1           SV *ret = newSVsv(POPs);
1091 1           PUTBACK;
1092 1 50         FREETMPS;
1093 1           LEAVE;
1094 1           SvREFCNT_dec(bytes);
1095 1           return ret;
1096             }
1097              
1098             {
1099 1           NV mag = 0.0;
1100 14 100         for (STRLEN i = 0; i < n; i++) {
1101 13           mag = (mag * 256.0) + (NV)bp[i];
1102             }
1103 1 50         if (negative) {
1104 0           NV two_pow = 1.0;
1105 0 0         for (STRLEN i = 0; i < n * 8; i++)
1106 0           two_pow *= 2.0;
1107 0           mag -= two_pow;
1108             }
1109 1           SvREFCNT_dec(bytes);
1110 1           return newSVnv(mag);
1111             }
1112              
1113             SvREFCNT_dec(bytes);
1114             smile_croak(aTHX_ "BigInteger decode failed");
1115             }
1116              
1117 5604 50         if (t == 0x28)
1118 0           return nv_to_sv(aTHX_ rd_ieee_from_7bit_groups(aTHX_ r, 32));
1119 5604 100         if (t == 0x29)
1120 5133           return nv_to_sv(aTHX_ rd_ieee_from_7bit_groups(aTHX_ r, 64));
1121              
1122 471 100         if (t == 0xE0)
1123 453           return rd_string_eos(aTHX_ r, 1);
1124 18 50         if (t == 0xE4)
1125 18           return rd_string_eos(aTHX_ r, 0);
1126              
1127 0           smile_croak(aTHX_ "Unsupported token encountered");
1128 0           return &PL_sv_undef;
1129             }
1130             MODULE = Data::Smile::XS PACKAGE = Data::Smile::XS
1131             PROTOTYPES: DISABLE
1132              
1133             BOOT:
1134 10           init_json_bool_cache(aTHX);
1135              
1136             SV*
1137             encode_smile(sv, ...)
1138             SV *sv
1139             PREINIT:
1140 15           SV *opts = NULL;
1141 15           HV *opts_hv = NULL;
1142 15           HE *he = NULL;
1143 15           STRLEN klen = 0;
1144             CODE:
1145             {
1146 15 50         if (items > 2) smile_croak(aTHX_ "encode_smile expects at most 2 arguments");
1147 15 100         if (items == 2) {
1148 10           opts = ST(1);
1149 10 100         if (SvOK(opts)) {
1150 7 50         if (!SvROK(opts) || SvTYPE(SvRV(opts)) != SVt_PVHV)
    50          
1151 0           smile_croak(aTHX_ "Options must be a hash reference");
1152 7           opts_hv = (HV*)SvRV(opts);
1153             }
1154             }
1155              
1156 15           SV *out = newSVpvn("", 0);
1157              
1158 15           int write_header = 1;
1159 15           int shared_names = 1;
1160 15           int shared_values = 1;
1161 15           int canonical = 0;
1162              
1163 15 100         if (opts_hv) {
1164 7           hv_iterinit(opts_hv);
1165 14 100         while ((he = hv_iternext(opts_hv))) {
1166 9 50         const char *k = HePV(he, klen);
1167 9           SV *v = HeVAL(he);
1168 9 100         if (klen == 12 && memEQ(k, "write_header", 12)) {
    100          
1169 3           write_header = SvTRUE(v) ? 1 : 0;
1170 3           continue;
1171             }
1172 6 100         if (klen == 12 && memEQ(k, "shared_names", 12)) {
    50          
1173 1           shared_names = SvTRUE(v) ? 1 : 0;
1174 1           continue;
1175             }
1176 5 100         if (klen == 13 && memEQ(k, "shared_values", 13)) {
    50          
1177 2           shared_values = SvTRUE(v) ? 1 : 0;
1178 2           continue;
1179             }
1180 3 100         if (klen == 9 && memEQ(k, "canonical", 9)) {
    50          
1181 1           canonical = SvTRUE(v) ? 1 : 0;
1182 1           continue;
1183             }
1184 2           smile_croak(aTHX_ "Unknown option for encode_smile");
1185             }
1186             }
1187              
1188 13 100         if (!write_header) {
1189 3 100         if (!hv_exists(opts_hv, "shared_values", 13)) {
1190 2           shared_values = 0;
1191             }
1192             }
1193              
1194 13 100         if (write_header) {
1195 10           unsigned char h3 = 0;
1196 10 50         if (shared_names) h3 |= 0x01;
1197 10 100         if (shared_values) h3 |= 0x02;
1198 10           unsigned char hdr[4] = { SMILE_HDR0, SMILE_HDR1, SMILE_HDR2, h3 };
1199 10           wr_bytes(aTHX_ out, hdr, 4);
1200             }
1201              
1202             smile_enc_ctx_t ctx;
1203 13           ctx.shared_names = shared_names;
1204 13           ctx.shared_values = shared_values;
1205 13           ctx.canonical = canonical;
1206 13           ctx.seen_name_count = 0;
1207 13           ctx.seen_value_count = 0;
1208 13           ctx.seen_names_map = (SV*)newRV_noinc((SV*)newHV());
1209 13           ctx.seen_values_map = (SV*)newRV_noinc((SV*)newHV());
1210              
1211 13           encode_value(aTHX_ out, &ctx, sv);
1212              
1213 13           SvREFCNT_dec(ctx.seen_names_map);
1214 13           SvREFCNT_dec(ctx.seen_values_map);
1215              
1216 13           RETVAL = out;
1217             }
1218             OUTPUT:
1219             RETVAL
1220              
1221             SV*
1222             decode_smile(bytes, ...)
1223             SV *bytes
1224             PREINIT:
1225 31           SV *opts = NULL;
1226 31           HV *opts_hv = NULL;
1227 31           HE *he = NULL;
1228 31           STRLEN klen = 0;
1229 31           int require_header = 0;
1230             CODE:
1231             {
1232 31 50         if (items > 2) smile_croak(aTHX_ "decode_smile expects at most 2 arguments");
1233 31 100         if (items == 2) {
1234 5           opts = ST(1);
1235 5 50         if (SvOK(opts)) {
1236 5 50         if (!SvROK(opts) || SvTYPE(SvRV(opts)) != SVt_PVHV)
    50          
1237 0           smile_croak(aTHX_ "Options must be a hash reference");
1238 5           opts_hv = (HV*)SvRV(opts);
1239             }
1240             }
1241              
1242 31           STRLEN len = 0;
1243 31           const unsigned char *p = (const unsigned char*)SvPVbyte(bytes, len);
1244              
1245             smile_reader_t r;
1246 31           r.p = p;
1247 31           r.end = p + len;
1248              
1249             smile_dec_ctx_t ctx;
1250 31           ctx.shared_names = 1; /* default true if header missing :contentReference[oaicite:3]{index=3} */
1251 31           ctx.shared_values = 0; /* default false if header missing :contentReference[oaicite:4]{index=4} */
1252 31           ctx.use_bigint = 1;
1253 31           ctx.json_bool = 1;
1254 31           ctx.seen_name_count = 0;
1255 31           ctx.seen_value_count = 0;
1256 31775 100         for (int i = 0; i < MAX_SHARED_NAMES; i++) ctx.seen_names[i] = NULL;
1257 31775 100         for (int i = 0; i < MAX_SHARED_VALUES; i++) ctx.seen_values[i] = NULL;
1258              
1259 31 100         if (opts_hv) {
1260 5           hv_iterinit(opts_hv);
1261 8 100         while ((he = hv_iternext(opts_hv))) {
1262 4 50         const char *k = HePV(he, klen);
1263 4 100         if (klen == 10 && memEQ(k, "use_bigint", 10)) {
    50          
1264 1           SV *v = HeVAL(he);
1265 1           ctx.use_bigint = SvTRUE(v) ? 1 : 0;
1266 1           continue;
1267             }
1268 3 100         if (klen == 14 && memEQ(k, "require_header", 14)) {
    50          
1269 2           SV *v = HeVAL(he);
1270 2           require_header = SvTRUE(v) ? 1 : 0;
1271 2           continue;
1272             }
1273 1 50         if (klen == 9 && memEQ(k, "json_bool", 9)) {
    0          
1274 0           SV *v = HeVAL(he);
1275 0           ctx.json_bool = SvTRUE(v) ? 1 : 0;
1276 0           continue;
1277             }
1278 1           smile_croak(aTHX_ "Unknown option for decode_smile");
1279             }
1280             }
1281              
1282 30 50         if ((size_t)(r.end - r.p) >= 4 && r.p[0] == SMILE_HDR0 && r.p[1] == SMILE_HDR1 && r.p[2] == SMILE_HDR2) {
    100          
    50          
    50          
1283 26           unsigned char h3 = r.p[3];
1284 26           r.p += 4;
1285 26           ctx.shared_names = (h3 & 0x01) ? 1 : 0;
1286 26           ctx.shared_values = (h3 & 0x02) ? 1 : 0;
1287             }
1288 4 100         else if (require_header) {
1289 1           smile_croak(aTHX_ "Smile header required");
1290             }
1291              
1292 29           RETVAL = decode_value(aTHX_ &r, &ctx);
1293              
1294 747 50         for (int i = 0; i < ctx.seen_name_count; i++) if (ctx.seen_names[i]) SvREFCNT_dec(ctx.seen_names[i]);
    100          
1295 2333 50         for (int i = 0; i < ctx.seen_value_count; i++) if (ctx.seen_values[i]) SvREFCNT_dec(ctx.seen_values[i]);
    100          
1296             }
1297             OUTPUT:
1298             RETVAL