File Coverage

Free.xs
Criterion Covered Total %
statement 128 151 84.7
branch 92 236 38.9
condition n/a
subroutine n/a
pod n/a
total 220 387 56.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 "ppport.h"
8              
9             #include
10             #include
11             #include
12              
13             #include "cbor_free_common.h"
14              
15             #include "cbor_free_boolean.h"
16             #include "cbor_free_encode.h"
17             #include "cbor_free_decode.h"
18              
19             #define _PACKAGE "CBOR::Free"
20              
21             #define CANONICAL_OPT "canonical"
22             #define PRESERVE_REFS_OPT "preserve_references"
23             #define SCALAR_REFS_OPT "scalar_references"
24             #define STRING_ENCODE_MODE_OPT "string_encode_mode"
25              
26             #define UNUSED(x) (void)(x)
27              
28             const char* const cbf_string_encode_mode_options[] = {
29             "sv",
30             "encode_text",
31             "as_text",
32             "as_binary",
33             };
34              
35             HV *cbf_stash = NULL;
36              
37 33           SV* _seqdecode_get( pTHX_ seqdecode_ctx* seqdecode) {
38 33           decode_ctx* decode_state = seqdecode->decode_state;
39              
40 33           decode_state->curbyte = decode_state->start;
41              
42 33 100         if (decode_state->flags & CBF_FLAG_PRESERVE_REFERENCES) {
43 2           reset_reflist_if_needed(aTHX_ decode_state);
44             }
45              
46 33           SV *referent = cbf_decode_one( aTHX_ seqdecode->decode_state );
47              
48 33 100         if (seqdecode->decode_state->incomplete_by) {
49 15           seqdecode->decode_state->incomplete_by = 0;
50 15           return &PL_sv_undef;
51             }
52              
53             // TODO: Once the lead offset gets big enough,
54             // recreate this buffer.
55 18           sv_chop( seqdecode->cbor, decode_state->curbyte );
56              
57 18           advance_decode_state_buffer( aTHX_ decode_state );
58              
59 18           return newRV_noinc(referent);
60             }
61              
62 9           bool _handle_flag_call( pTHX_ decode_ctx* decode_state, SV* new_setting, U8 flagval ) {
63 9 100         if (new_setting == NULL || SvTRUE(new_setting)) {
    50          
    50          
    0          
    50          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    50          
    50          
    50          
    50          
    0          
    50          
    0          
64 5           decode_state->flags |= flagval;
65             }
66             else {
67 4           decode_state->flags &= ~flagval;
68             }
69              
70 9           return( !!(decode_state->flags & flagval) );
71             }
72              
73 116           SV * _bless_to_sv( pTHX_ SV *class, void* ptr ) {
74 116           SV *RETVAL = newSV(0);
75 116 50         sv_setref_pv(RETVAL, SvPV_nolen(class), ptr);
76              
77 116           return RETVAL;
78             }
79              
80 6           static inline void * sv_to_ptr( pTHX_ SV *self) {
81 6 50         IV tmp = SvIV((SV*)SvRV(self));
82 6           return INT2PTR(void*, tmp);
83             }
84              
85 3           static inline SV* _set_string_decode( pTHX_ SV* self, enum cbf_string_decode_mode new_setting ) {
86 3           decode_ctx* decode_state = (decode_ctx*) sv_to_ptr(aTHX_ self);
87 3           decode_state->string_decode_mode = new_setting;
88              
89 3 50         return (GIMME_V == G_VOID) ? NULL : newSVsv(self);
    50          
90             }
91              
92 3           static inline SV* _seq_set_string_decode( pTHX_ SV* self, enum cbf_string_decode_mode new_setting ) {
93 3           seqdecode_ctx* seqdecode = (seqdecode_ctx*) sv_to_ptr(aTHX_ self);
94 3           seqdecode->decode_state->string_decode_mode = new_setting;
95              
96 3 50         return (GIMME_V == G_VOID) ? NULL : newSVsv(self);
    50          
97             }
98              
99 6           static inline bool _handle_preserve_references( pTHX_ decode_ctx* decode_state, SV* new_setting ) {
100 6           bool RETVAL = _handle_flag_call( aTHX_ decode_state, new_setting, CBF_FLAG_PRESERVE_REFERENCES );
101              
102 6 100         if (RETVAL) {
103 2           ensure_reflist_exists( aTHX_ decode_state );
104             }
105 4 100         else if (NULL != decode_state->reflist) {
106 2           delete_reflist( aTHX_ decode_state );
107             }
108              
109 6           return RETVAL;
110             }
111              
112 103           static inline void _set_tag_handlers( pTHX_ decode_ctx* decode_state, U8 items_len, SV** args ) {
113 103 50         if (!(items_len % 2)) {
114 0           croak("Odd key-value pair given!");
115             }
116              
117 103 100         if (NULL == decode_state->tag_handler) {
118 102           decode_state->tag_handler = newHV();
119             }
120              
121             U8 i;
122 206 100         for (i=1; i
123 103           HV* tag_handler = decode_state->tag_handler;
124              
125 103           SV* tagnum_sv = args[i];
126 103 50         UV tagnum = SvUV(tagnum_sv);
127              
128 103           i++;
129 103 50         if (i
130 103           SV* tagcb_sv = args[i];
131              
132 103           hv_store(
133             tag_handler,
134             (const char *) &tagnum,
135             sizeof(UV),
136             tagcb_sv,
137             0
138             );
139              
140 103           SvREFCNT_inc(tagcb_sv);
141             }
142             }
143 103           }
144              
145             //----------------------------------------------------------------------
146             //----------------------------------------------------------------------
147              
148             MODULE = CBOR::Free PACKAGE = CBOR::Free
149              
150             PROTOTYPES: DISABLE
151              
152             BOOT:
153 26           cbf_stash = gv_stashpv(_PACKAGE, FALSE);
154 26           newCONSTSUB(cbf_stash, "_MAX_RECURSION", newSVuv( MAX_ENCODE_RECURSE ));
155              
156             # Used in tests; useful to refactor to its own module?
157             SV *
158             _hash_keys_2_sv( SV * hashref )
159             CODE:
160 0 0         if (SVt_PVHV != SvTYPE(SvRV(hashref))) {
161 0           croak("need hashref");
162             }
163              
164 0           HV *hash = (HV *)SvRV(hashref);
165              
166 0           HE *stored = hv_store_ent(
167             hash,
168             newSVpvs("Hello"),
169             newSVpvs("There"),
170             0
171             );
172 0 0         fprintf(stderr, "HeSVKEY after store_ent: %p, %p\n", stored, HeSVKEY(stored));
    0          
173              
174 0           HE *entry1 = hv_fetch_ent(hash, newSVpvs("Hello"), 0, 0);
175 0 0         if (NULL == entry1) {
176 0           croak("huh?");
177             }
178             else {
179 0 0         fprintf(stderr, "HeSVKEY after store_ent/fetch_ent: %p, %p\n", entry1, HeSVKEY(entry1));
    0          
180             }
181              
182             HE *h_entry;
183              
184 0 0         while ( (h_entry = hv_iternext(hash)) ) {
185 0 0         if (NULL == HeSVKEY(h_entry)) {
    0          
    0          
186 0           fprintf(stderr, "no SV key\n");
187             // continue;
188 0 0         SV *keysv = HeSVKEY_force(h_entry);
    0          
    0          
189 0           keysv = newSVsv(keysv);
190             // SvREFCNT_inc(keysv);
191 0           sv_dump(keysv);
192 0           HeSVKEY_set(h_entry, keysv);
193 0 0         fprintf(stderr, "after set: %p\n", HeSVKEY(h_entry));
    0          
194             }
195             else {
196 0           fprintf(stderr, "entry has SV key\n");
197             }
198             }
199 0           sv_dump(hashref);
200              
201 0           RETVAL = newSVsv(hashref);
202              
203             OUTPUT:
204             RETVAL
205              
206             SV *
207             encode( SV * value, ... )
208             CODE:
209 16967           uint8_t encode_state_flags = 0;
210 16967           enum cbf_string_encode_mode string_encode_mode = CBF_STRING_ENCODE_SV;
211              
212             U8 i;
213             char* optname;
214             SV* opt_sv;
215              
216 17118 100         for (i=1; i
217 151 50         if (!(i % 2)) continue;
218              
219 151           opt_sv = ST(i);
220 151 50         if (!SvPOK(opt_sv)) continue;
221              
222 151           optname = SvPVX(opt_sv);
223              
224 151 100         if (strEQ(optname, STRING_ENCODE_MODE_OPT)) {
225 71           ++i;
226              
227 71 50         if (i
228 71           SV* opt = ST(i);
229              
230 71 50         if (SvOK(opt)) {
    0          
    0          
231 71 50         char* optstr = SvPV_nolen(opt);
232              
233             U8 i;
234 204 50         for (i=0; i
235 204 100         if (strEQ(optstr, cbf_string_encode_mode_options[i])) {
236 71           string_encode_mode = i;
237 71           break;
238             }
239             }
240              
241 71 50         if (i == CBF_STRING_ENCODE__LIMIT) {
242 71           croak("Invalid " STRING_ENCODE_MODE_OPT ": %s", optstr);
243             }
244             }
245              
246             }
247             }
248              
249 80 100         else if (strEQ(optname, CANONICAL_OPT)) {
250 72           ++i;
251 72 50         if (i
    50          
    50          
    50          
    0          
    0          
    100          
    50          
    50          
    50          
    100          
    50          
    50          
    100          
    50          
    0          
    100          
252 72           encode_state_flags |= ENCODE_FLAG_CANONICAL;
253             }
254             }
255              
256 8 100         else if (strEQ(optname, PRESERVE_REFS_OPT)) {
257 2           ++i;
258 2 50         if (i
    50          
    50          
    50          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    50          
    50          
    50          
    0          
    0          
    50          
259 2           encode_state_flags |= ENCODE_FLAG_PRESERVE_REFS;
260             }
261             }
262              
263 6 50         else if (strEQ(optname, SCALAR_REFS_OPT)) {
264 6           ++i;
265 6 50         if (i
    50          
    50          
    50          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    50          
    50          
    100          
    50          
    0          
    100          
266 6           encode_state_flags |= ENCODE_FLAG_SCALAR_REFS;
267             }
268             }
269              
270             else {
271 0           warn("Invalid option: %s", optname);
272             }
273             }
274              
275 16967           encode_ctx encode_state = cbf_encode_ctx_create(encode_state_flags, string_encode_mode);
276              
277 16967           RETVAL = newSV(0);
278              
279 16967           cbf_encode(aTHX_ value, &encode_state, RETVAL);
280              
281 16951           cbf_encode_ctx_free_reftracker( &encode_state );
282              
283             // Don’t use newSVpvn here because that will copy the string.
284             // Instead, create a new SV and manually assign its pieces.
285             // This follows the example from ext/POSIX/POSIX.xs:
286              
287 16951 50         SvUPGRADE(RETVAL, SVt_PV);
288 16951           SvPV_set(RETVAL, encode_state.buffer);
289 16951           SvPOK_on(RETVAL);
290 16951           SvCUR_set(RETVAL, encode_state.len - 1);
291 16951           SvLEN_set(RETVAL, encode_state.buflen);
292              
293             OUTPUT:
294             RETVAL
295              
296              
297             SV *
298             decode( SV *cbor )
299             CODE:
300 463           RETVAL = cbf_decode( aTHX_ cbor, NULL, false );
301              
302             OUTPUT:
303             RETVAL
304              
305             # ----------------------------------------------------------------------
306              
307             MODULE = CBOR::Free PACKAGE = CBOR::Free::Decoder
308              
309             PROTOTYPES: DISABLE
310              
311             SV*
312             new(SV *class)
313             CODE:
314 107           decode_ctx* decode_state = create_decode_state( aTHX_ NULL, NULL, CBF_FLAG_PERSIST_STATE);
315              
316 107           RETVAL = _bless_to_sv( aTHX_ class, (void*)decode_state);
317              
318             OUTPUT:
319             RETVAL
320              
321             SV*
322             decode(decode_ctx* decode_state, SV* cbor)
323             CODE:
324 127           decode_state->curbyte = 0;
325 127           renew_decode_state_buffer( aTHX_ decode_state, cbor );
326              
327 127 100         if (decode_state->flags & CBF_FLAG_PRESERVE_REFERENCES) {
328 2           reset_reflist_if_needed(aTHX_ decode_state);
329             }
330              
331 127           RETVAL = cbf_decode_document( aTHX_ decode_state );
332              
333             OUTPUT:
334             RETVAL
335              
336             bool
337             preserve_references(decode_ctx* decode_state, SV* new_setting = NULL)
338             CODE:
339 3           RETVAL = _handle_preserve_references( aTHX_ decode_state, new_setting );
340              
341             OUTPUT:
342             RETVAL
343              
344             bool
345             naive_utf8(decode_ctx* decode_state, SV* new_setting = NULL)
346             CODE:
347 2           RETVAL = _handle_flag_call( aTHX_ decode_state, new_setting, CBF_FLAG_NAIVE_UTF8 );
348              
349             OUTPUT:
350             RETVAL
351              
352             SV *
353             string_decode_cbor(SV* self)
354             CODE:
355 1           RETVAL = _set_string_decode( aTHX_ self, CBF_STRING_DECODE_CBOR );
356              
357             OUTPUT:
358             RETVAL
359              
360             SV *
361             string_decode_never(SV* self)
362             CODE:
363 1           RETVAL = _set_string_decode( aTHX_ self, CBF_STRING_DECODE_NEVER );
364              
365             OUTPUT:
366             RETVAL
367              
368             SV *
369             string_decode_always(SV* self)
370             CODE:
371 1           RETVAL = _set_string_decode( aTHX_ self, CBF_STRING_DECODE_ALWAYS );
372              
373             OUTPUT:
374             RETVAL
375              
376             void
377             _set_tag_handlers_backend(decode_ctx* decode_state, ...)
378             CODE:
379 103           _set_tag_handlers( aTHX_ decode_state, items, &ST(0) );
380              
381             void
382             DESTROY(decode_ctx* decode_state)
383             CODE:
384 107           free_decode_state( aTHX_ decode_state);
385              
386              
387             # ----------------------------------------------------------------------
388              
389             MODULE = CBOR::Free PACKAGE = CBOR::Free::SequenceDecoder
390              
391             PROTOTYPES: DISABLE
392              
393             SV *
394             new(SV *class)
395             CODE:
396              
397 9           SV* cbor = newSVpvs("");
398              
399 9           decode_ctx* decode_state = create_decode_state( aTHX_ cbor, NULL, CBF_FLAG_PERSIST_STATE);
400              
401             seqdecode_ctx* seqdecode;
402              
403 9           Newx( seqdecode, 1, seqdecode_ctx );
404              
405 9           seqdecode->decode_state = decode_state;
406 9           seqdecode->cbor = cbor;
407              
408 9           RETVAL = _bless_to_sv( aTHX_ class, (void*)seqdecode);
409              
410             OUTPUT:
411             RETVAL
412              
413             SV *
414             give(seqdecode_ctx* seqdecode, SV* addend)
415             CODE:
416 30           sv_catsv( seqdecode->cbor, addend );
417              
418 30           renew_decode_state_buffer( aTHX_ seqdecode->decode_state, seqdecode->cbor );
419              
420 30           RETVAL = _seqdecode_get( aTHX_ seqdecode);
421              
422             OUTPUT:
423             RETVAL
424              
425             SV *
426             get(seqdecode_ctx* seqdecode)
427             CODE:
428 3           RETVAL = _seqdecode_get( aTHX_ seqdecode);
429              
430             OUTPUT:
431             RETVAL
432              
433             bool
434             preserve_references(seqdecode_ctx* seqdecode, SV* new_setting = NULL)
435             CODE:
436 3           RETVAL = _handle_preserve_references( aTHX_ seqdecode->decode_state, new_setting );
437              
438             OUTPUT:
439             RETVAL
440              
441             bool
442             naive_utf8(seqdecode_ctx* seqdecode, SV* new_setting = NULL)
443             CODE:
444 1           RETVAL = _handle_flag_call( aTHX_ seqdecode->decode_state, new_setting, CBF_FLAG_NAIVE_UTF8 );
445              
446             OUTPUT:
447             RETVAL
448              
449              
450             SV *
451             string_decode_cbor(SV* self)
452             CODE:
453 1           RETVAL = _seq_set_string_decode( aTHX_ self, CBF_STRING_DECODE_CBOR );
454              
455             OUTPUT:
456             RETVAL
457              
458             SV *
459             string_decode_never(SV* self)
460             CODE:
461 1           RETVAL = _seq_set_string_decode( aTHX_ self, CBF_STRING_DECODE_NEVER );
462              
463             OUTPUT:
464             RETVAL
465              
466             SV *
467             string_decode_always(SV* self)
468             CODE:
469 1           RETVAL = _seq_set_string_decode( aTHX_ self, CBF_STRING_DECODE_ALWAYS );
470              
471             OUTPUT:
472             RETVAL
473              
474             void
475             _set_tag_handlers_backend(seqdecode_ctx* seqdecode, ...)
476             CODE:
477 0           _set_tag_handlers( aTHX_ seqdecode->decode_state, items, &ST(0) );
478              
479             void
480             DESTROY(seqdecode_ctx* seqdecode)
481             CODE:
482 9           free_decode_state( aTHX_ seqdecode->decode_state);
483 9           SvREFCNT_dec(seqdecode->cbor);
484              
485 9           Safefree(seqdecode);