File Coverage

cbor_free_encode.c
Criterion Covered Total %
statement 273 277 98.5
branch 196 286 68.5
condition n/a
subroutine n/a
pod n/a
total 469 563 83.3


line stmt bran cond sub pod time code
1             #include "easyxs/init.h"
2              
3             #include
4              
5             #include "cbor_free_encode.h"
6              
7             #define TAGGED_CLASS "CBOR::Free::Tagged"
8              
9             #define IS_SCALAR_REFERENCE(value) SvTYPE(SvRV(value)) <= SVt_PVMG
10              
11             static const unsigned char NUL = 0;
12             static const unsigned char CBOR_NULL_U8 = CBOR_NULL;
13             static const unsigned char CBOR_FALSE_U8 = CBOR_FALSE;
14             static const unsigned char CBOR_TRUE_U8 = CBOR_TRUE;
15              
16             static const unsigned char CBOR_INF_SHORT[3] = { 0xf9, 0x7c, 0x00 };
17             static const unsigned char CBOR_NAN_SHORT[3] = { 0xf9, 0x7e, 0x00 };
18             static const unsigned char CBOR_NEGINF_SHORT[3] = { 0xf9, 0xfc, 0x00 };
19              
20             static HV *tagged_stash = NULL;
21              
22             // Perl 5.18 and below appear not to set SvUTF8 if HeUTF8.
23             // This macro corrects for that:
24             #if PERL_VERSION < 20
25             #define CBF_HeSVKEY_force(h_entry, sv) \
26             sv = HeSVKEY_force(h_entry); \
27             if (HeUTF8(h_entry)) SvUTF8_on(sv);
28             #else
29             #define CBF_HeSVKEY_force(h_entry, sv) \
30             sv = HeSVKEY_force(h_entry);
31             #endif
32              
33             #define UTF8_DOWNGRADE_OR_CROAK(encode_state, sv) \
34             if (!sv_utf8_downgrade(sv, true)) { \
35             _croak_wide_character( aTHX_ encode_state, sv ); \
36             }
37              
38             #define UTF8_DOWNGRADE_IF_NEEDED(encode_state, to_encode) \
39             if (SvUTF8(to_encode)) { \
40             UTF8_DOWNGRADE_OR_CROAK(encode_state, to_encode); \
41             }
42              
43             #define STORE_PLAIN_HASH_KEY(encode_state, h_entry, key, key_length, major_type) \
44             key = HePV(h_entry, key_length); \
45             _init_length_buffer( aTHX_ key_length, major_type, encode_state ); \
46             _COPY_INTO_ENCODE( encode_state, (unsigned char *) key, key_length );
47              
48             #define STORE_SORTABLE_HASH_KEY(sortables_entry, h_entry, key, key_length, key_is_utf8) \
49             key = HePV(h_entry, key_length); \
50             sortables_entry.is_utf8 = key_is_utf8; \
51             sortables_entry.buffer = key; \
52             sortables_entry.length = key_length;
53              
54             #define STORE_UPGRADED_SORTABLE_HASH_KEY(sortables_entry, h_entry) \
55             SV* key_sv; \
56             CBF_HeSVKEY_force(h_entry, key_sv); \
57             sv_utf8_upgrade(key_sv); \
58             sortables_entry.is_utf8 = true; \
59             sortables_entry.buffer = SvPV(key_sv, sortables_entry.length);
60              
61             #define STORE_DOWNGRADED_SORTABLE_HASH_KEY(sortables_entry, h_entry, key_is_utf8) \
62             SV* key_sv; \
63             CBF_HeSVKEY_force(h_entry, key_sv); \
64             UTF8_DOWNGRADE_OR_CROAK(encode_state, key_sv); \
65             sortables_entry.is_utf8 = key_is_utf8; \
66             sortables_entry.buffer = SvPV(key_sv, sortables_entry.length);
67              
68             //----------------------------------------------------------------------
69              
70             // These encode num as big-endian into buffer.
71             // Importantly, on big-endian systems this is just a memcpy,
72             // while on little-endian systems it’s a bswap.
73              
74 15901           static inline void _u16_to_buffer( UV num, uint8_t *buffer ) {
75 15901           *(buffer++) = num >> 8;
76 15901           *(buffer++) = num;
77 15901           }
78              
79 310           static inline void _u32_to_buffer( UV num, unsigned char *buffer ) {
80 310           *(buffer++) = num >> 24;
81 310           *(buffer++) = num >> 16;
82 310           *(buffer++) = num >> 8;
83 310           *(buffer++) = num;
84 310           }
85              
86 10           static inline void _u64_to_buffer( UV num, unsigned char *buffer ) {
87             #if IS_64_BIT
88 10           *(buffer++) = num >> 56;
89 10           *(buffer++) = num >> 48;
90 10           *(buffer++) = num >> 40;
91 10           *(buffer++) = num >> 32;
92             #endif
93 10           *(buffer++) = num >> 24;
94 10           *(buffer++) = num >> 16;
95 10           *(buffer++) = num >> 8;
96 10           *(buffer++) = num;
97 10           }
98              
99             //----------------------------------------------------------------------
100             // Croakers
101              
102 2           static inline void _croak_unrecognized(pTHX_ encode_ctx *encode_state, SV *value) {
103 2 50         char * words[3] = { "Unrecognized", SvPV_nolen(value), NULL };
104              
105 2           cbf_encode_ctx_free_all(encode_state);
106              
107 2           _die( G_DISCARD, words );
108             }
109              
110 12           static inline void _croak_wide_character(pTHX_ encode_ctx *encode_state, SV *value) {
111 24           SV* args[2] = {
112 12           newSVpvs("WideCharacter"),
113 12           newSVsv(value),
114             };
115              
116 12           cbf_encode_ctx_free_all(encode_state);
117              
118 12           cbf_die_with_arguments( aTHX_ 2, args );
119 0           }
120              
121             // This has to be a macro because _croak() needs a string literal.
122             #define _croak_encode(encode_state, str) \
123             cbf_encode_ctx_free_all(encode_state); \
124             _croak(str);
125              
126             //----------------------------------------------------------------------
127              
128             // NOTE: Contrary to JSON’s “canonical” order, for canonical CBOR
129             // keys are only byte-sorted if their lengths are identical. Thus,
130             // “z” sorts EARLIER than “aa”. (cf. section 3.9 of the RFC)
131              
132             #define _SORT(x) ((struct sortable_hash_entry *)x)
133              
134 34           int _sort_map_keys( const void* a, const void* b ) {
135              
136             // The CBOR RFC defines canonical sorting such that the
137             // *encoded* keys are what gets sorted; however, it’s easier to
138             // anticipate the sort order algorithmically rather than to
139             // create the encoded keys *then* sort those. Since Perl hash keys
140             // are always strings (either with or without the UTF8 flag), we
141             // only have 2 CBOR types to deal with (text & binary strings) and
142             // can sort accordingly.
143              
144 34           return (
145 34           _SORT(a)->is_utf8 < _SORT(b)->is_utf8 ? -1
146 68 50         : _SORT(a)->is_utf8 > _SORT(b)->is_utf8 ? 1
147 62 100         : _SORT(a)->length < _SORT(b)->length ? -1
148 46 100         : _SORT(a)->length > _SORT(b)->length ? 1
149 18 100         : memcmp( _SORT(a)->buffer, _SORT(b)->buffer, _SORT(a)->length )
150             );
151             }
152              
153             //----------------------------------------------------------------------
154              
155 114           static inline HV *_get_tagged_stash() {
156 114 100         if (!tagged_stash) {
157             dTHX;
158 5           tagged_stash = gv_stashpv(TAGGED_CLASS, 1);
159             }
160              
161 114           return tagged_stash;
162             }
163              
164 44743           static inline void _COPY_INTO_ENCODE( encode_ctx *encode_state, const unsigned char *hdr, STRLEN len) {
165 44743 100         if ( (len + encode_state->len) > encode_state->buflen ) {
166 33           Renew( encode_state->buffer, encode_state->buflen + len + ENCODE_ALLOC_CHUNK_SIZE, char );
167 33           encode_state->buflen += len + ENCODE_ALLOC_CHUNK_SIZE;
168             }
169              
170 44743           Copy( hdr, encode_state->buffer + encode_state->len, len, char );
171 44743           encode_state->len += len;
172 44743           }
173              
174             // TODO? This could be a macro … it’d just be kind of unwieldy as such.
175 23362           static inline void _init_length_buffer( pTHX_ UV num, enum CBOR_TYPE major_type, encode_ctx *encode_state ) {
176 23362           uint8_t* control_byte_p = (void *) encode_state->scratch;
177 23362           *control_byte_p = major_type << CONTROL_BYTE_MAJOR_TYPE_SHIFT;
178              
179 23362 100         if ( num < CBOR_LENGTH_SMALL ) {
180 6465           *control_byte_p |= (uint8_t) num;
181              
182 6465           _COPY_INTO_ENCODE(encode_state, encode_state->scratch, 1);
183             }
184 16897 100         else if ( num <= 0xff ) {
185 676           *control_byte_p |= CBOR_LENGTH_SMALL;
186 676           encode_state->scratch[1] = (uint8_t) num;
187              
188 676           _COPY_INTO_ENCODE(encode_state, encode_state->scratch, 2);
189             }
190 16221 100         else if ( num <= 0xffff ) {
191 15901           *control_byte_p |= CBOR_LENGTH_MEDIUM;
192              
193 15901           _u16_to_buffer( num, 1 + encode_state->scratch );
194              
195 15901           _COPY_INTO_ENCODE(encode_state, encode_state->scratch, 3);
196             }
197 320 100         else if ( num <= 0xffffffffU ) {
198 310           *control_byte_p |= CBOR_LENGTH_LARGE;
199              
200 310           _u32_to_buffer( num, 1 + encode_state->scratch );
201              
202 310           _COPY_INTO_ENCODE(encode_state, encode_state->scratch, 5);
203             }
204             else {
205 10           *control_byte_p |= CBOR_LENGTH_HUGE;
206              
207 10           _u64_to_buffer( num, 1 + encode_state->scratch );
208              
209 10           _COPY_INTO_ENCODE(encode_state, encode_state->scratch, 9);
210             }
211 23362           }
212              
213             void _encode( pTHX_ SV *value, encode_ctx *encode_state );
214             static inline void _encode_tag( pTHX_ IV tagnum, SV *value, encode_ctx *encode_state );
215              
216             // Return indicates to encode the actual value.
217 14           bool _check_reference( pTHX_ SV *varref, encode_ctx *encode_state ) {
218 14 100         if ( SvREFCNT(varref) > 1 ) {
219             void *this_ref;
220              
221 12           IV r = 0;
222              
223 24 100         while ( (this_ref = encode_state->reftracker[r++]) ) {
224 18 100         if (this_ref == varref) {
225 6           _init_length_buffer( aTHX_ CBOR_TAG_SHAREDREF, CBOR_TYPE_TAG, encode_state );
226 6           _init_length_buffer( aTHX_ r - 1, CBOR_TYPE_UINT, encode_state );
227 6           return false;
228             }
229             }
230              
231 6 50         Renew( encode_state->reftracker, 1 + r, void * );
232 6           encode_state->reftracker[r - 1] = varref;
233 6           encode_state->reftracker[r] = NULL;
234              
235 6           _init_length_buffer( aTHX_ CBOR_TAG_SHAREABLE, CBOR_TYPE_TAG, encode_state );
236             }
237              
238 8           return true;
239             }
240              
241 61           static inline I32 _magic_safe_hv_iterinit( pTHX_ HV* hash ) {
242             I32 count;
243              
244 61 100         if (SvMAGICAL(hash)) {
245 5           count = 0;
246              
247 1767 100         while (hv_iternext(hash)) count++;
248              
249 5           hv_iterinit(hash);
250             }
251             else {
252 56           count = hv_iterinit(hash);
253             }
254              
255 61           return count;
256             }
257              
258 1235           static inline void _encode_string_sv( pTHX_ encode_ctx* encode_state, SV* value ) {
259 1235 50         char *val = SvPOK(value) ? SvPVX(value) : SvPV_nolen(value);
    0          
260              
261 1235           STRLEN len = SvCUR(value);
262              
263 1235           bool encode_as_text = !!SvUTF8(value);
264              
265             /*
266             if (!encode_as_text) {
267             STRLEN i;
268             for (i=0; i
269             if (val[i] & 0x80) break;
270             }
271              
272             // Encode as text if there were no high-bit octets.
273             encode_as_text = (i == len);
274             }
275             */
276              
277 1235 100         _init_length_buffer( aTHX_
278             len,
279             (encode_as_text ? CBOR_TYPE_UTF8 : CBOR_TYPE_BINARY),
280             encode_state
281             );
282              
283 1235           _COPY_INTO_ENCODE( encode_state, (unsigned char *) val, len );
284 1235           }
285              
286 10           static inline void _encode_string_unicode( pTHX_ encode_ctx* encode_state, SV* value ) {
287             SV *to_encode;
288              
289 10 100         if (SvUTF8(value)) {
290 4           to_encode = value;
291             }
292             else {
293 6           to_encode = newSVsv(value);
294 6           sv_2mortal(to_encode);
295              
296 6           sv_utf8_upgrade(to_encode);
297             }
298              
299 10           _encode_string_sv( aTHX_ encode_state, to_encode );
300 10           }
301              
302 11           static inline void _encode_string_utf8( pTHX_ encode_ctx* encode_state, SV* value ) {
303 11           SV *to_encode = newSVsv(value);
304 11           sv_2mortal(to_encode);
305              
306 11 100         UTF8_DOWNGRADE_IF_NEEDED(encode_state, to_encode);
    100          
307              
308 9           SvUTF8_on(to_encode);
309              
310 9           _encode_string_sv( aTHX_ encode_state, to_encode );
311 9           }
312              
313 11           static inline void _encode_string_octets( pTHX_ encode_ctx* encode_state, SV* value ) {
314 11           SV *to_encode = newSVsv(value);
315 11           sv_2mortal(to_encode);
316              
317 11 100         UTF8_DOWNGRADE_IF_NEEDED(encode_state, to_encode);
    100          
318              
319 9           _encode_string_sv( aTHX_ encode_state, to_encode );
320 9           }
321              
322 152           static inline void _upgrade_and_store_hash_key( pTHX_ HE* h_entry, encode_ctx *encode_state ) {
323             SV* key_sv;
324 152 50         CBF_HeSVKEY_force(h_entry, key_sv);
    100          
    50          
325 152           sv_utf8_upgrade(key_sv);
326 152           _encode_string_sv( aTHX_ encode_state, key_sv );
327 152           }
328              
329 4           static inline void _downgrade_and_store_hash_key( pTHX_ HE* h_entry, encode_ctx *encode_state, enum CBOR_TYPE string_type ) {
330             SV* key_sv;
331 4 50         CBF_HeSVKEY_force(h_entry, key_sv);
    50          
    50          
332 4 50         UTF8_DOWNGRADE_OR_CROAK(encode_state, key_sv);
333              
334             // We can do this without altering h_entry itself because
335             // key_sv is just a mortal copy of the key.
336 0 0         if (string_type == CBOR_TYPE_UTF8) SvUTF8_on(key_sv);
337              
338 0           _encode_string_sv( aTHX_ encode_state, key_sv );
339 0           }
340              
341 20531           void _encode( pTHX_ SV *value, encode_ctx *encode_state ) {
342 20531           ++encode_state->recurse_count;
343              
344 20531 100         if (encode_state->recurse_count > MAX_ENCODE_RECURSE) {
345              
346             // call_pv() killed the process in Win32; this seems to fix that.
347             static char * words[] = { NULL };
348 2           call_argv("CBOR::Free::_die_recursion", G_EVAL|G_DISCARD, words);
349              
350 2           _croak_encode( encode_state, NULL );
351             }
352              
353 20529 100         SvGETMAGIC(value);
    50          
354              
355 20529 100         if (!SvROK(value)) {
356              
357 20094 100         if (SvIOK(value)) {
358 18755           IV val = SvIVX(value);
359              
360             // In testing, Perl’s (0 + ~0) evaluated as < 0 here,
361             // but the SvUOK() check fixes that.
362 18755 100         if (val < 0 && !SvUOK(value)) {
    100          
363 39           _init_length_buffer( aTHX_ -(++val), CBOR_TYPE_NEGINT, encode_state );
364             }
365             else {
366             // NB: SvUOK doesn’t work to identify nonnegatives … ?
367 18755           _init_length_buffer( aTHX_ val, CBOR_TYPE_UINT, encode_state );
368             }
369             }
370 1339 100         else if (SvNOK(value)) {
371 27           NV val_nv = SvNVX(value);
372              
373 27 100         if (Perl_isnan(val_nv)) {
374 3           _COPY_INTO_ENCODE(encode_state, CBOR_NAN_SHORT, 3);
375             }
376 24 100         else if (Perl_isinf(val_nv)) {
377 6 100         if (val_nv > 0) {
378 3           _COPY_INTO_ENCODE(encode_state, CBOR_INF_SHORT, 3);
379             }
380             else {
381 6           _COPY_INTO_ENCODE(encode_state, CBOR_NEGINF_SHORT, 3);
382             }
383             }
384             else {
385              
386             // Typecast to a double to accommodate long-double perls.
387 18           double val = (double) val_nv;
388              
389 18           char *valptr = (char *) &val;
390              
391             #if IS_LITTLE_ENDIAN
392 18           encode_state->scratch[0] = CBOR_DOUBLE;
393 18           encode_state->scratch[1] = valptr[7];
394 18           encode_state->scratch[2] = valptr[6];
395 18           encode_state->scratch[3] = valptr[5];
396 18           encode_state->scratch[4] = valptr[4];
397 18           encode_state->scratch[5] = valptr[3];
398 18           encode_state->scratch[6] = valptr[2];
399 18           encode_state->scratch[7] = valptr[1];
400 18           encode_state->scratch[8] = valptr[0];
401              
402 27           _COPY_INTO_ENCODE(encode_state, encode_state->scratch, 9);
403             #else
404             unsigned char bytes[9] = { CBOR_DOUBLE, valptr[0], valptr[1], valptr[2], valptr[3], valptr[4], valptr[5], valptr[6], valptr[7] };
405             _COPY_INTO_ENCODE(encode_state, bytes, 9);
406             #endif
407             }
408             }
409 1312 100         else if (!SvOK(value)) {
    50          
    50          
410 225           _COPY_INTO_ENCODE(encode_state, &CBOR_NULL_U8, 1);
411             }
412             else {
413 1087           switch (encode_state->string_encode_mode) {
414             case CBF_STRING_ENCODE_SV:
415 1055           _encode_string_sv( aTHX_ encode_state, value );
416 1055           break;
417             case CBF_STRING_ENCODE_UNICODE:
418 10           _encode_string_unicode( aTHX_ encode_state, value );
419 10           break;
420             case CBF_STRING_ENCODE_UTF8:
421 11           _encode_string_utf8( aTHX_ encode_state, value );
422 9           break;
423             case CBF_STRING_ENCODE_OCTETS:
424 11           _encode_string_octets( aTHX_ encode_state, value );
425 20090           break;
426              
427             default:
428             assert(0);
429             }
430             }
431             }
432 435 100         else if (sv_isobject(value)) {
433 114           HV *stash = SvSTASH( SvRV(value) );
434              
435 114 100         if (_get_tagged_stash() == stash) {
436 105           AV *array = (AV *)SvRV(value);
437 105           SV **tag = av_fetch(array, 0, 0);
438 105 50         IV tagnum = SvIV(*tag);
439              
440 105           _encode_tag( aTHX_ tagnum, *(av_fetch(array, 1, 0)), encode_state );
441             }
442 9 100         else if (cbf_get_boolean_stash() == stash) {
443 16 50         _COPY_INTO_ENCODE(
    50          
    50          
    50          
    50          
444             encode_state,
445 8 0         SvTRUE(SvRV(value)) ? &CBOR_TRUE_U8 : &CBOR_FALSE_U8,
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    100          
    50          
    0          
    100          
    0          
446             1
447             );
448             }
449              
450             // TODO: Support TO_JSON() or TO_CBOR() method?
451              
452 114           else _croak_unrecognized(aTHX_ encode_state, value);
453             }
454 321 100         else if (SVt_PVAV == SvTYPE(SvRV(value))) {
455 245           AV *array = (AV *)SvRV(value);
456              
457 245 100         if (!encode_state->reftracker || _check_reference( aTHX_ (SV *)array, encode_state )) {
    100          
458             SSize_t len;
459 243           len = 1 + av_len(array);
460              
461 243           _init_length_buffer( aTHX_ len, CBOR_TYPE_ARRAY, encode_state );
462              
463             SSize_t i;
464              
465             SV **cur;
466 410 100         for (i=0; i
467 362           cur = av_fetch(array, i, 0);
468 362           _encode( aTHX_ *cur, encode_state );
469             }
470             }
471             }
472 76 100         else if (SVt_PVHV == SvTYPE(SvRV(value))) {
473 63           HV *hash = (HV *)SvRV(value);
474              
475 63 100         if (!encode_state->reftracker || _check_reference( aTHX_ (SV *)hash, encode_state)) {
    100          
476             char *key;
477             STRLEN key_length;
478              
479             HE* h_entry;
480             bool heutf8;
481              
482 61           I32 keyscount = _magic_safe_hv_iterinit(aTHX_ hash);
483              
484 61           _init_length_buffer( aTHX_ keyscount, CBOR_TYPE_MAP, encode_state );
485              
486 61 100         if (encode_state->is_canonical) {
487 22           I32 curkey = 0;
488              
489 22           struct sortable_hash_entry sortables[keyscount];
490              
491 60 100         while ( (h_entry = hv_iternext(hash)) ) {
492 42 50         heutf8 = HeUTF8(h_entry);
493              
494 42           switch (encode_state->string_encode_mode) {
495             case CBF_STRING_ENCODE_SV:
496 26 50         if (heutf8 || !CBF_HeUTF8(h_entry)) {
    50          
    0          
    50          
    50          
    50          
    0          
    100          
497 22 50         STORE_SORTABLE_HASH_KEY( sortables[curkey], h_entry, key, key_length, heutf8 );
    0          
498             }
499             else {
500 4 50         STORE_UPGRADED_SORTABLE_HASH_KEY(sortables[curkey], h_entry);
    50          
    50          
    50          
501             }
502              
503 26           break;
504              
505             case CBF_STRING_ENCODE_UNICODE:
506 6 100         if (heutf8) {
507 2 50         STORE_SORTABLE_HASH_KEY( sortables[curkey], h_entry, key, key_length, true );
    0          
508             }
509             else {
510 4 50         STORE_UPGRADED_SORTABLE_HASH_KEY(sortables[curkey], h_entry);
    50          
    50          
    50          
511             }
512 6           break;
513              
514             case CBF_STRING_ENCODE_UTF8:
515             case CBF_STRING_ENCODE_OCTETS:
516 10 100         if (heutf8) {
517 4 50         STORE_DOWNGRADED_SORTABLE_HASH_KEY(sortables[curkey], h_entry, encode_state->string_encode_mode == CBF_STRING_ENCODE_UTF8);
    50          
    50          
    50          
    0          
518             }
519             else {
520 6 50         STORE_SORTABLE_HASH_KEY( sortables[curkey], h_entry, key, key_length, encode_state->string_encode_mode == CBF_STRING_ENCODE_UTF8 );
    0          
521             }
522 6           break;
523              
524             default:
525             assert(0);
526             }
527              
528 38           sortables[curkey].value = hv_iterval(hash, h_entry);
529              
530 38           curkey++;
531             }
532              
533 18           qsort(sortables, keyscount, sizeof(struct sortable_hash_entry), _sort_map_keys);
534              
535 56 100         for (curkey=0; curkey < keyscount; ++curkey) {
536 38 100         _init_length_buffer( aTHX_ sortables[curkey].length, sortables[curkey].is_utf8 ? CBOR_TYPE_UTF8 : CBOR_TYPE_BINARY, encode_state );
537 38           _COPY_INTO_ENCODE( encode_state, (unsigned char *) sortables[curkey].buffer, sortables[curkey].length );
538              
539 38           _encode( aTHX_ sortables[curkey].value, encode_state );
540             }
541             }
542             else {
543 3108 100         while ( (h_entry = hv_iternext(hash)) ) {
544              
545             /*
546             fprintf(stderr, "HeSVKEY: %p\n", HeSVKEY(h_entry));
547             fprintf(stderr, "HeUTF8: %d\n", HeUTF8(h_entry));
548             fprintf(stderr, "CBF_HeUTF8: %d\n", CBF_HeUTF8(h_entry));
549             */
550              
551 3053           switch (encode_state->string_encode_mode) {
552             case CBF_STRING_ENCODE_SV:
553 2635 100         if (HeUTF8(h_entry) || !CBF_HeUTF8(h_entry)) {
    50          
    100          
    100          
    50          
    50          
    50          
    100          
    50          
    100          
554 2621 100         STORE_PLAIN_HASH_KEY( encode_state, h_entry, key, key_length, HeUTF8(h_entry) ? CBOR_TYPE_UTF8 : CBOR_TYPE_BINARY );
    50          
    100          
    100          
555             }
556             else {
557 14           _upgrade_and_store_hash_key( aTHX_ h_entry, encode_state);
558             }
559 2635           break;
560              
561             case CBF_STRING_ENCODE_UNICODE:
562 140 100         if (HeUTF8(h_entry)) {
    100          
563 2 50         STORE_PLAIN_HASH_KEY( encode_state, h_entry, key, key_length, CBOR_TYPE_UTF8 );
    0          
564             }
565             else {
566 138           _upgrade_and_store_hash_key( aTHX_ h_entry, encode_state);
567              
568             }
569 140           break;
570              
571             case CBF_STRING_ENCODE_UTF8:
572 139 100         if (HeUTF8(h_entry)) {
    100          
573 2           _downgrade_and_store_hash_key( aTHX_ h_entry, encode_state, CBOR_TYPE_UTF8 );
574             }
575             else {
576 137 100         STORE_PLAIN_HASH_KEY( encode_state, h_entry, key, key_length, CBOR_TYPE_UTF8 );
    50          
577             }
578              
579 137           break;
580              
581             case CBF_STRING_ENCODE_OCTETS:
582 139 100         if (HeUTF8(h_entry)) {
    100          
583 2           _downgrade_and_store_hash_key( aTHX_ h_entry, encode_state, CBOR_TYPE_BINARY );
584             }
585             else {
586 137 100         STORE_PLAIN_HASH_KEY( encode_state, h_entry, key, key_length, CBOR_TYPE_BINARY );
    50          
587             }
588              
589 137           break;
590              
591             default:
592             assert(0);
593             }
594              
595 3049           _encode( aTHX_ hv_iterval(hash, h_entry), encode_state );
596             }
597             }
598             }
599             }
600 25 100         else if (encode_state->encode_scalar_refs && IS_SCALAR_REFERENCE(value)) {
    50          
601 12           SV *referent = SvRV(value);
602              
603 12 100         if (!encode_state->reftracker || _check_reference( aTHX_ referent, encode_state)) {
    100          
604 10           _encode_tag( aTHX_ CBOR_TAG_INDIRECTION, referent, encode_state );
605             }
606             }
607             else {
608 1           _croak_unrecognized(aTHX_ encode_state, value);
609             }
610              
611 20318           --encode_state->recurse_count;
612 20318           }
613              
614 115           static inline void _encode_tag( pTHX_ IV tagnum, SV *value, encode_ctx *encode_state ) {
615 115           _init_length_buffer( aTHX_ tagnum, CBOR_TYPE_TAG, encode_state );
616 115           _encode( aTHX_ value, encode_state );
617 115           }
618              
619             //----------------------------------------------------------------------
620              
621 16967           encode_ctx cbf_encode_ctx_create(uint8_t flags, enum cbf_string_encode_mode string_encode_mode) {
622             encode_ctx encode_state;
623              
624 16967           encode_state.buffer = NULL;
625 16967           Newx( encode_state.buffer, ENCODE_ALLOC_CHUNK_SIZE, char );
626              
627 16967           encode_state.buflen = ENCODE_ALLOC_CHUNK_SIZE;
628 16967           encode_state.len = 0;
629 16967           encode_state.recurse_count = 0;
630              
631 16967           encode_state.is_canonical = !!(flags & ENCODE_FLAG_CANONICAL);
632              
633 16967           encode_state.text_keys = !!(flags & ENCODE_FLAG_TEXT_KEYS);
634              
635 16967           encode_state.encode_scalar_refs = !!(flags & ENCODE_FLAG_SCALAR_REFS);
636              
637 16967 100         if (flags & ENCODE_FLAG_PRESERVE_REFS) {
638 2           Newxz( encode_state.reftracker, 1, void * );
639             }
640             else {
641 16965           encode_state.reftracker = NULL;
642             }
643              
644 16967           encode_state.string_encode_mode = string_encode_mode;
645              
646 16967           return encode_state;
647             }
648              
649 16967           void cbf_encode_ctx_free_reftracker(encode_ctx* encode_state) {
650 16967           Safefree( encode_state->reftracker );
651 16967           }
652              
653 16           void cbf_encode_ctx_free_all(encode_ctx* encode_state) {
654 16           cbf_encode_ctx_free_reftracker(encode_state);
655 16           Safefree( encode_state->buffer );
656 16           }
657              
658 16967           SV *cbf_encode( pTHX_ SV *value, encode_ctx *encode_state, SV *RETVAL ) {
659 16967           _encode(aTHX_ value, encode_state);
660              
661             // Ensure that there’s a trailing NUL:
662 16951           _COPY_INTO_ENCODE( encode_state, &NUL, 1 );
663              
664 16951           return RETVAL;
665             }