File Coverage

c/jsonparse.c
Criterion Covered Total %
statement 217 218 99.5
branch 207 238 86.9
condition n/a
subroutine n/a
pod n/a
total 424 456 92.9


line stmt bran cond sub pod time code
1             typedef struct {
2             const unsigned char *buf;
3             const unsigned char *end;
4             UV depth;
5             } fujson_parse_ctx;
6              
7             static SV *fujson_parse(pTHX_ fujson_parse_ctx *);
8              
9 5019           static void fujson_parse_ws(pTHX_ fujson_parse_ctx *ctx) {
10             unsigned char x;
11 5117 100         while (ctx->buf < ctx->end) {
12 4462           x = *ctx->buf;
13 4462 100         if (!(x == 0x09 || x == 0x0a || x == 0x0d || x == 0x20)) break;
    100          
    100          
    100          
14 98           ctx->buf++;
15             }
16 5019           }
17              
18 36           static inline int fujson_parse_string_escape(pTHX_ fujson_parse_ctx *ctx, fustr *r) {
19             unsigned int n, s;
20 36           ctx->buf++; /* '\\' */
21 36 100         if (UNLIKELY(ctx->buf == ctx->end)) return 1;
22 35           switch (*(ctx->buf++)) {
23 2           case '"': *(r->cur++) = '\"'; break;
24 2           case '\\':*(r->cur++) = '\\'; break;
25 2           case '/': *(r->cur++) = '/'; break; /* We don't escape this one */
26 3           case 'b': *(r->cur++) = 0x08; break;
27 2           case 't': *(r->cur++) = 0x09; break;
28 2           case 'n': *(r->cur++) = 0x0a; break;
29 1           case 'f': *(r->cur++) = 0x0c; break;
30 2           case 'r': *(r->cur++) = 0x0d; break;
31 18           case 'u':
32             /* (awful code adapted from ncdu) */
33             #define h4(b) (fu_hexdig((b)[0])<<12) + (fu_hexdig((b)[1])<<8) + (fu_hexdig((b)[2])<<4) + fu_hexdig((b)[3])
34 18 100         if (ctx->end - ctx->buf < 4) return 1;
35 16           n = h4(ctx->buf);
36 16 100         if (n >= 0x10000 || (n & 0xfc00) == 0xdc00) return 1;
    100          
37 13           ctx->buf += 4;
38 13 100         if ((n & 0xfc00) == 0xd800) { /* high surrogate */
39 3 100         if (ctx->end - ctx->buf < 6) return 1;
40 2 50         if (ctx->buf[0] != '\\' || ctx->buf[1] != 'u') return 1;
    50          
41 2           s = h4(ctx->buf+2);
42 2 50         if (s >= 0x10000 || (s & 0xfc00) != 0xdc00) return 1;
    50          
43 2           n = 0x10000 + (((n & 0x03ff) << 10) | (s & 0x03ff));
44 2           ctx->buf += 6;
45             }
46 12           r->cur = (char *)uvchr_to_utf8((U8 *)r->cur, n);
47 12 100         if (n >= 0x80) r->setutf8 = 1;
48 12           break;
49             #undef h4
50 1           default:
51 1           return 1;
52             }
53 28           return 0;
54             }
55              
56 1154           static int fujson_parse_string_buf(pTHX_ fujson_parse_ctx *ctx, fustr *r) {
57             size_t len;
58             unsigned char x;
59 1154           ctx->buf++; /* '"' */
60             while (true) {
61 4744136           fustr_reserve(r, 4);
62 4744136 100         if (UNLIKELY(ctx->buf == ctx->end)) return 1;
63 4744135           x = *ctx->buf;
64 4744135 100         if (UNLIKELY(x == '"')) {
65 1143           ctx->buf++;
66 1143           return 0;
67 4742992 100         } else if (UNLIKELY(x == '\\')) {
68 36 100         if (fujson_parse_string_escape(aTHX_ ctx, r)) return 1;
69 4742956 100         } else if (x >= 0x80) {
70 17 100         if (UNLIKELY((len = isC9_STRICT_UTF8_CHAR(ctx->buf, ctx->end)) == 0)) return 1;
71 16           memcpy(r->cur, ctx->buf, len);
72 16           r->cur += len;
73 16           ctx->buf += len;
74 16           r->setutf8 = 1;
75 4742939 100         } else if (x >= 0x20) {
76 4742938           *(r->cur++) = x;
77 4742938           ctx->buf++;
78 1           } else return 1;
79             }
80             }
81              
82 633           static SV *fujson_parse_string(pTHX_ fujson_parse_ctx *ctx) {
83             fustr r;
84 633           fustr_init(&r, NULL, SIZE_MAX);
85 633 100         if (fujson_parse_string_buf(aTHX_ ctx, &r)) {
86 11 50         if (r.sv) SvREFCNT_dec(r.sv);
87 11           return NULL;
88             } else {
89 622           return fustr_done(&r);
90             }
91             }
92              
93             /* Validate JSON grammar of a number, increments ctx->buf to the end of the
94             * number and returns -1 on error, 0 if it's an int, 1 for floats. */
95 42           static int fujson_parse_number_grammar(fujson_parse_ctx *ctx) {
96 42           int ret = 0;
97 42 100         if (*ctx->buf == '-') ctx->buf++;
98 42 50         if (ctx->buf == ctx->end) return -1;
99 42 100         if (*ctx->buf == '0' && (ctx->buf+1 == ctx->end ||
    100          
100 9 100         !(ctx->buf[1] == '.' || ctx->buf[1] == 'e' || ctx->buf[1] == 'E'))) {
    50          
    50          
101             /* rfc8259 permits "-0", so we'll not check for that */
102 7           ctx->buf++;
103 7           return 0;
104             }
105             #define DIG1 \
106             if (ctx->buf == ctx->end || *ctx->buf < '0' || *ctx->buf > '9') return -1; \
107             ctx->buf++; \
108             while (ctx->buf != ctx->end && *ctx->buf >= '0' && *ctx->buf <= '9') ctx->buf++;
109              
110             /* int part */
111 129 50         DIG1;
    100          
    50          
    100          
    100          
    100          
112             /* decimal part */
113 34 100         if (ctx->buf != ctx->end && *ctx->buf == '.') {
    100          
114 10           ret = 1;
115 10           ctx->buf++;
116 89 100         DIG1;
    100          
    50          
    100          
    100          
    100          
117             }
118             /* exponent */
119 32 100         if (ctx->buf != ctx->end && (*ctx->buf == 'e' || *ctx->buf == 'E')) {
    100          
    100          
120 12           ret = 1;
121 12           ctx->buf++;
122 12 100         if (ctx->buf == ctx->end) return -1;
123 11 100         if (*ctx->buf == '+' || *ctx->buf == '-') ctx->buf++;
    100          
124 11 100         DIG1;
    50          
    100          
    100          
    50          
    0          
125             }
126              
127             #undef DIG1
128 28           return ret;
129             }
130              
131 42           static SV *fujson_parse_number(pTHX_ fujson_parse_ctx *ctx) {
132 42           const unsigned char *start = ctx->buf;
133 42           int isnum = fujson_parse_number_grammar(ctx);
134 42 100         if (isnum == -1) return NULL;
135              
136             UV uv;
137 35           const char *end = (const char *)ctx->buf;
138             /* grok_atoUV() in this context can only return false on overflow */
139 35 100         if (!isnum && grok_atoUV((const char *)(*start == '-' ? start+1 : start), &uv, &end)) {
    100          
    100          
140 22 100         if (*start != '-') return newSVuv(uv);
141 5 100         if (uv <= ((UV)IV_MAX)+1) return newSViv(-uv);
142             }
143              
144             /* floating point or overflowed integer, might lose precision */
145             NV val;
146 14           my_atof3((const char *)start, &val, ctx->buf - start); /* this function is not documented to be public... */
147 14           return newSVnv(val);
148             }
149              
150 520           static SV *fujson_parse_array(pTHX_ fujson_parse_ctx *ctx) {
151 520           AV *av = newAV();
152             SV *r;
153 520 100         if (--ctx->depth == 0) return NULL;
154 519           ctx->buf++; /* '[' */
155 519           fujson_parse_ws(aTHX_ ctx);
156 519 100         if (ctx->buf == ctx->end) goto err;
157 518 100         if (*ctx->buf == ']') goto done;
158             while (true) {
159 527 100         if (!(r = fujson_parse(aTHX_ ctx))) goto err;
160 521           av_push_simple(av, r);
161 521           fujson_parse_ws(aTHX_ ctx);
162 521 50         if (ctx->buf == ctx->end) goto err;
163 521 100         if (*ctx->buf == ']') goto done;
164 16 50         if (*ctx->buf != ',') goto err;
165 16           ctx->buf++;
166             }
167 512           done:
168 512           ctx->buf++; /* ']' */
169 512           ctx->depth++;
170 512           return newRV_noinc((SV *)av);
171 7           err:
172 7           SvREFCNT_dec((SV *)av);
173 7           return NULL;
174             }
175              
176 522           static SV *fujson_parse_obj(pTHX_ fujson_parse_ctx *ctx) {
177 522           HV *hv = newHV();
178             SV *val;
179             char *keystart;
180             UV keyhash;
181             fustr key;
182 522           fustr_init(&key, NULL, SIZE_MAX);
183              
184 522 100         if (--ctx->depth == 0) return NULL;
185 521           ctx->buf++; /* '{' */
186 521           fujson_parse_ws(aTHX_ ctx);
187 521 100         if (ctx->buf == ctx->end) goto err;
188 520 100         if (*ctx->buf == '}') goto done;
189             while (true) {
190             /* key */
191 524 100         if (*ctx->buf != '"') goto err;
192 521 50         if (fujson_parse_string_buf(aTHX_ ctx, &key)) goto err;
193 521 50         keystart = fustr_start(&key);
194 521 100         if (key.setutf8) keyhash = 0;
195 517 50         else PERL_HASH(keyhash, keystart, key.cur - keystart);
196 521 100         if (hv_common(hv, NULL, keystart, key.cur - keystart, key.setutf8, HV_FETCH_ISEXISTS, NULL, keyhash)) goto err;
197              
198             /* ':' */
199 519           fujson_parse_ws(aTHX_ ctx);
200 519 50         if (ctx->buf == ctx->end) goto err;
201 519 100         if (*ctx->buf != ':') goto err;
202 518           ctx->buf++;
203              
204             /* value */
205 518 100         if (!(val = fujson_parse(aTHX_ ctx))) goto err;
206 514           hv_common(hv, NULL, keystart, key.cur - keystart, key.setutf8, HV_FETCH_ISSTORE|HV_FETCH_JUST_SV, val, keyhash);
207 514           key.cur = keystart;
208 514           key.setutf8 = 0;
209              
210 514           fujson_parse_ws(aTHX_ ctx);
211 514 100         if (ctx->buf == ctx->end) goto err;
212 513 100         if (*ctx->buf == '}') goto done;
213 9 50         if (*ctx->buf != ',') goto err;
214 9           ctx->buf++;
215 9           fujson_parse_ws(aTHX_ ctx);
216             }
217 509           done:
218 509 50         if (key.sv) SvREFCNT_dec(key.sv);
219 509           ctx->buf++; /* '}' */
220 509           ctx->depth++;
221 509           return newRV_noinc((SV *)hv);
222 12           err:
223 12 50         if (key.sv) SvREFCNT_dec(key.sv);
224 12           SvREFCNT_dec((SV *)hv);
225 12           return NULL;
226             }
227              
228 1750           static SV *fujson_parse(pTHX_ fujson_parse_ctx *ctx) {
229 1750           fujson_parse_ws(aTHX_ ctx);
230 1750 100         if (ctx->buf == ctx->end) return NULL;
231 1749           switch (*ctx->buf) {
232 633           case '"': return fujson_parse_string(aTHX_ ctx);
233 522           case '{': return fujson_parse_obj(aTHX_ ctx);
234 520           case '[': return fujson_parse_array(aTHX_ ctx);
235 12           case 't':
236 12 100         if (ctx->end - ctx->buf < 4) return NULL;
237 10 50         if (memcmp(ctx->buf, "true", 4) != 0) return NULL;
238 10           ctx->buf += 4;
239 10           return newSV_true();
240 7           case 'f':
241 7 100         if (ctx->end - ctx->buf < 5) return NULL;
242 6 50         if (memcmp(ctx->buf, "false", 5) != 0) return NULL;
243 6           ctx->buf += 5;
244 6           return newSV_false();
245 8           case 'n':
246 8 100         if (ctx->end - ctx->buf < 4) return NULL;
247 7 50         if (memcmp(ctx->buf, "null", 4) != 0) return NULL;
248 7           ctx->buf += 4;
249 7           return newSV(0);
250 47           default:
251 47 100         if (*ctx->buf == '-' || (*ctx->buf >= '0' && *ctx->buf <= '9'))
    100          
    100          
252 42           return fujson_parse_number(aTHX_ ctx);
253             }
254 5           return NULL;
255             }
256              
257 708           static SV *fujson_parse_xs(pTHX_ I32 ax, I32 argc, SV *val) {
258 708           I32 i = 1;
259             char *arg;
260             SV *r;
261 708           SV *offset = NULL;
262 708           UV maxlen = 0;
263 708           int decutf8 = 0;
264             STRLEN buflen;
265             fujson_parse_ctx ctx;
266              
267 708           ctx.depth = 0;
268 734 100         while (i < argc) {
269 26           arg = SvPV_nolen(ST(i));
270 26           i++;
271 26 50         if (i == argc) croak("Odd name/value argument for json_parse()");
272 26           r = ST(i);
273 26           i++;
274              
275 26 100         if (strcmp(arg, "utf8") == 0) decutf8 = SvTRUEx(r);
276 19 100         else if (strcmp(arg, "max_size") == 0) maxlen = SvUV(r);
277 15 100         else if (strcmp(arg, "max_depth") == 0) ctx.depth = SvUV(r);
278 13 100         else if (strcmp(arg, "allow_control") == 0) {}
279 12 50         else if (strcmp(arg, "offset") == 0) offset = r;
280 0           else croak("Unknown flag: '%s'", arg);
281             }
282 708 100         if (maxlen == 0) maxlen = 1<<30;
283 708 100         if (ctx.depth == 0) ctx.depth = 512;
284              
285 708 100         arg = decutf8 ? SvPVbyte(val, buflen) : SvPVutf8(val, buflen);
286 708           ctx.buf = (const unsigned char *)arg;
287 708           ctx.end = ctx.buf + buflen;
288              
289 708 100         if (offset) {
290 12 50         if (!SvROK(offset)) croak("Offset must be a reference to a scalar");
291 12           offset = SvRV(offset);
292 12 100         if (!looks_like_number(offset) || SvIV(offset) < 0) croak("Offset must be a positive integer");
    50          
293 11 100         if (SvUV(offset) >= buflen) croak("Offset too large");
294 10           ctx.buf += SvUV(offset);
295 10 100         if ((UV)(ctx.end - ctx.buf) > maxlen) ctx.end = ctx.buf + maxlen;
296              
297 696 100         } else if ((UV)(ctx.end - ctx.buf) > maxlen)
298 1           croak("Input string is larger than max_size");
299              
300 705           r = fujson_parse(aTHX_ &ctx);
301 705 100         if (!r) croak("JSON parsing failed at offset %"UVuf, (UV)((char *)ctx.buf - arg));
302              
303 666           fujson_parse_ws(aTHX_ &ctx);
304 666 100         if (offset) {
305 9 100         if (ctx.buf == ctx.end) sv_set_undef(offset);
306 8           else SvUV_set(offset, (UV)((char *)ctx.buf - arg));
307 657 100         } else if (ctx.buf != ctx.end) {
308 7           SvREFCNT_dec(r);
309 7           croak("garbage after JSON value at offset %"UVuf, (UV)((char *)ctx.buf - arg));
310             }
311              
312 659           return sv_2mortal(r);
313             }