File Coverage

SL.xs
Criterion Covered Total %
statement 572 638 89.6
branch 328 508 64.5
condition n/a
subroutine n/a
pod n/a
total 900 1146 78.5


line stmt bran cond sub pod time code
1             #include "perl-jsonsl.h"
2             #include "jsonxs_inline.h"
3              
4             /*
5             * JSON::SL, JSON::SL::Boolean
6             */
7             #define MY_CXT_KEY "JSON::SL::_guts" XS_VERSION
8              
9             typedef struct {
10             PLJSONSL* quick;
11             HV *stash_obj;
12             HV *stash_boolean;
13             HV *stash_tuba;
14             } my_cxt_t;
15             START_MY_CXT
16              
17             static int PLJSONSL_Escape_Table_dfl[0x80];
18             #define ESCTBL PLJSONSL_Escape_Table_dfl
19              
20             #define PLJSONSL_CROAK_USAGE(m) \
21             die("JSON::SL: %s %s", GvNAME(CvGV(cv)), m)
22              
23              
24             #ifdef PLJSONSL_HAVE_HV_COMMON
25             #define storeget_he(pjsn, hv, buf, len, value, is_utf8) \
26             hv_common((HV*)(hv), NULL, /*KEYSV*/ \
27             buf, len, \
28             ((is_utf8)?HVhek_UTF8:0), /*flags*/\
29             HV_FETCH_ISSTORE, /*action*/ \
30             value, \
31             0 /* HASH (tell perl to compute) */)
32              
33             #define delete_he(pjsn,hv,he) delete_he_THX(aTHX_ pjsn, hv, he)
34             static void
35 6           delete_he_THX(pTHX_ const PLJSONSL *dummy, HV* hv, HE* he)
36             {
37             char *kbuf;
38             STRLEN klen;
39             int is_utf8;
40              
41 6 50         kbuf = HePV(he, klen);
    0          
42 6 50         is_utf8 = HeUTF8(he);
43 6           hv_common(hv, NULL, /* KEYSV */
44             kbuf, klen,
45             (is_utf8 ? HVhek_UTF8 : 0), /*flags*/
46             HV_DELETE|G_DISCARD, /*action*/
47             NULL, /* value */
48             HeHASH(he) /*hash value - already computed */);
49 6           }
50              
51             #define PLJSONSL_INIT_KSV(blah)
52             #define PLJSONSL_DESTROY_KSV(blah)
53              
54              
55             #else
56             /* probably very dangerous, but the beginning of hv_store_common
57             * looks quite simple...
58             */
59              
60             #define CLOBBER_PV(sv,buf,len) \
61             SvCUR_set(sv, len); \
62             SvPVX(sv) = buf; \
63             SvUTF8_off(sv);
64              
65             #define UNCLOBBER_PV(sv) \
66             SvCUR_set(sv, 0); \
67             SvPVX(sv) = NULL; \
68              
69             #define PLJSONSL_INIT_KSV(pjsn) \
70             pjsn->ksv = newSV(16); \
71             pjsn->ksv_origpv = SvPVX(pjsn->ksv); \
72             SvLEN_set(pjsn->ksv, 0); \
73             SvPOK_on(pjsn->ksv);
74              
75             #define PLJSONSL_DESTROY_KSV(pjsn) \
76             CLOBBER_PV(pjsn->ksv, pjsn->ksv_origpv, 0); \
77             SvREFCNT_dec(pjsn->ksv);
78              
79             #define storeget_he(...) storeget_he_THX(aTHX_ __VA_ARGS__)
80             static HE*
81             storeget_he_THX(pTHX_ PLJSONSL *pjsn, HV *hv, const char *buf, size_t len,
82             SV *value, int is_utf8)
83             {
84             HE *ret;
85              
86             CLOBBER_PV(pjsn->ksv, buf, len);
87             if (is_utf8) {
88             SvUTF8_on(pjsn->ksv);
89             }
90             ret = hv_store_ent(hv, pjsn->ksv, value, 0);
91             UNCLOBBER_PV(pjsn->ksv);
92             return ret;
93             }
94              
95             #define delete_he(...) delete_he_THX(aTHX_ __VA_ARGS__)
96             static void
97             delete_he_THX(pTHX_ PLJSONSL *pjsn, HV *hv, HE* he)
98             {
99             char *kbuf;
100             STRLEN klen;
101             kbuf = HePV(he, klen);
102              
103             CLOBBER_PV(pjsn->ksv, buf, len);
104             if (HeUTF8(he)) {
105             SvUTF8_on(pjsn->ksv);
106             }
107             (void)hv_delete_ent(hv, pjsn->ksv, G_DISCARD, HeHASH(he));
108             UNCLOBBER_PV(pjsn->ksv);
109             }
110              
111             #endif /* HAVE_HV_COMMON */
112              
113              
114             #define REFDEC_FIELD(pjsn, fld) \
115             if (pjsn->fld != NULL) \
116             { \
117             SvREFCNT_dec(pjsn->fld); \
118             pjsn->fld = NULL; \
119             } \
120              
121              
122              
123             #define GET_STATE_BUFFER(pjsn, pos) \
124             (char*)(SvPVX(pjsn->buf) + (pos - pjsn->pos_min_valid))
125              
126             #define PLJSONSL_NEWSVUV_fast(sv, val) \
127             sv = newSV(0); \
128             sv_upgrade(sv, SVt_IV); \
129             SvIOK_only(sv); \
130             SvUVX(sv) = val;
131              
132             /**
133             * These 'common' functions are generic enough to work
134             * on all objects wiht a common pjsn head.
135             */
136              
137             #define pljsonsl_common_mknumeric(s,b,n) \
138             pljsonsl_common_mknumeric_THX(aTHX_ s,b,n)
139             static SV *
140 28           pljsonsl_common_mknumeric_THX(pTHX_
141             struct jsonsl_state_st *state,
142             const char *buf,
143             size_t nbuf)
144             {
145             #define die_numeric(err) \
146             die("JSON::SL - Malformed number (%s)", err);
147              
148             SV *newsv;
149 28           switch (state->special_flags) {
150             /* Simple signed/unsigned numbers, no exponents or fractions to worry about */
151             case JSONSL_SPECIALf_UNSIGNED:
152 15 100         if (nbuf == 1) {
153 12 50         PLJSONSL_NEWSVUV_fast(newsv, state->nelem);
154 12           break;
155             } /* else, ndigits > 1 */
156 3 50         if (*buf == '0') { die_numeric("leading zero for non-fraction"); }
157 3 50         if (nbuf < (UV_DIG-1)) {
158 3 50         PLJSONSL_NEWSVUV_fast(newsv, state->nelem);
159 3           break;
160             } /* else, potential overflow */
161 0           newsv = jsonxs_inline_process_number(buf);
162 0           break;
163              
164             case JSONSL_SPECIALf_SIGNED:
165 1           nbuf--;
166 1 50         if (nbuf == 0) { die_numeric("found lone '-'"); }
167 1 50         if (nbuf > 1 && buf[1] == '0') {
    50          
168 0           die_numeric("Leading 0 after '-'");
169             }
170 1 50         if (nbuf < (IV_DIG-1)) {
171 1           newsv = newSViv(-((IV)state->nelem));
172 1           break;
173             } /*else */
174 0           newsv = jsonxs_inline_process_number(buf);
175 0           break;
176              
177             default:
178 12 50         if (state->special_flags & JSONSL_SPECIALf_NUMNOINT) {
179 12           newsv = jsonxs_inline_process_number(buf);
180             }
181 12           break;
182             }
183 28           return newsv;
184             #undef die_numeric
185             }
186              
187             #define pljsonsl_common_mkboolean(pjsn_head, value) \
188             pljsonsl_common_mkboolean_THX(aTHX_ pjsn_head, value)
189              
190             static SV *
191 4           pljsonsl_common_mkboolean_THX(pTHX_
192             PLJSONSL *pjsn_head,
193             jsonsl_special_t specialf)
194             {
195             SV *retsv, *ivsv;
196 4           ivsv = newSViv(specialf == JSONSL_SPECIALf_TRUE);
197 4           retsv = newRV_noinc(ivsv);
198 4           sv_bless(retsv, pjsn_head->stash_boolean);
199 4           return retsv;
200             }
201              
202             #define pljsonsl_common_initialize(mycxt, pjsn_head, max_levels) \
203             pljsonsl_common_initialize_THX(aTHX_ mycxt, pjsn_head, max_levels)
204              
205             static void
206 33           pljsonsl_common_initialize_THX(pTHX_
207             my_cxt_t *mycxt,
208             PLJSONSL *pjsn_head,
209             size_t max_levels)
210             {
211 33           pjsn_head->jsn = jsonsl_new(max_levels+2);
212 33           pjsn_head->jsn->data = pjsn_head;
213 33           pjsn_head->stash_boolean = mycxt->stash_boolean;
214             PLJSONSL_mkTHX(pjsn_head);
215 33           memcpy(pjsn_head->escape_table, ESCTBL, sizeof(ESCTBL));
216 33           }
217              
218              
219             #define process_special(pjsn,st) process_special_THX(aTHX_ pjsn,st)
220             static inline void
221 30           process_special_THX(pTHX_
222             PLJSONSL *pjsn,
223             struct jsonsl_state_st *state)
224             {
225             SV *newsv;
226 30           char *buf = GET_STATE_BUFFER(pjsn, state->pos_begin);
227 30           size_t poscur = pjsn->jsn->pos;
228              
229 30           switch (state->special_flags) {
230             /* might look redundant, but is most common, so it's first */
231             case JSONSL_SPECIALf_UNSIGNED:
232             case JSONSL_SPECIALf_SIGNED:
233 16           newsv = pljsonsl_common_mknumeric(state,
234             buf,
235             poscur - state->pos_begin);
236 16           break;
237              
238             case JSONSL_SPECIALf_TRUE:
239             case JSONSL_SPECIALf_FALSE:
240 2           newsv = pljsonsl_common_mkboolean(pjsn, state->special_flags);
241 2           break;
242             case JSONSL_SPECIALf_NULL:
243 2           newsv = newSV(0);
244 2           break;
245             default:
246 10           newsv = pljsonsl_common_mknumeric(state,
247             buf,
248             poscur - state->pos_begin);
249 10           break;
250             }
251              
252 30 50         if (newsv == NULL) {
253 0           warn("Buffer is %p", buf);
254 0           warn("Length is %lu", poscur - state->pos_begin);
255 0           warn("Special flag is %d", state->special_flags);
256 0           die("WTF!");
257             }
258              
259 30           state->sv = newsv;
260 30           return;
261             }
262              
263             /**
264             * This is called to clean up any quotes, and possibly
265             * handle \u-escapes in the future
266             */
267             #define process_string(pjsn,st) process_string_THX(aTHX_ pjsn,st)
268             static void
269 25           process_string_THX(pTHX_
270             PLJSONSL* pjsn,
271             struct jsonsl_state_st *state)
272             {
273             SV *retsv;
274 25           char *buf = GET_STATE_BUFFER(pjsn, state->pos_begin);
275             size_t buflen;
276 25           size_t poscur = pjsn->jsn->pos;
277 25           buf++;
278 25           buflen = (poscur - state->pos_begin) - 1;
279 25           retsv = newSV(buflen);
280              
281 25           sv_upgrade(retsv, SVt_PV);
282 25           SvPOK_on(retsv);
283              
284 25 100         if (state->nescapes == 0) {
285 16           SvCUR_set(retsv, buflen);
286 16           memcpy(SvPVX(retsv), buf, buflen);
287             } else {
288             jsonsl_error_t err;
289             jsonsl_special_t flags;
290             size_t newlen;
291 9           newlen = jsonsl_util_unescape_ex(buf,
292             SvPVX(retsv),
293             buflen,
294 9           pjsn->escape_table,
295             &flags,
296             &err, NULL);
297 9 50         if (!newlen) {
298 0           SvREFCNT_dec(retsv);
299 0           die("Could not unescape string: %s", jsonsl_strerror(err));
300             }
301             /* Shrink the buffer to the effective new size */
302 9           SvCUR_set(retsv, newlen);
303 9 50         if (flags & JSONSL_SPECIALf_NONASCII) {
304 9           SvUTF8_on(retsv);
305             }
306             }
307              
308 25           state->sv = retsv;
309 25 100         if (pjsn->options.utf8) {
310 1           SvUTF8_on(state->sv);
311             }
312 25           }
313              
314              
315             /**
316             * This function will try and determine if the current
317             * item is a matched result (which should be returned to
318             * the user).
319             * If this is a complete match, the SV (along with relevant info)
320             * will be pushed to the result stack and return true. Returns
321             * false otherwise.
322             */
323             #define object_mkresult(pjsn,st_p,st_c) object_mkresult_THX(aTHX_ pjsn, st_p,st_c)
324             static inline int
325 1160           object_mkresult_THX(pTHX_
326             PLJSONSL *pjsn,
327             struct jsonsl_state_st *parent,
328             struct jsonsl_state_st *child)
329             {
330             #define STORE_INFO(b, v) \
331             (void)hv_stores(info_hv, PLJSONSL_INFO_KEY_##b, v)
332             HV *info_hv;
333              
334 1160 100         if (pjsn->options.object_drip == 0 &&
    100          
335 8 50         (child->matchres != JSONSL_MATCH_COMPLETE || child->type == JSONSL_T_HKEY)) {
336 1147           return 0;
337             }
338              
339 13           info_hv = newHV();
340 13 100         if (SvTYPE(child->sv) == SVt_PVHV || SvTYPE(child->sv) == SVt_PVAV) {
    100          
341 6           STORE_INFO(VALUE, newRV_noinc(child->sv));
342             } else {
343 7           STORE_INFO(VALUE, child->sv);
344             }
345              
346 13 100         if (pjsn->options.noqstr == 0 && pjsn->options.object_drip == 0) {
    100          
347 7           STORE_INFO(QUERY, newSVpvn_share(child->matchjpr->orig,
348             child->matchjpr->norig, 0));
349             }
350              
351 13 100         if (pjsn->options.nopath == 0) {
352             SV *pathstr;
353             int ii;
354 12           pathstr = newSVpvs("/");
355 33 100         for (ii = 2; ii <= (child->level); ii++) {
356 21           struct jsonsl_state_st *cur = pjsn->jsn->stack + ii;
357 21           struct jsonsl_state_st *prev = jsonsl_last_state(pjsn->jsn, cur);
358 21 100         if (prev->type == JSONSL_T_LIST) {
359 13           sv_catpvf(pathstr, "%d/", cur->u_loc.idx);
360             } else {
361             char *kbuf;
362             STRLEN klen;
363             assert(cur->u_loc.key);
364 8 50         kbuf = HePV(cur->u_loc.key, klen);
    0          
365 8           sv_catpvn(pathstr, kbuf, klen);
366 8           sv_catpvs(pathstr, "/");
367 8 50         if (HeKUTF8(cur->u_loc.key)) {
368 8           SvUTF8_on(pathstr);
369             }
370             }
371             }
372             /* Trim the trailing '/' from the path string */
373 12 50         if (SvCUR(pathstr) != 1) {
374 12           SvCUR_set(pathstr, SvCUR(pathstr)-1);
375             }
376 12           STORE_INFO(PATH, pathstr);
377             }
378              
379             /**
380             * For the sake of allowing inspection of the object tree, array
381             * and hash types are always added to their parents, even if they
382             * are a complete match to be removed from the stack.
383             */
384 13 50         if (parent && parent->sv) {
    50          
385 13           SvREADONLY_off(parent->sv);
386 13           SvREFCNT_inc_simple_void_NN(child->sv);
387 13 100         if (parent->type == JSONSL_T_LIST) {
388 7           SV *popped_sv = av_pop((AV*)parent->sv);
389 7 50         if (popped_sv) {
390 7           SvREFCNT_dec(popped_sv);
391             }
392             } else {
393 6           HE* child_he = child->u_loc.key;
394 6           SvREADONLY_off(HeVAL(child_he));
395 6           delete_he(pjsn, (HV*)parent->sv, child_he);
396             #if 0
397             /* for perls with hv_common, the above should be a macro for this: */
398             hv_common((HV*)parent->sv,
399             NULL, kbuf, klen,
400             0, /*maybe HVhek_UTF8?*/
401             HV_DELETE|G_DISCARD ,
402             NULL,
403             HeHASH(child->u_loc.key));
404             #endif
405 6           child->u_loc.key = NULL;
406             }
407              
408 13           SvREADONLY_on(parent->sv);
409 13           SvREADONLY_off(child->sv);
410             }
411              
412 13           av_push(pjsn->results, newRV_noinc((SV*)info_hv));
413 13           return 1;
414             #undef STORE_INFO
415             }
416              
417              
418             /**
419             * Because we only want to maintain 'complete' elements, for
420             * strings we ensure that their SVs do not get created until
421             * the entire string is done (as a partial string would
422             * not be of much use to the user anyway).
423             * The opposite is true of hashes and arrays, which we create
424             * immediately.
425             */
426 3219           static void body_push_callback(jsonsl_t jsn,
427             jsonsl_action_t action,
428             register struct jsonsl_state_st *state,
429             const char *at)
430             {
431             struct jsonsl_state_st *parent;
432             SV *newsv;
433             char *mkey;
434             size_t mnkey;
435 3219           register PLJSONSL *pjsn = (PLJSONSL*)jsn->data;
436             PLJSONSL_dTHX(pjsn);
437              
438             /* Reset the position first */
439              
440 3219           pjsn->keep_pos = state->pos_begin;
441              
442 3219           parent = jsonsl_last_state(jsn, state);
443             /* Here we set up parent positioning variables.. */
444 3219 100         if (parent->type == JSONSL_T_OBJECT) {
445 2137 100         if (state->type == JSONSL_T_HKEY) {
446 1071           return;
447             }
448             assert(pjsn->curhk);
449 1066           mkey = HeKEY(pjsn->curhk);
450 1066           mnkey = HeKLEN(pjsn->curhk);
451             /**
452             * Set the HE of our current value to the current HK, and then
453             * remove curhk's visibility.
454             */
455 1066           state->u_loc.key = pjsn->curhk;
456 1066           pjsn->curhk = NULL;
457             } else {
458 1082           state->u_loc.idx = parent->nelem - 1;
459 1082           mkey = NULL;
460 1082           mnkey = state->u_loc.idx;
461             }
462              
463 2148 100         if (parent->matchres == JSONSL_MATCH_POSSIBLE) {
464 12           state->matchjpr = jsonsl_jpr_match_state(jsn, state, mkey, mnkey,
465 12           &state->matchres);
466             } else {
467 2136           state->matchjpr = NULL;
468 2136           state->matchres = JSONSL_MATCH_NOMATCH;
469             }
470              
471             /**
472             * Ignore warnings about uninitialized newsv variable.
473             */
474 2148 100         if (!JSONSL_STATE_IS_CONTAINER(state)) {
    100          
475 61           return; /* nothing more to do here. String types are added at POP */
476             }
477              
478 2087 100         if (state->type == JSONSL_T_OBJECT) {
479 1030           newsv = (SV*)newHV();
480 1057 50         } else if (state->type == JSONSL_T_LIST) {
481 1057           newsv = (SV*)newAV();
482             } else {
483 0           die("WTF");
484             }
485              
486 2087           SvREADONLY_on(newsv);
487 2087 100         if (parent->type == JSONSL_T_LIST) {
488 1059           SvREADONLY_off(parent->sv);
489 1059           av_push((AV*)parent->sv, newRV_noinc(newsv));
490 1059           SvREADONLY_on(parent->sv);
491             } else {
492             /* we have the HE. */
493 1028           HeVAL(state->u_loc.key) = newRV_noinc(newsv);
494 1028           SvREADONLY_on(HeVAL(state->u_loc.key));
495             }
496              
497 2087           state->sv = newsv;
498             }
499              
500             /**
501             * Creates a new HE*. We use this HE later on using HeVAL to assign the value.
502             */
503              
504             #define create_hk(pjsn,st_c,st_p) create_hk_THX(aTHX_ pjsn,st_c,st_p)
505             static void
506 1071           create_hk_THX(pTHX_ PLJSONSL *pjsn,
507             struct jsonsl_state_st *state,
508             struct jsonsl_state_st *parent)
509             {
510 1071           char *buf = GET_STATE_BUFFER(pjsn, state->pos_begin);
511 1071           size_t poscur = pjsn->jsn->pos;
512 1071           STRLEN len = (poscur - state->pos_begin)-1;
513              
514             assert(pjsn->curhk == NULL);
515 1071           buf++;
516              
517 1071           SvREADONLY_off(parent->sv);
518              
519 1071 100         if (state->nescapes) {
520             /* we have escapes within a key. rare, but allowable. No choice
521             * but to allocate a new buffer for it
522             */
523              
524             /* This sets state->sv to the key sv. would be nice if there was a cleaner
525             * path to this
526             */
527 2           process_string(pjsn, state);
528 2           pjsn->curhk = hv_store_ent((HV*)parent->sv, state->sv, &PL_sv_undef, 0);
529 2           SvREFCNT_dec(state->sv);
530 2           state->sv = NULL;
531             } else {
532              
533             /**
534             * Fast path, no copying to new SV.
535             * We need to store &PL_sv_undef first to fool hv_common
536             * into thinking we're not doing anything special. Then
537             * we switch it out to &PL_sv_placeholder so it doesn't appear
538             * visible.
539             */
540 1069 100         pjsn->curhk = storeget_he(pjsn, parent->sv, buf, len, &PL_sv_undef,
    50          
541             /*determine if UTF8: */
542             pjsn->options.utf8 || state->special_flags == JSONSL_SPECIALf_NONASCII);
543             #if 0
544             /* which is really this: */
545             pjsn->curhk = hv_common((HV*)parent->sv, /* HV*/
546             NULL, /* keysv */
547             buf, len,
548             0, /* flags. Maybe setting utf8 */
549             HV_FETCH_ISSTORE, /*action*/
550             &PL_sv_undef, /*value*/
551             0);
552             #endif
553             }
554              
555 1071           HeVAL(pjsn->curhk) = &PL_sv_placeholder;
556 1071           SvREADONLY_on(parent->sv);
557 1071           }
558              
559             /* forward-declare initial state handler */
560             static void initial_callback(jsonsl_t jsn,
561             jsonsl_action_t action,
562             struct jsonsl_state_st *state,
563             const char *at);
564              
565             /**
566             * In this callback we ensure to clean up our strings and push it
567             * into the parent SV
568             */
569 2231           static void body_pop_callback(jsonsl_t jsn,
570             jsonsl_action_t action,
571             register struct jsonsl_state_st *state,
572             const char *at)
573             {
574             /* Ending of an element */
575 2231           struct jsonsl_state_st *parent = jsonsl_last_state(jsn, state);
576 2231           register PLJSONSL *pjsn = (PLJSONSL*)jsn->data;
577             PLJSONSL_dTHX(pjsn);
578              
579             #define INSERT_STRING \
580             if (parent && object_mkresult(pjsn, parent, state) == 0) { \
581             SvREADONLY_off(parent->sv); \
582             if (parent->type == JSONSL_T_OBJECT) { \
583             assert(state->u_loc.key); \
584             HeVAL(state->u_loc.key) = state->sv; \
585             } else { \
586             av_push((AV*)parent->sv, state->sv); \
587             } \
588             SvREADONLY_on(parent->sv); \
589             } \
590              
591 2231 100         if (state->type == JSONSL_T_STRING) {
592 23           process_string(pjsn, state);
593 23 50         INSERT_STRING;
    100          
    100          
594 2208 100         } else if (state->type == JSONSL_T_HKEY) {
595             assert(parent->type == JSONSL_T_OBJECT);
596 1071           create_hk(pjsn, state, parent);
597 1137 100         } else if (state->type == JSONSL_T_SPECIAL) {
598             assert(state->special_flags);
599 30           process_special(pjsn, state);
600 30 50         INSERT_STRING;
    100          
    100          
601             } else {
602 1107           SvREADONLY_off(state->sv);
603 1107           object_mkresult(pjsn, parent, state);
604             }
605              
606             #undef INSERT_STRING
607              
608 2231 100         if (state->sv == pjsn->root) {
609 43 100         if (pjsn->njprs == 0 && pjsn->options.object_drip == 0) {
    50          
610 38           av_push(pjsn->results, newRV_noinc(pjsn->root));
611             } /* otherwise, already pushed */
612 43           pjsn->root = NULL;
613 43           jsn->action_callback_PUSH = initial_callback;
614             }
615              
616 2231           state->u_loc.idx = -1;
617 2231           state->sv = NULL;
618 2231           pjsn->keep_pos = 0;
619              
620 2231           }
621              
622 14           static int error_callback(jsonsl_t jsn,
623             jsonsl_error_t err,
624             struct jsonsl_state_st *state,
625             char *at)
626             {
627 14           PLJSONSL *pjsn = (PLJSONSL*)jsn->data;
628             PLJSONSL_dTHX(pjsn);
629             /**
630             * TODO: allow option for user-defined recovery function
631             */
632              
633 14           die("JSON::SL - Got error %s at position %lu", jsonsl_strerror(err), jsn->pos);
634             return 0;
635             }
636              
637 4           static void invoke_root_cb(PLJSONSL *pjsn)
638             {
639             PLJSONSL_dTHX(pjsn);
640 4 50         if (!pjsn->options.root_callback) {
641 0           return;
642             }
643 4           dSP;
644 4           ENTER;
645 4           SAVETMPS;
646 4 50         PUSHMARK(SP);
647 4 50         XPUSHs(sv_2mortal(newRV_inc(pjsn->root)));
648 4           PUTBACK;
649 4           call_sv(pjsn->options.root_callback, G_DISCARD);
650 4 50         FREETMPS;
651 4           LEAVE;
652             }
653              
654 60           static void initial_callback(jsonsl_t jsn,
655             jsonsl_action_t action,
656             struct jsonsl_state_st *state,
657             const char *at)
658             {
659 60           PLJSONSL *pjsn = (PLJSONSL*)jsn->data;
660             PLJSONSL_dTHX(pjsn);
661              
662             assert(action == JSONSL_ACTION_PUSH);
663 60 100         if (state->type == JSONSL_T_LIST) {
664 26           pjsn->root = (SV*)newAV();
665 34 50         } else if (state->type == JSONSL_T_OBJECT) {
666 34           pjsn->root = (SV*)newHV();
667             } else {
668 0           die("Found type %s as root element", jsonsl_strtype(state->type));
669             }
670              
671 60 100         if (pjsn->options.root_callback) {
672 4           invoke_root_cb(pjsn);
673             }
674              
675 60           state->sv = pjsn->root;
676 60           jsn->action_callback = NULL;
677 60           jsn->action_callback_PUSH = body_push_callback;
678 60           jsn->action_callback_POP = body_pop_callback;
679 60           jsonsl_jpr_match_state(jsn, state, NULL, 0, &state->matchres);
680             /* Mark root element as read only */
681 60           SvREADONLY_on(pjsn->root);
682 60           }
683              
684             #define CHECK_MAX_SIZE(pjsn,input) \
685             if (pjsn->options.max_size && SvCUR(input) > pjsn->options.max_size) { \
686             die("JSON::SL - max_size is %lu, but input is %lu bytes", \
687             pjsn->options.max_size, SvCUR(input)); \
688             }
689              
690             #define pljsonsl_feed_incr(pjsn,str) pljsonsl_feed_incr_THX(aTHX_ pjsn,str)
691             static void
692 24           pljsonsl_feed_incr_THX(pTHX_ PLJSONSL* pjsn, SV *input)
693             {
694 24           size_t start_pos = pjsn->jsn->pos;
695 24           STRLEN cur_len = SvCUR(pjsn->buf);
696              
697 24 50         if (!SvPOK(input)) {
698 0           die("Input is not a string");
699             }
700              
701 24 100         CHECK_MAX_SIZE(pjsn, input)
    100          
702              
703 23           pjsn->pos_min_valid = pjsn->jsn->pos - cur_len;
704 23 100         if (SvUTF8(input)) {
705 1           pjsn->options.utf8 = 1;
706             }
707 23           sv_catpvn(pjsn->buf, SvPVX_const(input), SvCUR(input));
708 23           jsonsl_feed(pjsn->jsn,
709 23           SvPVX_const(pjsn->buf) + (SvCUR(pjsn->buf)-SvCUR(input)),
710 23           SvCUR(input));
711             /**
712             * Callbacks may detect the beginning of a string, in which case
713             * we need to ensure the continuity of the string. In this case
714             * pos_keep is set to the position of the input stream (not the SV *input,
715             * but rather jsn->pos) from which we should begin buffering data.
716             *
717             * Now we might need to chop. The amount of bytes to chop is the
718             * difference between start_pos and the keep_pos
719             * variable (if any)
720             */
721 11 50         if (pjsn->keep_pos == 0) {
722 11           SvCUR_set(pjsn->buf, 0);
723 0 0         } else if (pjsn->keep_pos > start_pos) {
724 0           sv_chop(pjsn->buf, SvPVX_const(pjsn->buf) + (pjsn->keep_pos - start_pos));
725             }
726 11           }
727              
728             static PLJSONSL*
729 32           pljsonsl_get_and_initialize_global(pTHX)
730             {
731             dMY_CXT;
732             PLJSONSL *pjsn;
733 32 100         if (MY_CXT.quick == NULL) {
734 7           Newxz(pjsn, 1, PLJSONSL);
735 7           pljsonsl_common_initialize(&MY_CXT, pjsn, PLJSONSL_MAX_DEFAULT-1);
736 7           pjsn->priv_global.is_global = 1;
737             PLJSONSL_INIT_KSV(pjsn);
738 7           MY_CXT.quick = pjsn;
739             }
740              
741 32           pjsn = MY_CXT.quick;
742 32           jsonsl_reset(pjsn->jsn);
743 32           jsonsl_enable_all_callbacks(pjsn->jsn);
744 32           pjsn->jsn->error_callback = error_callback;
745 32           pjsn->jsn->action_callback_PUSH = initial_callback;
746 32           pjsn->results = (AV*)sv_2mortal((SV*)newAV());
747 32           return pjsn;
748             }
749              
750             #define pljsonsl_feed_oneshot(pjsn,str) pljsonsl_feed_oneshot_THX(aTHX_ pjsn,str)
751             static void
752 32           pljsonsl_feed_oneshot_THX(pTHX_ PLJSONSL* pjsn, SV *input)
753             {
754 32 50         if (!SvPOK(input)) {
755 0           die("Input is not a string");
756             }
757              
758 32 50         if (SvUTF8(input)) {
759 0           pjsn->options.utf8 = 1;
760             }
761 32 50         CHECK_MAX_SIZE(pjsn, input);
    0          
762 32           pjsn->buf = input;
763 32           jsonsl_feed(pjsn->jsn, SvPVX_const(input), SvCUR(input));
764 30           pjsn->buf = NULL;
765 30           pjsn->options.utf8 = 0;
766             /* the current root is never in the result stack..
767             * so mortalizing it won't hurt anyone.
768             */
769 30           }
770              
771             /**
772             * Takes an array ref (or list?) of JSONPointer strings and converts
773             * them to JPR objects. Dies on error
774             */
775             #define pljsonsl_set_jsonpointer(pjsn,jprstr) \
776             pljsonsl_set_jsonpointer_THX(aTHX_ pjsn,jprstr)
777             static void
778 4           pljsonsl_set_jsonpointer_THX(pTHX_ PLJSONSL *pjsn, AV *paths)
779             {
780             jsonsl_jpr_t *jprs;
781             jsonsl_error_t err;
782             int ii;
783 4           int max = av_len(paths)+1;
784             const char *diestr, *pathstr;
785              
786 4 50         if (!max) {
787 0           die("No paths given!");
788             }
789              
790 4 50         Newxz(jprs, max, jsonsl_jpr_t);
791              
792 8 100         for (ii = 0; ii < max; ii++) {
793 4           SV **tmpsv = av_fetch(paths, ii, 0);
794 4 50         if (tmpsv == NULL || SvPOK(*tmpsv) == 0) {
    50          
795 0           diestr = "Found empty path";
796 0           goto GT_ERR;
797             }
798 4           jprs[ii] = jsonsl_jpr_new(SvPVX_const(*tmpsv), &err);
799 4 50         if (jprs[ii] == NULL) {
800 0           pathstr = SvPVX_const(*tmpsv);
801 0           goto GT_ERR;
802             }
803             }
804              
805 4           jsonsl_jpr_match_state_init(pjsn->jsn, jprs, max);
806 4           pjsn->jprs = jprs;
807 4           pjsn->njprs = max;
808 4           return;
809              
810             GT_ERR:
811 0 0         for (ii = 0; ii < max; ii++) {
812 0 0         if (jprs[ii] == NULL) {
813 0           break;
814             }
815 0           jsonsl_jpr_destroy(jprs[ii]);
816             }
817 0           Safefree(jprs);
818 0 0         if (pathstr) {
819 0           die("Couldn't convert %s to jsonpointer: %s", pathstr, jsonsl_strerror(err));
820             } else {
821 4           die(diestr);
822             }
823             }
824              
825              
826              
827              
828              
829             /**
830             * JSON::SL::Tuba functions.
831             * In case you haven't wondered already, 'Tuba' is a play on 'SAX'.
832             */
833              
834              
835             /**
836             * This is our quick version of MRO caching. Maybe I'll swap this out
837             * for something which already exists (as I get the feeling I've reinveted
838             * the wheel here.
839             * namep is populated with the handler name, gvp is a pointer to the GV**
840             * - or an offset into the PLTUBA's methgv structure.
841             */
842             static void
843 57           pltuba_get_method_info(PLTUBA *tuba,
844             jsonsl_action_t action,
845             pltuba_callback_type cbtype,
846             GV ***gvpp,
847             const char **namep)
848             {
849 57           GV **methgvp = NULL;
850 57           const char *methname = NULL;
851 57           cbtype &= 0x7f;
852              
853 57 50         if (tuba->options.cb_unified) {
854 0           methname = "on_any";
855 0           methgvp = &tuba->methgv.on_any;
856 0           goto GT_RETASGN;
857             }
858             #define PLTUBA_METH_GETMETH
859             #include "srcout/tuba_dispatch_getmeth.h"
860             #undef PLTUBA_METH_GETMETH
861             GT_RETASGN:
862 57 50         if (gvpp) {
863 57           *gvpp = methgvp;
864             }
865 57 50         if (namep) {
866 57           *namep = methname;
867             }
868 57           }
869              
870             static void
871 4           pltuba_invalidate_gvs_THX(pTHX_ PLTUBA *tuba)
872             {
873              
874             #define X(action,type) \
875             REFDEC_FIELD(tuba, methgv.action## _ ##type)
876              
877 4 50         PLTUBA_XMETHGV
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    100          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
878             #undef X
879 4           }
880              
881             /**
882             * Maps a 'jsonsl' type to a tuba callback type.
883             */
884             static pltuba_callback_type
885 58           convert_to_tuba_cbt(struct jsonsl_state_st *state)
886             {
887 58 100         if (state->type != JSONSL_T_SPECIAL) {
888 46           return state->type;
889             }
890 12 100         if (state->special_flags & JSONSL_SPECIALf_BOOLEAN) {
891 4           return PLTUBA_CALLBACK_BOOLEAN;
892 8 100         } else if (state->special_flags & (JSONSL_SPECIALf_NUMERIC | JSONSL_SPECIALf_DASH)) {
893 6           return PLTUBA_CALLBACK_NUMBER;
894 2 50         } else if (state->special_flags == JSONSL_SPECIALf_NULL) {
895 2           return PLTUBA_CALLBACK_NULL;
896             }
897 0           warn("Special flag is %d", state->special_flags);
898 0           die("wtf?");
899             return 0;
900             }
901              
902             /**
903             * This function invokes the selected callback (if it exists).
904             */
905             #define pltuba_invoke_callback(tb,a,cbt,sv) \
906             pltuba_invoke_callback_THX(aTHX_ tb,a,cbt,sv)
907             static void
908 26           pltuba_invoke_callback_THX(pTHX_ PLTUBA *tuba,
909             int action,
910             pltuba_callback_type cbtype,
911             SV *mextrasv)
912             {
913 26           dSP;
914 26           GV **methp = NULL;
915 26           GV *meth = NULL;
916 26           const char *meth_name = NULL;
917 26           int effective_type = cbtype;
918 26           int effective_action = action;
919 26           int stop_mro = 0;
920             /**
921             * If we are in a pop mode of a callback with the accumulator flag set,
922             * then we provide the data in the SV as the argument (maybe with some
923             * conversion into an appropriate object), otherwise, we just signal as
924             * normal.
925             */
926 26           cbtype &= 0x7f;
927              
928 26 100         if (tuba->accum && action == JSONSL_ACTION_POP) {
    50          
929 14           effective_action = PLTUBA_ACTION_ON;
930 14           pltuba_get_method_info(tuba, PLTUBA_ACTION_ON, cbtype, &methp, &meth_name);
931             assert(mextrasv == NULL);
932 14           mextrasv = tuba->accum;
933 14           tuba->accum = NULL;
934             } else {
935 12           pltuba_get_method_info(tuba, action, cbtype, &methp, &meth_name);
936             }
937              
938 26 50         if (meth_name == NULL) {
939 0           die("Can't find method name. Action=%c, Type=%c", action, cbtype);
940             }
941              
942 26 100         if (!mextrasv) {
943 12           mextrasv = &PL_sv_undef;
944             } else {
945 14           sv_2mortal(mextrasv);
946             }
947              
948             assert(methp);
949              
950 26 100         if (tuba->last_stash != SvSTASH(SvRV(tuba->selfrv))) {
951 2           pltuba_invalidate_gvs_THX(aTHX_ tuba);
952 2           tuba->last_stash = SvSTASH(SvRV(tuba->selfrv));
953             }
954              
955             do {
956 52 100         if (*methp == NULL) {
957 32           meth = gv_fetchmethod_autoload(SvSTASH(SvRV(tuba->selfrv)), meth_name, 1);
958 32 100         if (meth && GvCV(meth)) {
    50          
959 1 50         if (tuba->options.no_cache_mro == 0) {
960 1           *methp = meth;
961 1           SvREFCNT_inc(meth);
962             }
963 1           break;
964             } /* else */
965 31           pltuba_get_method_info(tuba, PLTUBA_ACTION_ON,
966             PLTUBA_CALLBACK_ANY, &methp, &meth_name);
967             assert(methp && meth_name);
968 31           stop_mro++;
969             } else {
970 20           meth = *methp;
971 20           break;
972             }
973 31 100         } while (stop_mro < 2);
974              
975 26           PLTUBA_SET_PARAMFIELDS_dv(tuba, Mode, effective_action);
976 26           PLTUBA_SET_PARAMFIELDS_dv(tuba, Type, effective_type);
977              
978             /**
979             * We still want a SAVETMPS/FREETMPS pair active before we decide
980             * to call a function or not, as the contents mextrasv and possibly
981             * some of the hash values are mortalized.
982             */
983 26           ENTER; SAVETMPS;
984 26 100         if (meth && GvCV(meth)) {
    50          
985 21 50         PUSHMARK(SP);
986 21 50         EXTEND(SP, 2);
987 21           PUSHs(tuba->selfrv);
988 21           PUSHs(tuba->paramhvrv);
989 21 100         if (mextrasv != &PL_sv_undef) {
990 12 50         XPUSHs(mextrasv);
991             }
992 21           PUTBACK;
993 21           call_sv((SV*)GvCV(meth), G_DISCARD);
994             } else {
995 5 50         if (!tuba->options.allow_unhandled) {
996 0           die("Tuba: Cannot find handler for mode 0x%02x action 0x%02x",
997             effective_action, effective_type);
998             }
999             }
1000 26 50         FREETMPS; LEAVE;
1001 26           }
1002              
1003             /**
1004             * Flush characters between the invocation of the last callback
1005             * and the current one. the until argument is the end position (inclusive)
1006             * at which we should stop submitting 'character' data.
1007             */
1008             #define pltuba_flush_characters(tb,end) \
1009             pltuba_flush_characters_THX(aTHX_ tb,end)
1010             static void
1011 25           pltuba_flush_characters_THX(pTHX_ PLTUBA *tuba, size_t until)
1012             {
1013             STRLEN toFlush;
1014             const char *buf;
1015             SV *chunksv;
1016              
1017 25 50         if (!tuba->keep_pos) {
1018 0           return;
1019             }
1020              
1021 25           toFlush = (until - tuba->keep_pos);
1022 25 50         if (toFlush == 0) {
1023 0           return;
1024             }
1025 25           buf = GET_STATE_BUFFER(tuba, tuba->keep_pos);
1026              
1027 25 100         if (tuba->shift_quote) {
1028 19           buf++;
1029 19           toFlush--;
1030             }
1031              
1032 25           tuba->keep_pos = 0;
1033 25           tuba->shift_quote = 0;
1034              
1035 25 50         if (toFlush == 0 && tuba->shift_quote == 0) {
    0          
1036             /* if we have no data and the count was not artificially decremented, then
1037             * don't invoke the callback
1038             */
1039 0           return;
1040             }
1041              
1042             /* if accumulator mode is on, don't send the data right away.
1043             * buffer it instead */
1044 25 50         if (tuba->accum) {
1045 25           sv_catpvn(tuba->accum, buf, toFlush);
1046 25           return;
1047             } /* else, no accum for this state */
1048              
1049 0           chunksv = newSVpvn(buf, toFlush);
1050 0           pltuba_invoke_callback(tuba,
1051             PLTUBA_ACTION_ON,
1052             PLTUBA_CALLBACK_DATA,
1053             chunksv);
1054             /**
1055             * SV has been mortalized by the invoke_callback function
1056             */
1057             }
1058             /**
1059             * Push callback. This is easy because we never actually do any character
1060             * data here.
1061             */
1062             static void
1063 29           pltuba_jsonsl_push_callback(jsonsl_t jsn,
1064             jsonsl_action_t action,
1065             struct jsonsl_state_st *state,
1066             const char *at)
1067             {
1068 29           PLTUBA *tuba = (PLTUBA*)jsn->data;
1069             PLJSONSL_dTHX(tuba);
1070 29           struct jsonsl_state_st *parent = jsonsl_last_state(jsn, state);
1071 29           pltuba_callback_type cbt = convert_to_tuba_cbt(state);
1072 29 100         if (state->level == 1) {
1073 2           pltuba_invoke_callback(tuba, action, PLTUBA_CALLBACK_JSON, NULL);
1074             } else {
1075             assert(parent);
1076 27 100         if (parent->type == JSONSL_T_LIST) {
1077 5           PLTUBA_SET_PARAMFIELDS_iv(tuba, Index, parent->nelem-1);
1078             } else {
1079 22           PLTUBA_RESET_PARAMFIELD(tuba, Index);
1080             }
1081             }
1082              
1083 29 100         if (tuba->accum_options[cbt & 0x7f]) {
1084             assert(tuba->accum == NULL);
1085             /* accum is only ever valid for atomic types */
1086 25           tuba->accum = newSVpvn("", 0);
1087             } else {
1088 4 100         if (JSONSL_STATE_IS_CONTAINER(state) && tuba->kaccum) {
    50          
    100          
1089 2           sv_2mortal(tuba->kaccum);
1090 2           tuba->kaccum = NULL;
1091 2           pltuba_invoke_callback(tuba, action, cbt, NULL);
1092 2           PLTUBA_RESET_PARAMFIELD(tuba, Key);
1093             } else {
1094 2           pltuba_invoke_callback(tuba, action, cbt, NULL);
1095             }
1096             }
1097              
1098             /* This is a different branch and must get executed regardless
1099             * of whether we invoke a callback or use the accumulator */
1100 29 100         if (!JSONSL_STATE_IS_CONTAINER(state)) {
    100          
1101 25           tuba->keep_pos = state->pos_begin;
1102 44 100         if (state->type & JSONSL_Tf_STRINGY) {
1103 19           tuba->shift_quote = 1;
1104             }
1105             } else {
1106 4           tuba->keep_pos = 0;
1107             }
1108 29           }
1109              
1110             /**
1111             * If we're special, then convert all weird stuff to their
1112             * proper perly form. Simple plain integers are not weird and
1113             * can be stringified on demand.
1114             * This is akin to JSON::SL's process_string and process_special
1115             * functions.
1116             */
1117             #define pltuba_process_accum(tuba, state) \
1118             pltuba_process_accum_THX(aTHX_ tuba, state)
1119             static void
1120 25           pltuba_process_accum_THX(pTHX_
1121             PLTUBA *tuba,
1122             struct jsonsl_state_st *state)
1123             {
1124 25           size_t poscur = tuba->jsn->pos;
1125 25 100         if (state->type == JSONSL_T_SPECIAL) {
1126             SV *newsv;
1127              
1128 6 100         if ( (state->special_flags & JSONSL_SPECIALf_NUMERIC) &&
    100          
1129 3           (state->special_flags & JSONSL_SPECIALf_NUMNOINT) == 0) {
1130             goto GT_NONEWSV;
1131              
1132 5 100         } else if (state->special_flags & JSONSL_SPECIALf_NUMNOINT) {
1133 2           newsv = pljsonsl_common_mknumeric(state,
1134             SvPVX_const(tuba->accum),
1135             poscur - state->pos_begin);
1136 3 100         } else if (state->special_flags & JSONSL_SPECIALf_BOOLEAN) {
1137 2           newsv = pljsonsl_common_mkboolean((PLJSONSL*)tuba,
1138             state->special_flags);
1139             } else {
1140 1           newsv = &PL_sv_undef;
1141             }
1142 5           SvREFCNT_dec(tuba->accum);
1143 6           tuba->accum = newsv;
1144             GT_NONEWSV:
1145             ;
1146             } else {
1147 19 50         if (tuba->options.utf8) {
1148 0           SvUTF8_on(tuba->accum);
1149             }
1150 19 100         if (state->nescapes) {
1151             jsonsl_error_t err;
1152             jsonsl_special_t flags;
1153             size_t newlen;
1154 2           newlen = jsonsl_util_unescape_ex(SvPVX_const(tuba->accum),
1155 2           SvPVX(tuba->accum),
1156 2           SvCUR(tuba->accum),
1157 2           tuba->escape_table,
1158             &flags,
1159             &err,
1160             NULL);
1161 2 50         if (newlen == 0) {
1162 0           die("Could not unescape string: %s", jsonsl_strerror(err));
1163             }
1164 2           SvCUR_set(tuba->accum, newlen);
1165 2 50         if (flags & JSONSL_SPECIALf_NONASCII) {
1166 2           SvUTF8_on(tuba->accum);
1167             }
1168             }
1169             }
1170 25           }
1171              
1172             static void
1173 29           pltuba_jsonsl_pop_callback(jsonsl_t jsn,
1174             jsonsl_action_t action,
1175             struct jsonsl_state_st *state,
1176             const char *at)
1177             {
1178 29           PLTUBA *tuba = (PLTUBA*)jsn->data;
1179             PLJSONSL_dTHX(tuba);
1180 29           pltuba_callback_type cbt = convert_to_tuba_cbt(state);
1181 29           size_t poscur = jsn->pos;
1182              
1183 29 100         if (!JSONSL_STATE_IS_CONTAINER(state)) {
    100          
1184             /* Special handling for character crap.. */
1185 25           pltuba_flush_characters(tuba, poscur);
1186              
1187              
1188 25 50         if (tuba->accum) {
1189 25           pltuba_process_accum(tuba, state);
1190             } else {
1191 0 0         if (state->nescapes) {
1192 0           PLTUBA_SET_PARAMFIELDS_sv(tuba, Escaped, &PL_sv_yes);
1193             }
1194             }
1195              
1196 25 100         if (state->type == JSONSL_T_HKEY &&
    50          
1197 11           tuba->options.accum_kv) {
1198             /**
1199             * If we are accumulating the key then don't flush characters under
1200             * any circumstances. Just swap over the accumulator buffer
1201             */
1202             assert(tuba->accum);
1203 11           tuba->kaccum = tuba->accum;
1204 11           tuba->accum = NULL;
1205 11           tuba->keep_pos = 0;
1206 11           PLTUBA_SET_PARAMFIELDS_sv(tuba, Key, tuba->kaccum);
1207 11           return;
1208             }
1209             }
1210              
1211 18 100         if (tuba->kaccum && state->type != JSONSL_T_HKEY) {
    50          
1212 9           sv_2mortal(tuba->kaccum);
1213 9           tuba->kaccum = NULL;
1214             }
1215              
1216 18           pltuba_invoke_callback(tuba, action, cbt, NULL);
1217              
1218             /**
1219             * Clear all fields
1220             */
1221             #define X(kname) \
1222             PLTUBA_RESET_PARAMFIELD(tuba, kname);
1223 18           PLTUBA_XPARAMS;
1224             #undef X
1225              
1226 18 100         if (state->level == 1) {
1227 2           pltuba_invoke_callback(tuba, action, PLTUBA_CALLBACK_JSON, NULL);
1228             }
1229 18           tuba->keep_pos = 0;
1230             }
1231              
1232             static int
1233 0           pltuba_jsonsl_error_callback(jsonsl_t jsn,
1234             jsonsl_error_t error,
1235             struct jsonsl_state_st *state,
1236             char *at)
1237             {
1238             /**
1239             * This needs special handling, as we will be receiving a return
1240             * value from Perl for this..
1241             */
1242 0           die ("Got error: %s", jsonsl_strerror(error));
1243             return 0;
1244             }
1245              
1246             #define pltuba_feed(tb,str) pltuba_feed_THX(aTHX_ tb,str)
1247             static void
1248 2           pltuba_feed_THX(pTHX_ PLTUBA *tuba, SV *input)
1249             {
1250 2 50         if (!SvPOK(input)) {
1251 0           die("Input is not string!");
1252             }
1253 2           tuba->buf = input;
1254 2           tuba->pos_min_valid = tuba->jsn->pos;
1255              
1256 2           SvREADONLY_on(input);
1257 2           jsonsl_feed(tuba->jsn, SvPVX_const(input), SvCUR(input));
1258 2 50         if (tuba->keep_pos) {
1259 0           pltuba_flush_characters(tuba, tuba->jsn->pos);
1260 0           tuba->keep_pos = tuba->jsn->pos;
1261             }
1262 2           SvREADONLY_off(input);
1263 2           }
1264              
1265             static SV *
1266 2           pltuba_initialize_THX(pTHX_ const char *pkg)
1267             {
1268             SV *ptriv, *retrv;
1269             HV *hvret;
1270             HV *subclass;
1271             dMY_CXT;
1272              
1273             /* Initialize our internal C data structures */
1274             PLTUBA *tuba;
1275 2           Newxz(tuba, 1, PLTUBA);
1276 2           pljsonsl_common_initialize(&MY_CXT, (PLJSONSL*)tuba, PLJSONSL_MAX_DEFAULT);
1277              
1278 2           tuba->jsn->action_callback_PUSH = pltuba_jsonsl_push_callback;
1279 2           tuba->jsn->action_callback_POP = pltuba_jsonsl_pop_callback;
1280 2           tuba->jsn->error_callback = pltuba_jsonsl_error_callback;
1281 2           jsonsl_enable_all_callbacks(tuba->jsn);
1282              
1283 2           ptriv = newSViv(PTR2IV(tuba));
1284 2           SvREADONLY_on(ptriv);
1285              
1286             /* The Perl object .. */
1287 2           hvret = newHV();
1288 2           (void)hv_stores(hvret, PLTUBA_HKEY_NAME, ptriv);
1289 2           tuba->selfrv = newRV_inc((SV*)hvret);
1290 2           sv_rvweaken(tuba->selfrv);
1291 2           retrv = newRV_noinc((SV*)hvret);
1292              
1293 2           subclass = gv_stashpv(pkg, GV_ADD);
1294 2           sv_bless(retrv, subclass);
1295              
1296 2           tuba->paramhv = newHV();
1297 2           tuba->paramhvrv = newRV_noinc((SV*)tuba->paramhv);
1298             {
1299 2           SV *ksv = newSV(0);
1300             HE *tmphe;
1301              
1302             #define X(kname) \
1303             sv_setpvs(ksv, #kname); \
1304             tmphe = hv_store_ent(tuba->paramhv, ksv, &PL_sv_undef, 0); \
1305             HeVAL(tmphe) = &PL_sv_placeholder; \
1306             assert(tmphe); \
1307             tuba->p_ents.pe_##kname.he = tmphe;
1308              
1309 2           PLTUBA_XPARAMS;
1310             #undef X
1311             }
1312              
1313             #define initialize_param_iv(b) \
1314             PLTUBA_PARAM_FIELD(tuba, b).sv = newSViv(0); \
1315             SvREADONLY_on(PLTUBA_PARAM_FIELD(tuba,b).sv);
1316             #define initialize_param_dualvar(b) \
1317             PLTUBA_PARAM_FIELD(tuba, b).sv = newSViv(0); \
1318             sv_setpv(PLTUBA_PARAM_FIELD(tuba, b).sv, " "); \
1319             SvIOK_on(PLTUBA_PARAM_FIELD(tuba,b).sv); \
1320             SvREADONLY_on(PLTUBA_PARAM_FIELD(tuba,b).sv);
1321              
1322 2           initialize_param_iv(Index);
1323 2           initialize_param_dualvar(Mode);
1324 2           initialize_param_dualvar(Type);
1325              
1326             #undef initialize_param_iv
1327             #undef initialize_param_dualvar
1328              
1329 2           SvREADONLY_on(tuba->paramhv);
1330 2           return retrv;
1331             }
1332              
1333             /**
1334             * Initialize our thread-local context
1335             */
1336             #define POPULATE_CXT \
1337             MY_CXT.stash_obj = gv_stashpv(PLJSONSL_CLASS_NAME, GV_ADD); \
1338             MY_CXT.stash_boolean = gv_stashpv(PLJSONSL_BOOLEAN_NAME, GV_ADD); \
1339             MY_CXT.stash_tuba = gv_stashpv(PLTUBA_CLASS_NAME, GV_ADD); \
1340             MY_CXT.quick = NULL;
1341              
1342              
1343             /**
1344             * These two macros arrange for the contents of the result stack to be returned
1345             * to perlspace.
1346             */
1347             #define dRESULT_VARS \
1348             int result_count; \
1349             int result_iter; \
1350             SV *result_sv;
1351              
1352             #define RETURN_RESULTS(pjsn) \
1353             switch(GIMME_V) { \
1354             case G_VOID: \
1355             result_count = 0; \
1356             break; \
1357             case G_SCALAR: \
1358             result_sv = av_shift(pjsn->results); \
1359             if (result_sv == &PL_sv_undef) { \
1360             result_count = 0; \
1361             break; \
1362             } \
1363             XPUSHs(sv_2mortal(result_sv)); \
1364             result_count = 1; \
1365             break; \
1366             case G_ARRAY: \
1367             result_count = av_len(pjsn->results) + 1; \
1368             if (result_count == 0) { \
1369             break; \
1370             } \
1371             EXTEND(SP, result_count); \
1372             for (result_iter = 0; result_iter < result_count; result_iter++) { \
1373             result_sv = av_delete(pjsn->results, result_iter, 0); \
1374             /*already mortal according to av_delete*/ \
1375             PUSHs(result_sv); \
1376             } \
1377             av_clear(pjsn->results); \
1378             break; \
1379             default: \
1380             die("eh? (RETURN_RESULTS)"); \
1381             result_count = 0; \
1382             break; \
1383             }
1384              
1385              
1386              
1387              
1388             MODULE = JSON::SL PACKAGE = JSON::SL PREFIX = PLJSONSL_
1389              
1390             PROTOTYPES: DISABLED
1391              
1392             BOOT:
1393             {
1394             MY_CXT_INIT;
1395 19           POPULATE_CXT;
1396 19           PLJSONSL_ESCTBL_INIT(ESCTBL);
1397             }
1398              
1399             SV *
1400             PLJSONSL_new(SV *pkg, ...)
1401             PREINIT:
1402             PLJSONSL *pjsn;
1403             SV *ptriv, *retrv;
1404             int levels;
1405             dMY_CXT;
1406             CODE:
1407             (void)pkg;
1408 24 100         if (items > 1) {
1409 3 50         if (!SvIOK(ST(1))) {
1410 0           die("Second argument (if provided) must be numeric");
1411             }
1412 3 50         levels = SvIV(ST(1));
1413 3 50         if (levels < 2) {
1414 0           die ("Levels must be at least 2");
1415             }
1416             } else {
1417 21           levels = PLJSONSL_MAX_DEFAULT;
1418             }
1419              
1420 24           Newxz(pjsn, 1, PLJSONSL);
1421 24           pljsonsl_common_initialize(&MY_CXT, pjsn, levels);
1422 24           ptriv = newSViv(PTR2IV(pjsn));
1423 24           retrv = newRV_noinc(ptriv);
1424 24           sv_bless(retrv, MY_CXT.stash_obj);
1425 24           pjsn->buf = newSVpvn("", 0);
1426              
1427 24           jsonsl_enable_all_callbacks(pjsn->jsn);
1428 24           pjsn->jsn->action_callback = initial_callback;
1429 24           pjsn->jsn->error_callback = error_callback;
1430              
1431 24           pjsn->results = newAV();
1432             PLJSONSL_INIT_KSV(pjsn);
1433 24           RETVAL = retrv;
1434              
1435             OUTPUT: RETVAL
1436              
1437              
1438             void
1439             PLJSONSL_set_jsonpointer(PLJSONSL *pjsn, AV *paths)
1440             PPCODE:
1441 4           pljsonsl_set_jsonpointer(pjsn, paths);
1442              
1443             SV *
1444             PLJSONSL_root(PLJSONSL *pjsn)
1445             CODE:
1446 3 50         if (pjsn->root) {
1447 3           RETVAL = newRV_inc(pjsn->root);
1448             } else {
1449 0           RETVAL = &PL_sv_undef;
1450             }
1451             OUTPUT: RETVAL
1452              
1453             void
1454             PLJSONSL__modify_readonly(PLJSONSL *pjsn, SV *ref)
1455             ALIAS:
1456             make_referrent_writeable = 1
1457             make_referrent_readonly = 2
1458             CODE:
1459 3 50         if (!SvROK(ref)) {
1460 0           die("Variable is not a reference!");
1461             }
1462 3 50         if (ix == 0) {
1463 0           PLJSONSL_CROAK_USAGE("use make_referrent_writeable or make_referrent_readonly");
1464 3 100         } else if (ix == 1) {
1465 1           SvREADONLY_off(SvRV(ref));
1466 2 50         } else if (ix == 2) {
1467 2           SvREADONLY_on(SvRV(ref));
1468             }
1469              
1470             int
1471             PLJSONSL_referrent_is_writeable(PLJSONSL *pjsn, SV *ref)
1472             CODE:
1473 5 50         if (!SvROK(ref)) {
1474 0           die("Variable is not a reference!");
1475             }
1476 5           RETVAL = SvREADONLY(SvRV(ref)) == 0;
1477             OUTPUT: RETVAL
1478              
1479              
1480             void
1481             PLJSONSL_feed(PLJSONSL *pjsn, SV *input)
1482             ALIAS:
1483             incr_parse =1
1484              
1485             PPCODE:
1486             {
1487             dRESULT_VARS;
1488 24           pljsonsl_feed_incr(pjsn, input);
1489 20 50         RETURN_RESULTS(pjsn);
    50          
    50          
    50          
    50          
    50          
    100          
1490             }
1491              
1492             void
1493             PLJSONSL_fetch(PLJSONSL *pjsn)
1494             PPCODE:
1495             {
1496             dRESULT_VARS;
1497 5 50         RETURN_RESULTS(pjsn);
    50          
    50          
    50          
    50          
    50          
    100          
1498             }
1499              
1500             int
1501             PLJSONSL__escape_table_chr(PLJSONSL *pjsn, U8 chrc, ...)
1502             CODE:
1503 2 50         if (chrc > 0x7f) {
1504 0           warn("Attempted to set non-ASCII escape preference");
1505 0           RETVAL = -1;
1506             } else {
1507 2           RETVAL = pjsn->escape_table[chrc];
1508 2 50         if (items == 3) {
1509 2 50         pjsn->escape_table[chrc] = SvIV(ST(2));
1510             }
1511             }
1512             OUTPUT: RETVAL
1513              
1514             void
1515             PLJSONSL_reset(PLJSONSL *pjsn)
1516             CODE:
1517 2 50         REFDEC_FIELD(pjsn, root);
1518              
1519 2 50         if (pjsn->results) {
1520 2           av_clear(pjsn->results);
1521             }
1522 2 50         if (pjsn->buf) {
1523 2           SvCUR_set(pjsn->buf, 0);
1524             }
1525              
1526 2           jsonsl_reset(pjsn->jsn);
1527 2           pjsn->pos_min_valid = 0;
1528 2           pjsn->keep_pos = 0;
1529 2           pjsn->curhk = NULL;
1530 2           pjsn->jsn->action_callback_PUSH = initial_callback;
1531              
1532             SV*
1533             PLJSONSL_root_callback(PLJSONSL *pjsn, SV *callback)
1534             CODE:
1535 4           RETVAL = pjsn->options.root_callback;
1536 4 100         if (RETVAL) {
1537 1           SvREFCNT_inc(RETVAL);
1538             } else {
1539 3           RETVAL = &PL_sv_undef;
1540             }
1541              
1542 4 100         if (SvTYPE(callback) == SVt_NULL) {
1543 1 50         if (pjsn->options.root_callback) {
1544 1           SvREFCNT_dec(pjsn->options.root_callback);
1545 1           pjsn->options.root_callback = NULL;
1546             }
1547             } else {
1548 3 100         if (SvTYPE(callback) != SVt_RV ||
    50          
1549 2           SvTYPE(SvRV(callback)) != SVt_PVCV) {
1550 1           die("Second argument must be undef or a CODE ref");
1551             }
1552 2 50         if (pjsn->options.root_callback) {
1553 0           SvREFCNT_dec(pjsn->options.root_callback);
1554             }
1555 2           pjsn->options.root_callback = newRV_inc(SvRV(callback));
1556             }
1557              
1558             OUTPUT: RETVAL
1559              
1560             void
1561             PLJSONSL_DESTROY(PLJSONSL *pjsn)
1562             PREINIT:
1563             int ii;
1564              
1565             CODE:
1566 24 50         if (pjsn->priv_global.is_global == 0) {
1567 24 100         REFDEC_FIELD(pjsn, root);
1568 24 50         REFDEC_FIELD(pjsn, results);
1569 24 50         REFDEC_FIELD(pjsn, buf);
1570 24 100         REFDEC_FIELD(pjsn, options.root_callback);
1571             } /* else, it's a mortal and shouldn't be freed */
1572 24           jsonsl_jpr_match_state_cleanup(pjsn->jsn);
1573 24 100         if (pjsn->jprs) {
1574 8 100         for ( ii = 0; ii < pjsn->njprs; ii++) {
1575 4 50         if (pjsn->jprs[ii] == NULL) {
1576 0           break;
1577             }
1578 4           jsonsl_jpr_destroy(pjsn->jprs[ii]);
1579             }
1580 4           Safefree(pjsn->jprs);
1581 4           pjsn->jprs = NULL;
1582             }
1583 24 50         if (pjsn->jsn) {
1584 24           jsonsl_destroy(pjsn->jsn);
1585 24           pjsn->jsn = NULL;
1586             }
1587             PLJSONSL_DESTROY_KSV(pjsn);
1588 24           Safefree(pjsn);
1589              
1590             void
1591             PLJSONSL_decode_json(SV *input)
1592             PREINIT:
1593             PLJSONSL* pjsn;
1594             dRESULT_VARS;
1595              
1596             PPCODE:
1597 32           pjsn = pljsonsl_get_and_initialize_global(aTHX);
1598 32           pljsonsl_feed_oneshot(pjsn, input);
1599              
1600 30           pjsn->curhk = NULL;
1601 30           pjsn->keep_pos = 0;
1602 30           pjsn->pos_min_valid = 0;
1603 30           pjsn->jsn->action_callback_PUSH = initial_callback;
1604              
1605 30 100         RETURN_RESULTS(pjsn);
    50          
    50          
    0          
    0          
    0          
    0          
1606 30 100         if (result_count == 0 && av_len(pjsn->results) == -1) {
    50          
1607 1           die("Incomplete JSON string?");
1608             }
1609              
1610             SV *
1611             PLJSONSL_unescape_json_string(SV *input)
1612             PREINIT:
1613             size_t origlen, newlen;
1614 3           SV *retsv = NULL;
1615             char *errpos;
1616             jsonsl_error_t err;
1617             jsonsl_special_t flags;
1618              
1619             CODE:
1620 3 50         if (!SvPOK(input)) {
1621 0           die("Input is not a valid string");
1622             }
1623 3           origlen = SvCUR(input);
1624 3 50         if (origlen) {
1625 3           retsv = newSV(origlen);
1626 3           newlen = jsonsl_util_unescape_ex(SvPVX_const(input), SvPVX(retsv),
1627 3           SvCUR(input), ESCTBL, &flags,
1628             &err, (const char**)&errpos);
1629 3 100         if (newlen == 0) {
1630 1           SvREFCNT_dec(retsv);
1631 1           die("Could not unescape: %s at pos %lu ('%c'..)",
1632             jsonsl_strerror(err),
1633 1           errpos - SvPVX_const(input),
1634 1           *errpos
1635             );
1636             }
1637              
1638 2           SvCUR_set(retsv, newlen);
1639 2           SvPOK_only(retsv);
1640 2 50         if (SvUTF8(input) || (flags & JSONSL_SPECIALf_NONASCII)) {
    100          
1641 2           SvUTF8_on(retsv);
1642             }
1643             } else {
1644 0           retsv = &PL_sv_undef;
1645             }
1646 2           RETVAL = retsv;
1647             OUTPUT: RETVAL
1648              
1649              
1650              
1651             void
1652             PLJSONSL_CLONE(PLJSONSL *pjsn)
1653             CODE:
1654             {
1655             MY_CXT_CLONE;
1656 0           POPULATE_CXT;
1657             }
1658              
1659             MODULE = JSON::SL PACKAGE = JSON::SL::Tuba PREFIX = PLTUBA_
1660              
1661             SV *
1662             PLTUBA__initialize(const char *pkg)
1663             CODE:
1664 2           RETVAL = pltuba_initialize_THX(aTHX_ pkg);
1665             OUTPUT: RETVAL
1666              
1667             void
1668             PLTUBA_DESTROY(PLTUBA* tuba)
1669             CODE:
1670 2           jsonsl_destroy(tuba->jsn);
1671 2           tuba->jsn = NULL;
1672              
1673 2 50         REFDEC_FIELD(tuba, accum);
1674 2 50         REFDEC_FIELD(tuba, kaccum);
1675 2 50         REFDEC_FIELD(tuba, selfrv);
1676             #define X(kname) \
1677             PLTUBA_RESET_PARAMFIELD(tuba, kname); \
1678             REFDEC_FIELD(tuba, p_ents.pe_##kname.sv);
1679 2 50         PLTUBA_XPARAMS;
    50          
    50          
    50          
    50          
    50          
1680             #undef X
1681 2 50         REFDEC_FIELD(tuba, paramhvrv);
1682             /* Implicit that the hash has been decrementas as well.
1683             * Don't do another dec
1684             */
1685 2           tuba->paramhv = NULL;
1686 2           pltuba_invalidate_gvs_THX(aTHX_ tuba);
1687 2           Safefree(tuba);
1688              
1689             int
1690             PLTUBA__ax_opt(PLTUBA *tuba, int mode, ...)
1691             CODE:
1692 20           RETVAL = tuba->accum_options[mode & 0xff];
1693 20 100         if (items > 2) {
1694 15 50         tuba->accum_options[mode & 0xff] = SvIV(ST(2));
1695             }
1696             OUTPUT: RETVAL
1697              
1698             int
1699             PLTUBA_accum_kv(PLTUBA *tuba, ...)
1700             CODE:
1701 4 50         if (items > 2) {
1702 0           die("accum_kv(..boolean)");
1703             }
1704 4           RETVAL = tuba->options.accum_kv;
1705 4 100         if (items == 2) {
1706 3 50         int newval = SvIV(ST(1));
1707 3 50         if (newval) {
1708 3           tuba->accum_options['#'] = 1;
1709             }
1710 3           tuba->options.accum_kv = newval;
1711             }
1712             OUTPUT: RETVAL
1713              
1714              
1715             void
1716             PLTUBA__parse(PLTUBA* tuba, SV *input)
1717             CODE:
1718 2           pltuba_feed(tuba, input);
1719              
1720              
1721             INCLUDE: srcout/option_accessors.xs