File Coverage

cbor_free_encode.c
Criterion Covered Total %
statement 273 277 98.5
branch 188 286 65.7
condition n/a
subroutine n/a
pod n/a
total 461 563 81.8


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 62 100         : _SORT(a)->length < _SORT(b)->length ? -1
152 48 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 43121           static inline void _COPY_INTO_ENCODE( encode_ctx *encode_state, const unsigned char *hdr, STRLEN len) {
169 43121 100         if ( (len + encode_state->len) > encode_state->buflen ) {
170 29           Renew( encode_state->buffer, encode_state->buflen + len + ENCODE_ALLOC_CHUNK_SIZE, char );
171 29           encode_state->buflen += len + ENCODE_ALLOC_CHUNK_SIZE;
172             }
173              
174 43121           Copy( hdr, encode_state->buffer + encode_state->len, len, char );
175 43121           encode_state->len += len;
176 43121           }
177              
178             // TODO? This could be a macro … it’d just be kind of unwieldy as such.
179 22284           static inline void _init_length_buffer( pTHX_ UV num, enum CBOR_TYPE major_type, encode_ctx *encode_state ) {
180 22284           union control_byte *scratch0 = (void *) encode_state->scratch;
181 22284           scratch0->pieces.major_type = major_type;
182              
183 22284 100         if ( num < CBOR_LENGTH_SMALL ) {
184 5391           scratch0->pieces.length_type = (uint8_t) num;
185              
186 5391           _COPY_INTO_ENCODE(encode_state, encode_state->scratch, 1);
187             }
188 16893 100         else if ( num <= 0xff ) {
189 672           scratch0->pieces.length_type = CBOR_LENGTH_SMALL;
190 672           encode_state->scratch[1] = (uint8_t) num;
191              
192 672           _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 22284           }
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 57           static inline I32 _magic_safe_hv_iterinit( pTHX_ HV* hash ) {
246             I32 count;
247              
248 57 100         if (SvMAGICAL(hash)) {
249 1           count = 0;
250              
251 1227 100         while (hv_iternext(hash)) count++;
252              
253 1           hv_iterinit(hash);
254             }
255             else {
256 56           count = hv_iterinit(hash);
257             }
258              
259 57           return count;
260             }
261              
262 1099           static inline void _encode_string_sv( pTHX_ encode_ctx* encode_state, SV* value ) {
263 1099 50         char *val = SvPOK(value) ? SvPVX(value) : SvPV_nolen(value);
    0          
264              
265 1099           STRLEN len = SvCUR(value);
266              
267 1099           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 1099 100         _init_length_buffer( aTHX_
282             len,
283             (encode_as_text ? CBOR_TYPE_UTF8 : CBOR_TYPE_BINARY),
284             encode_state
285             );
286              
287 1099           _COPY_INTO_ENCODE( encode_state, (unsigned char *) val, len );
288 1099           }
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 10           static inline void _encode_string_utf8( pTHX_ encode_ctx* encode_state, SV* value ) {
307 10           SV *to_encode = newSVsv(value);
308 10           sv_2mortal(to_encode);
309              
310 10 100         UTF8_DOWNGRADE_IF_NEEDED(encode_state, to_encode);
    100          
311              
312 8           SvUTF8_on(to_encode);
313              
314 8           _encode_string_sv( aTHX_ encode_state, to_encode );
315 8           }
316              
317 10           static inline void _encode_string_octets( pTHX_ encode_ctx* encode_state, SV* value ) {
318 10           SV *to_encode = newSVsv(value);
319 10           sv_2mortal(to_encode);
320              
321 10 100         UTF8_DOWNGRADE_IF_NEEDED(encode_state, to_encode);
    100          
322              
323 8           _encode_string_sv( aTHX_ encode_state, to_encode );
324 8           }
325              
326 18           static inline void _upgrade_and_store_hash_key( pTHX_ HE* h_entry, encode_ctx *encode_state ) {
327             SV* key_sv;
328 18 50         CBF_HeSVKEY_force(h_entry, key_sv);
    50          
    50          
329 18           sv_utf8_upgrade(key_sv);
330 18           _encode_string_sv( aTHX_ encode_state, key_sv );
331 18           }
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 19989           void _encode( pTHX_ SV *value, encode_ctx *encode_state ) {
346 19989           ++encode_state->recurse_count;
347              
348 19989 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 19987 100         SvGETMAGIC(value);
    50          
358              
359 19987 100         if (!SvROK(value)) {
360              
361 19556 100         if (SvIOK(value)) {
362 18219           IV val = SvIVX(value);
363              
364             // In testing, Perl’s (0 + ~0) evaluated as < 0 here,
365             // but the SvUOK() check fixes that.
366 18219 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 18219           _init_length_buffer( aTHX_ val, CBOR_TYPE_UINT, encode_state );
372             }
373             }
374 1337 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 1310 100         else if (!SvOK(value)) {
    50          
    50          
414 225           _COPY_INTO_ENCODE(encode_state, &CBOR_NULL_U8, 1);
415             }
416             else {
417 1085           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 10           _encode_string_utf8( aTHX_ encode_state, value );
426 8           break;
427             case CBF_STRING_ENCODE_OCTETS:
428 10           _encode_string_octets( aTHX_ encode_state, value );
429 19552           break;
430              
431             default:
432             assert(0);
433             }
434             }
435             }
436 431 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 317 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 72 100         else if (SVt_PVHV == SvTYPE(SvRV(value))) {
477 59           HV *hash = (HV *)SvRV(value);
478              
479 59 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 57           I32 keyscount = _magic_safe_hv_iterinit(aTHX_ hash);
487              
488 57           _init_length_buffer( aTHX_ keyscount, CBOR_TYPE_MAP, encode_state );
489              
490 57 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 2568 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             // encode_state h_entry key key_length
555              
556 2517           switch (encode_state->string_encode_mode) {
557             case CBF_STRING_ENCODE_SV:
558 2501 100         if (HeUTF8(h_entry) || !CBF_HeUTF8(h_entry)) {
    50          
    100          
    100          
    50          
    50          
    50          
    100          
    50          
    100          
559 2487 100         STORE_PLAIN_HASH_KEY( encode_state, h_entry, key, key_length, HeUTF8(h_entry) ? CBOR_TYPE_UTF8 : CBOR_TYPE_BINARY );
    50          
    100          
    100          
560             }
561             else {
562 14           _upgrade_and_store_hash_key( aTHX_ h_entry, encode_state);
563             }
564 2501           break;
565              
566             case CBF_STRING_ENCODE_UNICODE:
567 6 50         if (HeUTF8(h_entry)) {
    100          
568 2 50         STORE_PLAIN_HASH_KEY( encode_state, h_entry, key, key_length, CBOR_TYPE_UTF8 );
    0          
569             }
570             else {
571 4           _upgrade_and_store_hash_key( aTHX_ h_entry, encode_state);
572              
573             }
574 6           break;
575              
576             case CBF_STRING_ENCODE_UTF8:
577 5 50         if (HeUTF8(h_entry)) {
    100          
578 2           _downgrade_and_store_hash_key( aTHX_ h_entry, encode_state, CBOR_TYPE_UTF8 );
579             }
580             else {
581 3 50         STORE_PLAIN_HASH_KEY( encode_state, h_entry, key, key_length, CBOR_TYPE_UTF8 );
    0          
582             }
583              
584 3           break;
585              
586             case CBF_STRING_ENCODE_OCTETS:
587 5 50         if (HeUTF8(h_entry)) {
    100          
588 2           _downgrade_and_store_hash_key( aTHX_ h_entry, encode_state, CBOR_TYPE_BINARY );
589             }
590             else {
591 3 50         STORE_PLAIN_HASH_KEY( encode_state, h_entry, key, key_length, CBOR_TYPE_BINARY );
    0          
592             }
593              
594 3           break;
595              
596             default:
597             assert(0);
598             }
599              
600 2513           _encode( aTHX_ hv_iterval(hash, h_entry), encode_state );
601             }
602             }
603             }
604             }
605 25 100         else if (encode_state->encode_scalar_refs && IS_SCALAR_REFERENCE(value)) {
    50          
606 12           SV *referent = SvRV(value);
607              
608 12 100         if (!encode_state->reftracker || _check_reference( aTHX_ referent, encode_state)) {
    100          
609 10           _encode_tag( aTHX_ CBOR_TAG_INDIRECTION, referent, encode_state );
610             }
611             }
612             else {
613 1           _croak_unrecognized(aTHX_ encode_state, value);
614             }
615              
616 19776           --encode_state->recurse_count;
617 19776           }
618              
619 115           static inline void _encode_tag( pTHX_ IV tagnum, SV *value, encode_ctx *encode_state ) {
620 115           _init_length_buffer( aTHX_ tagnum, CBOR_TYPE_TAG, encode_state );
621 115           _encode( aTHX_ value, encode_state );
622 115           }
623              
624             //----------------------------------------------------------------------
625              
626 16961           encode_ctx cbf_encode_ctx_create(uint8_t flags, enum cbf_string_encode_mode string_encode_mode) {
627             encode_ctx encode_state;
628              
629 16961           encode_state.buffer = NULL;
630 16961           Newx( encode_state.buffer, ENCODE_ALLOC_CHUNK_SIZE, char );
631              
632 16961           encode_state.buflen = ENCODE_ALLOC_CHUNK_SIZE;
633 16961           encode_state.len = 0;
634 16961           encode_state.recurse_count = 0;
635              
636 16961           encode_state.is_canonical = !!(flags & ENCODE_FLAG_CANONICAL);
637              
638 16961           encode_state.text_keys = !!(flags & ENCODE_FLAG_TEXT_KEYS);
639              
640 16961           encode_state.encode_scalar_refs = !!(flags & ENCODE_FLAG_SCALAR_REFS);
641              
642 16961 100         if (flags & ENCODE_FLAG_PRESERVE_REFS) {
643 2           Newxz( encode_state.reftracker, 1, void * );
644             }
645             else {
646 16959           encode_state.reftracker = NULL;
647             }
648              
649 16961           encode_state.string_encode_mode = string_encode_mode;
650              
651 16961           return encode_state;
652             }
653              
654 16961           void cbf_encode_ctx_free_reftracker(encode_ctx* encode_state) {
655 16961           Safefree( encode_state->reftracker );
656 16961           }
657              
658 16           void cbf_encode_ctx_free_all(encode_ctx* encode_state) {
659 16           cbf_encode_ctx_free_reftracker(encode_state);
660 16           Safefree( encode_state->buffer );
661 16           }
662              
663 16961           SV *cbf_encode( pTHX_ SV *value, encode_ctx *encode_state, SV *RETVAL ) {
664 16961           _encode(aTHX_ value, encode_state);
665              
666             // Ensure that there’s a trailing NUL:
667 16945           _COPY_INTO_ENCODE( encode_state, &NUL, 1 );
668              
669 16945           return RETVAL;
670             }