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