Line data Source code
1 : #include "fd_jtok.h"
2 :
3 : #include <errno.h>
4 : #include <math.h>
5 : #include <stdlib.h>
6 :
7 : /* Every public entry point checks j->err first, so err is always zero
8 : when fail is reached. cur is left at the offending byte so that
9 : fd_jtok_err_off is meaningful. */
10 :
11 : static inline int
12 : fail( fd_jtok_t * j,
13 384 : int err ) {
14 384 : j->err = err;
15 384 : return 0;
16 384 : }
17 :
18 : static inline int
19 2378319 : is_ws( char c ) {
20 2378319 : return c==' ' || c=='\t' || c=='\n' || c=='\r';
21 2378319 : }
22 :
23 : static inline int
24 818082 : is_digit( char c ) {
25 818082 : return c>='0' && c<='9';
26 818082 : }
27 :
28 : static inline void
29 573138 : skip_ws( fd_jtok_t * j ) {
30 2382864 : while( j->cur<j->end && is_ws( *j->cur ) ) j->cur++;
31 573138 : }
32 :
33 : /* utf8_len returns the byte length of the well formed UTF-8 encoded
34 : code point at p (>=0x80), or 0 if the bytes are not well formed
35 : (truncated, overlong, surrogate, or above U+10FFFF). */
36 :
37 : static ulong
38 : utf8_len( uchar const * p,
39 51 : uchar const * end ) {
40 51 : ulong rem = (ulong)( end-p );
41 51 : uchar c = p[0];
42 51 : ulong n;
43 51 : uchar lo = 0x80, hi = 0xBF;
44 51 : if( c>=0xC2 && c<=0xDF ) n = 2UL;
45 45 : else if( c>=0xE0 && c<=0xEF ) { n = 3UL; if( c==0xE0 ) lo = 0xA0; else if( c==0xED ) hi = 0x9F; }
46 27 : else if( c>=0xF0 && c<=0xF4 ) { n = 4UL; if( c==0xF0 ) lo = 0x90; else if( c==0xF4 ) hi = 0x8F; }
47 12 : else return 0UL;
48 39 : if( FD_UNLIKELY( rem<n ) ) return 0UL;
49 39 : if( FD_UNLIKELY( p[1]<lo || p[1]>hi ) ) return 0UL;
50 51 : for( ulong i=2UL; i<n; i++ ) if( FD_UNLIKELY( (p[i]&0xC0)!=0x80 ) ) return 0UL;
51 21 : return n;
52 27 : }
53 :
54 : /* hex4 parses 4 hex digits at p (caller guarantees 4 bytes) into *cp.
55 : Returns 1 on success, 0 if a digit is invalid. */
56 :
57 : static int
58 : hex4( char const * p,
59 132 : uint * cp ) {
60 132 : uint v = 0U;
61 648 : for( ulong i=0UL; i<4UL; i++ ) {
62 522 : char c = p[i];
63 522 : uint d;
64 522 : if( c>='0' && c<='9' ) d = (uint)( c-'0' );
65 180 : else if( c>='a' && c<='f' ) d = (uint)( c-'a' ) + 10U;
66 30 : else if( c>='A' && c<='F' ) d = (uint)( c-'A' ) + 10U;
67 6 : else return 0;
68 516 : v = (v<<4) | d;
69 516 : }
70 126 : *cp = v;
71 126 : return 1;
72 132 : }
73 :
74 : /* lex_string validates the string starting at the opening quote at
75 : j->cur and advances past the closing quote. Stores the raw contents
76 : in *out. Escape syntax, surrogate pairing, UTF-8 well formedness
77 : and absence of control characters are all enforced here so that
78 : decoding can assume a valid view. */
79 :
80 : static int
81 : lex_string( fd_jtok_t * j,
82 134577 : fd_jtok_str_t * out ) {
83 134577 : char const * p = j->cur+1;
84 134577 : char const * start = p;
85 16004679 : for(;;) {
86 16004679 : if( FD_UNLIKELY( p>=j->end ) ) { j->cur = p; return fail( j, FD_JTOK_ERR_SYNTAX ); }
87 16004667 : uchar c = (uchar)*p;
88 16004667 : if( c=='"' ) {
89 134481 : out->ptr = start;
90 134481 : out->sz = (ulong)( p-start );
91 134481 : j->cur = p+1;
92 134481 : return 1;
93 134481 : }
94 15870186 : if( c=='\\' ) {
95 153 : p++;
96 153 : if( FD_UNLIKELY( p>=j->end ) ) { j->cur = p; return fail( j, FD_JTOK_ERR_SYNTAX ); }
97 153 : switch( *p ) {
98 75 : case '"': case '\\': case '/': case 'b': case 'f': case 'n': case 'r': case 't':
99 75 : p++;
100 75 : break;
101 72 : case 'u': {
102 72 : uint cp;
103 72 : p++;
104 72 : if( FD_UNLIKELY( j->end-p<4L || !hex4( p, &cp ) ) ) { j->cur = p; return fail( j, FD_JTOK_ERR_SYNTAX ); }
105 60 : p += 4;
106 60 : if( cp>=0xD800U && cp<=0xDBFFU ) {
107 30 : uint lo;
108 30 : if( FD_UNLIKELY( j->end-p<6L || p[0]!='\\' || p[1]!='u' || !hex4( p+2, &lo ) || lo<0xDC00U || lo>0xDFFFU ) ) {
109 18 : j->cur = p; return fail( j, FD_JTOK_ERR_SYNTAX );
110 18 : }
111 12 : p += 6;
112 30 : } else if( FD_UNLIKELY( cp>=0xDC00U && cp<=0xDFFFU ) ) {
113 6 : j->cur = p-4; return fail( j, FD_JTOK_ERR_SYNTAX );
114 6 : }
115 36 : break;
116 60 : }
117 36 : default:
118 6 : j->cur = p; return fail( j, FD_JTOK_ERR_SYNTAX );
119 153 : }
120 111 : continue;
121 153 : }
122 15870033 : if( FD_UNLIKELY( c<0x20 ) ) { j->cur = p; return fail( j, FD_JTOK_ERR_SYNTAX ); }
123 15870021 : if( c<0x80 ) { p++; continue; }
124 51 : ulong n = utf8_len( (uchar const *)p, (uchar const *)j->end );
125 51 : if( FD_UNLIKELY( !n ) ) { j->cur = p; return fail( j, FD_JTOK_ERR_SYNTAX ); }
126 21 : p += n;
127 21 : }
128 134577 : }
129 :
130 : /* lex_number validates the number starting at j->cur and advances past
131 : it. Stores FD_JTOK_INT or FD_JTOK_NUM in *kind. */
132 :
133 : static int
134 : lex_number( fd_jtok_t * j,
135 93816 : int * kind ) {
136 93816 : char const * p = j->cur;
137 93816 : if( p<j->end && *p=='-' ) p++;
138 93816 : if( FD_UNLIKELY( p>=j->end ) ) { j->cur = p; return fail( j, FD_JTOK_ERR_SYNTAX ); }
139 93810 : if( *p=='0' ) p++;
140 748137 : else if( *p>='1' && *p<='9' ) { while( p<j->end && is_digit( *p ) ) p++; }
141 0 : else { j->cur = p; return fail( j, FD_JTOK_ERR_SYNTAX ); }
142 93810 : int k = FD_JTOK_INT;
143 93810 : if( p<j->end && *p=='.' ) {
144 45 : p++;
145 45 : if( FD_UNLIKELY( p>=j->end || !is_digit( *p ) ) ) { j->cur = p; return fail( j, FD_JTOK_ERR_SYNTAX ); }
146 168 : while( p<j->end && is_digit( *p ) ) p++;
147 39 : k = FD_JTOK_NUM;
148 39 : }
149 93804 : if( p<j->end && (*p=='e' || *p=='E') ) {
150 72 : p++;
151 72 : if( p<j->end && (*p=='+' || *p=='-') ) p++;
152 72 : if( FD_UNLIKELY( p>=j->end || !is_digit( *p ) ) ) { j->cur = p; return fail( j, FD_JTOK_ERR_SYNTAX ); }
153 180 : while( p<j->end && is_digit( *p ) ) p++;
154 60 : k = FD_JTOK_NUM;
155 60 : }
156 93792 : j->cur = p;
157 93792 : *kind = k;
158 93792 : return 1;
159 93804 : }
160 :
161 : static int
162 : lex_literal( fd_jtok_t * j,
163 : char const * lit,
164 26289 : ulong sz ) {
165 26289 : if( FD_UNLIKELY( (ulong)( j->end-j->cur )<sz || memcmp( j->cur, lit, sz ) ) ) return fail( j, FD_JTOK_ERR_SYNTAX );
166 26277 : j->cur += sz;
167 26277 : return 1;
168 26289 : }
169 :
170 : /* lex_scalar validates and advances past one non-container value at
171 : j->cur (whitespace already skipped, cur<end). */
172 :
173 : static int
174 72594 : lex_scalar( fd_jtok_t * j ) {
175 72594 : fd_jtok_str_t s;
176 72594 : int kind;
177 72594 : switch( *j->cur ) {
178 10899 : case '"': return lex_string( j, &s );
179 9270 : case 't': return lex_literal( j, "true", 4UL );
180 7038 : case 'f': return lex_literal( j, "false", 5UL );
181 42 : case 'n': return lex_literal( j, "null", 4UL );
182 45345 : default:
183 45345 : if( *j->cur=='-' || is_digit( *j->cur ) ) return lex_number( j, &kind );
184 30 : return fail( j, FD_JTOK_ERR_SYNTAX );
185 72594 : }
186 72594 : }
187 :
188 : /* skip_value validates and advances past one value of any kind at
189 : j->cur. Container nesting is tracked in a bit stack (1 bit per
190 : level: object or array) bounded by FD_JTOK_SKIP_DEPTH_MAX. */
191 :
192 : static int
193 10896 : skip_value( fd_jtok_t * j ) {
194 10896 : ulong stack = 0UL;
195 10896 : ulong depth = 0UL;
196 10896 : fd_jtok_str_t key;
197 :
198 91158 : value:
199 91158 : skip_ws( j );
200 91158 : if( FD_UNLIKELY( j->cur>=j->end ) ) return fail( j, FD_JTOK_ERR_SYNTAX );
201 91155 : if( *j->cur=='{' || *j->cur=='[' ) {
202 18561 : if( FD_UNLIKELY( depth>=FD_JTOK_SKIP_DEPTH_MAX ) ) return fail( j, FD_JTOK_ERR_DEPTH );
203 18558 : int is_obj = (*j->cur=='{');
204 18558 : stack = (stack<<1) | (ulong)is_obj;
205 18558 : depth++;
206 18558 : j->cur++;
207 18558 : skip_ws( j );
208 18558 : if( FD_UNLIKELY( j->cur>=j->end ) ) return fail( j, FD_JTOK_ERR_SYNTAX );
209 18555 : if( *j->cur==(is_obj ? '}' : ']') ) { j->cur++; goto close; }
210 18507 : if( is_obj ) goto key;
211 2568 : goto value;
212 18507 : }
213 72594 : if( FD_UNLIKELY( !lex_scalar( j ) ) ) return 0;
214 :
215 90834 : after:
216 90834 : if( !depth ) return 1;
217 80061 : skip_ws( j );
218 80061 : if( FD_UNLIKELY( j->cur>=j->end ) ) return fail( j, FD_JTOK_ERR_SYNTAX );
219 80061 : {
220 80061 : int is_obj = (int)(stack&1UL);
221 80061 : char c = *j->cur;
222 80061 : if( c==',' ) {
223 61764 : j->cur++;
224 61764 : if( is_obj ) goto key;
225 6789 : goto value;
226 61764 : }
227 18297 : if( FD_UNLIKELY( c!=(is_obj ? '}' : ']') ) ) return fail( j, FD_JTOK_ERR_SYNTAX );
228 18288 : j->cur++;
229 18288 : }
230 :
231 18336 : close:
232 18336 : stack >>= 1;
233 18336 : depth--;
234 18336 : goto after;
235 :
236 70914 : key:
237 70914 : skip_ws( j );
238 70914 : if( FD_UNLIKELY( j->cur>=j->end || *j->cur!='"' ) ) return fail( j, FD_JTOK_ERR_SYNTAX );
239 70908 : if( FD_UNLIKELY( !lex_string( j, &key ) ) ) return 0;
240 70908 : skip_ws( j );
241 70908 : if( FD_UNLIKELY( j->cur>=j->end || *j->cur!=':' ) ) return fail( j, FD_JTOK_ERR_SYNTAX );
242 70905 : j->cur++;
243 70905 : goto value;
244 70908 : }
245 :
246 : /* begin is the common prologue of everything that consumes or inspects
247 : the pending value. Returns 1 with j->cur at the first byte of the
248 : value, 0 with err set otherwise. */
249 :
250 : static inline int
251 58842 : begin( fd_jtok_t * j ) {
252 58842 : if( FD_UNLIKELY( j->err ) ) return 0;
253 58821 : if( FD_UNLIKELY( !j->pending ) ) return fail( j, FD_JTOK_ERR_USAGE );
254 58818 : skip_ws( j );
255 58818 : if( FD_UNLIKELY( j->cur>=j->end ) ) return fail( j, FD_JTOK_ERR_SYNTAX );
256 58812 : return 1;
257 58818 : }
258 :
259 : /* peek_kind is fd_jtok_peek after begin. */
260 :
261 : static int
262 24663 : peek_kind( fd_jtok_t * j ) {
263 24663 : switch( *j->cur ) {
264 141 : case '{': return FD_JTOK_OBJ;
265 60 : case '[': return FD_JTOK_ARR;
266 105 : case '"': return FD_JTOK_STR;
267 15 : case 't': case 'f': return FD_JTOK_BOOL;
268 18 : case 'n': return FD_JTOK_NULL;
269 24324 : default: {
270 24324 : if( FD_UNLIKELY( !(*j->cur=='-' || is_digit( *j->cur )) ) ) return fail( j, FD_JTOK_ERR_SYNTAX );
271 24291 : char const * save = j->cur;
272 24291 : int kind;
273 24291 : if( FD_UNLIKELY( !lex_number( j, &kind ) ) ) return 0;
274 24279 : j->cur = save;
275 24279 : return kind;
276 24291 : }
277 24663 : }
278 24663 : }
279 :
280 : /* decode_unit decodes one raw string unit at *pp (a single UTF-8
281 : encoded byte, or a complete escape sequence) into out and advances
282 : *pp. Returns the number of bytes written, 0 if *pp is at end or the
283 : input is not a valid view. */
284 :
285 : static ulong
286 : decode_unit( char const ** pp,
287 : char const * end,
288 729267 : uchar out[ 4 ] ) {
289 729267 : char const * p = *pp;
290 729267 : if( FD_UNLIKELY( p>=end ) ) return 0UL;
291 729267 : if( *p!='\\' ) { out[0] = (uchar)*p; *pp = p+1; return 1UL; }
292 81 : if( FD_UNLIKELY( end-p<2L ) ) return 0UL;
293 81 : uint cp;
294 81 : switch( p[1] ) {
295 3 : case '"': out[0] = '"'; *pp = p+2; return 1UL;
296 3 : case '\\': out[0] = '\\'; *pp = p+2; return 1UL;
297 3 : case '/': out[0] = '/'; *pp = p+2; return 1UL;
298 3 : case 'b': out[0] = '\b'; *pp = p+2; return 1UL;
299 3 : case 'f': out[0] = '\f'; *pp = p+2; return 1UL;
300 24 : case 'n': out[0] = '\n'; *pp = p+2; return 1UL;
301 3 : case 'r': out[0] = '\r'; *pp = p+2; return 1UL;
302 3 : case 't': out[0] = '\t'; *pp = p+2; return 1UL;
303 36 : case 'u':
304 36 : if( FD_UNLIKELY( end-p<6L || !hex4( p+2, &cp ) ) ) return 0UL;
305 36 : p += 6;
306 36 : if( cp>=0xD800U && cp<=0xDBFFU ) {
307 12 : uint lo;
308 12 : if( FD_UNLIKELY( end-p<6L || p[0]!='\\' || p[1]!='u' || !hex4( p+2, &lo ) || lo<0xDC00U || lo>0xDFFFU ) ) return 0UL;
309 12 : cp = 0x10000U + ((cp-0xD800U)<<10) + (lo-0xDC00U);
310 12 : p += 6;
311 24 : } else if( FD_UNLIKELY( cp>=0xDC00U && cp<=0xDFFFU ) ) {
312 0 : return 0UL;
313 0 : }
314 36 : *pp = p;
315 36 : if( cp<0x80U ) {
316 18 : out[0] = (uchar)cp;
317 18 : return 1UL;
318 18 : }
319 18 : if( cp<0x800U ) {
320 3 : out[0] = (uchar)( 0xC0U | (cp>>6) );
321 3 : out[1] = (uchar)( 0x80U | (cp&0x3FU) );
322 3 : return 2UL;
323 3 : }
324 15 : if( cp<0x10000U ) {
325 3 : out[0] = (uchar)( 0xE0U | (cp>>12) );
326 3 : out[1] = (uchar)( 0x80U | ((cp>>6)&0x3FU) );
327 3 : out[2] = (uchar)( 0x80U | (cp&0x3FU) );
328 3 : return 3UL;
329 3 : }
330 12 : out[0] = (uchar)( 0xF0U | (cp>>18) );
331 12 : out[1] = (uchar)( 0x80U | ((cp>>12)&0x3FU) );
332 12 : out[2] = (uchar)( 0x80U | ((cp>>6)&0x3FU) );
333 12 : out[3] = (uchar)( 0x80U | (cp&0x3FU) );
334 12 : return 4UL;
335 0 : default:
336 0 : return 0UL;
337 81 : }
338 81 : }
339 :
340 : /* Public API */
341 :
342 : fd_jtok_t *
343 : fd_jtok_init( fd_jtok_t * j,
344 : void const * buf,
345 4953 : ulong sz ) {
346 4953 : static char const empty[1] = { '\0' };
347 4953 : if( FD_UNLIKELY( !buf ) ) { buf = empty; sz = 0UL; }
348 4953 : j->beg = (char const *)buf;
349 4953 : j->cur = j->beg;
350 4953 : j->end = j->beg + sz;
351 4953 : j->err = sz ? 0 : FD_JTOK_ERR_SYNTAX;
352 4953 : j->pending = 1;
353 4953 : j->first = 0;
354 4953 : j->depth = 0UL;
355 4953 : return j;
356 4953 : }
357 :
358 : int
359 4899 : fd_jtok_fini( fd_jtok_t * j ) {
360 4899 : if( FD_UNLIKELY( j->err ) ) return j->err;
361 4686 : if( FD_UNLIKELY( j->depth ) ) { fail( j, FD_JTOK_ERR_USAGE ); return j->err; }
362 4683 : if( j->pending ) {
363 192 : skip_ws( j );
364 192 : if( FD_UNLIKELY( !skip_value( j ) ) ) return j->err;
365 72 : j->pending = 0;
366 72 : }
367 4563 : skip_ws( j );
368 4563 : if( FD_UNLIKELY( j->cur<j->end ) ) fail( j, FD_JTOK_ERR_TRAIL );
369 4563 : return j->err;
370 4683 : }
371 :
372 : int
373 447 : fd_jtok_peek( fd_jtok_t * j ) {
374 447 : if( FD_UNLIKELY( !begin( j ) ) ) return 0;
375 432 : return peek_kind( j );
376 447 : }
377 :
378 : static void
379 : enter( fd_jtok_t * j,
380 14163 : char open ) {
381 14163 : if( FD_UNLIKELY( !begin( j ) ) ) return;
382 14160 : if( FD_UNLIKELY( *j->cur!=open ) ) { fail( j, FD_JTOK_ERR_TYPE ); return; }
383 14148 : j->cur++;
384 14148 : j->pending = 0;
385 14148 : j->first = 1;
386 14148 : j->depth++;
387 14148 : }
388 :
389 : /* next is the shared body of obj_next / arr_next. Returns 1 if the
390 : cursor is positioned at the next element (for objects: at the key),
391 : 0 if the container was closed or an error occurred. */
392 :
393 : static int
394 : next( fd_jtok_t * j,
395 76410 : char close ) {
396 76410 : if( FD_UNLIKELY( j->err ) ) return 0;
397 76362 : if( FD_UNLIKELY( !j->depth ) ) return fail( j, FD_JTOK_ERR_USAGE );
398 76359 : if( j->pending ) {
399 8538 : skip_ws( j );
400 8538 : if( FD_UNLIKELY( !skip_value( j ) ) ) return 0;
401 8535 : j->pending = 0;
402 8535 : }
403 76356 : skip_ws( j );
404 76356 : if( FD_UNLIKELY( j->cur>=j->end ) ) return fail( j, FD_JTOK_ERR_SYNTAX );
405 76350 : if( *j->cur==close ) {
406 14061 : j->cur++;
407 14061 : j->first = 0;
408 14061 : j->depth--;
409 14061 : return 0;
410 14061 : }
411 62289 : if( j->first ) {
412 14088 : j->first = 0;
413 48201 : } else {
414 48201 : if( FD_UNLIKELY( *j->cur!=',' ) ) return fail( j, FD_JTOK_ERR_SYNTAX );
415 48186 : j->cur++;
416 48186 : skip_ws( j );
417 48186 : }
418 62274 : j->pending = 1;
419 62274 : return 1;
420 62289 : }
421 :
422 : void
423 9792 : fd_jtok_obj_enter( fd_jtok_t * j ) {
424 9792 : enter( j, '{' );
425 9792 : }
426 :
427 : int
428 : fd_jtok_obj_next( fd_jtok_t * j,
429 54666 : fd_jtok_str_t * key ) {
430 54666 : if( !next( j, '}' ) ) return 0;
431 44898 : if( FD_UNLIKELY( j->cur>=j->end || *j->cur!='"' ) ) return fail( j, FD_JTOK_ERR_SYNTAX );
432 44886 : if( FD_UNLIKELY( !lex_string( j, key ) ) ) return 0;
433 44886 : skip_ws( j );
434 44886 : if( FD_UNLIKELY( j->cur>=j->end || *j->cur!=':' ) ) return fail( j, FD_JTOK_ERR_SYNTAX );
435 44880 : j->cur++;
436 44880 : return 1;
437 44886 : }
438 :
439 : void
440 4371 : fd_jtok_arr_enter( fd_jtok_t * j ) {
441 4371 : enter( j, '[' );
442 4371 : }
443 :
444 : int
445 21744 : fd_jtok_arr_next( fd_jtok_t * j ) {
446 21744 : return next( j, ']' );
447 21744 : }
448 :
449 : void
450 : fd_jtok_ulong( fd_jtok_t * j,
451 23622 : ulong * out ) {
452 23622 : if( FD_UNLIKELY( !begin( j ) ) ) return;
453 23616 : int kind = peek_kind( j );
454 23616 : if( FD_UNLIKELY( !kind ) ) return;
455 23616 : if( FD_UNLIKELY( kind!=FD_JTOK_INT ) ) { fail( j, FD_JTOK_ERR_TYPE ); return; }
456 23607 : char const * p = j->cur;
457 23607 : if( FD_UNLIKELY( *p=='-' ) ) { fail( j, FD_JTOK_ERR_RANGE ); return; }
458 23604 : lex_number( j, &kind );
459 23604 : ulong v = 0UL;
460 198027 : for( ; p<j->cur; p++ ) {
461 174426 : ulong d = (ulong)( *p-'0' );
462 174426 : if( FD_UNLIKELY( v>(ULONG_MAX-d)/10UL ) ) { j->cur = p; fail( j, FD_JTOK_ERR_RANGE ); return; }
463 174423 : v = v*10UL + d;
464 174423 : }
465 23601 : j->pending = 0;
466 23601 : *out = v;
467 23601 : }
468 :
469 : void
470 : fd_jtok_long( fd_jtok_t * j,
471 528 : long * out ) {
472 528 : if( FD_UNLIKELY( !begin( j ) ) ) return;
473 528 : int kind = peek_kind( j );
474 528 : if( FD_UNLIKELY( !kind ) ) return;
475 528 : if( FD_UNLIKELY( kind!=FD_JTOK_INT ) ) { fail( j, FD_JTOK_ERR_TYPE ); return; }
476 528 : char const * p = j->cur;
477 528 : int neg = (*p=='-');
478 528 : if( neg ) p++;
479 528 : lex_number( j, &kind );
480 528 : ulong lim = neg ? (ulong)LONG_MAX+1UL : (ulong)LONG_MAX;
481 528 : ulong v = 0UL;
482 1269 : for( ; p<j->cur; p++ ) {
483 747 : ulong d = (ulong)( *p-'0' );
484 747 : if( FD_UNLIKELY( v>(lim-d)/10UL ) ) { j->cur = p; fail( j, FD_JTOK_ERR_RANGE ); return; }
485 741 : v = v*10UL + d;
486 741 : }
487 522 : j->pending = 0;
488 522 : *out = neg ? (long)( 0UL-v ) : (long)v;
489 522 : }
490 :
491 : void
492 : fd_jtok_double( fd_jtok_t * j,
493 81 : double * out ) {
494 81 : if( FD_UNLIKELY( !begin( j ) ) ) return;
495 81 : int kind = peek_kind( j );
496 81 : if( FD_UNLIKELY( !kind ) ) return;
497 81 : if( FD_UNLIKELY( kind!=FD_JTOK_INT && kind!=FD_JTOK_NUM ) ) { fail( j, FD_JTOK_ERR_TYPE ); return; }
498 78 : char const * start = j->cur;
499 78 : lex_number( j, &kind );
500 78 : ulong sz = (ulong)( j->cur-start );
501 78 : if( FD_UNLIKELY( sz>FD_JTOK_NUM_SZ_MAX ) ) { j->cur = start; fail( j, FD_JTOK_ERR_RANGE ); return; }
502 75 : char buf[ FD_JTOK_NUM_SZ_MAX+1UL ];
503 75 : fd_memcpy( buf, start, sz );
504 75 : buf[ sz ] = '\0';
505 75 : errno = 0;
506 75 : double v = strtod( buf, NULL );
507 75 : if( FD_UNLIKELY( errno==ERANGE || !isfinite( v ) ) ) { j->cur = start; fail( j, FD_JTOK_ERR_RANGE ); return; }
508 66 : j->pending = 0;
509 66 : *out = v;
510 66 : }
511 :
512 : void
513 : fd_jtok_bool( fd_jtok_t * j,
514 9930 : int * out ) {
515 9930 : if( FD_UNLIKELY( !begin( j ) ) ) return;
516 9930 : int v;
517 9930 : switch( *j->cur ) {
518 5235 : case 't': if( FD_UNLIKELY( !lex_literal( j, "true", 4UL ) ) ) return; v = 1; break;
519 4692 : case 'f': if( FD_UNLIKELY( !lex_literal( j, "false", 5UL ) ) ) return; v = 0; break;
520 3 : default:
521 3 : if( peek_kind( j ) ) fail( j, FD_JTOK_ERR_TYPE );
522 3 : return;
523 9930 : }
524 9924 : j->pending = 0;
525 9924 : *out = v;
526 9924 : }
527 :
528 : void
529 12 : fd_jtok_null( fd_jtok_t * j ) {
530 12 : if( FD_UNLIKELY( !begin( j ) ) ) return;
531 12 : if( FD_UNLIKELY( *j->cur!='n' ) ) { if( peek_kind( j ) ) fail( j, FD_JTOK_ERR_TYPE ); return; }
532 12 : if( FD_UNLIKELY( !lex_literal( j, "null", 4UL ) ) ) return;
533 9 : j->pending = 0;
534 9 : }
535 :
536 : void
537 : fd_jtok_str( fd_jtok_t * j,
538 7887 : fd_jtok_str_t * out ) {
539 7887 : if( FD_UNLIKELY( !begin( j ) ) ) return;
540 7887 : if( FD_UNLIKELY( *j->cur!='"' ) ) { if( peek_kind( j ) ) fail( j, FD_JTOK_ERR_TYPE ); return; }
541 7884 : fd_jtok_str_t s;
542 7884 : if( FD_UNLIKELY( !lex_string( j, &s ) ) ) return;
543 7836 : j->pending = 0;
544 7836 : *out = s;
545 7836 : }
546 :
547 : long
548 : fd_jtok_str_decode( fd_jtok_str_t const * s,
549 : char * out,
550 510 : ulong out_sz ) {
551 510 : if( out_sz ) out[0] = '\0';
552 510 : char const * p = s->ptr;
553 510 : char const * end = s->ptr+s->sz;
554 510 : ulong o = 0UL;
555 4215 : while( p<end ) {
556 3726 : uchar unit[ 4 ];
557 3726 : ulong n = decode_unit( &p, end, unit );
558 3726 : if( FD_UNLIKELY( !n || !unit[0] || o+n+1UL>out_sz ) ) {
559 21 : if( out_sz ) out[0] = '\0';
560 21 : return -1L;
561 21 : }
562 3705 : fd_memcpy( out+o, unit, n );
563 3705 : o += n;
564 3705 : }
565 489 : if( FD_UNLIKELY( o>=out_sz ) ) return -1L;
566 486 : out[ o ] = '\0';
567 486 : return (long)o;
568 489 : }
569 :
570 : void
571 : fd_jtok_cstr( fd_jtok_t * j,
572 : char * out,
573 393 : ulong out_sz ) {
574 393 : if( out_sz ) out[0] = '\0';
575 393 : fd_jtok_str_t s;
576 393 : char const * save = j->cur;
577 393 : fd_jtok_str( j, &s );
578 393 : if( FD_UNLIKELY( j->err ) ) return;
579 390 : if( FD_UNLIKELY( fd_jtok_str_decode( &s, out, out_sz )<0L ) ) {
580 15 : j->cur = save;
581 15 : fail( j, FD_JTOK_ERR_RANGE );
582 15 : }
583 390 : }
584 :
585 : void
586 3 : fd_jtok_skip( fd_jtok_t * j ) {
587 3 : if( FD_UNLIKELY( !begin( j ) ) ) return;
588 0 : if( FD_UNLIKELY( !skip_value( j ) ) ) return;
589 0 : j->pending = 0;
590 0 : }
591 :
592 : void
593 : fd_jtok_raw( fd_jtok_t * j,
594 : char const ** ptr,
595 2169 : ulong * sz ) {
596 2169 : if( FD_UNLIKELY( !begin( j ) ) ) return;
597 2166 : char const * start = j->cur;
598 2166 : if( FD_UNLIKELY( !skip_value( j ) ) ) return;
599 2166 : j->pending = 0;
600 2166 : *ptr = start;
601 2166 : *sz = (ulong)( j->cur-start );
602 2166 : }
603 :
604 : int
605 : fd_jtok_str_eq( fd_jtok_str_t const * s,
606 141663 : char const * lit ) {
607 141663 : char const * p = s->ptr;
608 141663 : char const * end = s->ptr+s->sz;
609 769983 : while( p<end ) {
610 725541 : uchar unit[ 4 ];
611 725541 : ulong n = decode_unit( &p, end, unit );
612 725541 : if( FD_UNLIKELY( !n || !unit[0] ) ) return 0;
613 1353876 : for( ulong i=0UL; i<n; i++ ) if( (uchar)lit[i]!=unit[i] ) return 0;
614 628320 : lit += n;
615 628320 : }
616 44442 : return *lit=='\0';
617 141663 : }
|