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 0 : int err ) {
14 0 : j->err = err;
15 0 : return 0;
16 0 : }
17 :
18 : static inline int
19 0 : is_ws( char c ) {
20 0 : return c==' ' || c=='\t' || c=='\n' || c=='\r';
21 0 : }
22 :
23 : static inline int
24 0 : is_digit( char c ) {
25 0 : return c>='0' && c<='9';
26 0 : }
27 :
28 : static inline void
29 0 : skip_ws( fd_jtok_t * j ) {
30 0 : while( j->cur<j->end && is_ws( *j->cur ) ) j->cur++;
31 0 : }
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 0 : uchar const * end ) {
40 0 : ulong rem = (ulong)( end-p );
41 0 : uchar c = p[0];
42 0 : ulong n;
43 0 : uchar lo = 0x80, hi = 0xBF;
44 0 : if( c>=0xC2 && c<=0xDF ) n = 2UL;
45 0 : else if( c>=0xE0 && c<=0xEF ) { n = 3UL; if( c==0xE0 ) lo = 0xA0; else if( c==0xED ) hi = 0x9F; }
46 0 : else if( c>=0xF0 && c<=0xF4 ) { n = 4UL; if( c==0xF0 ) lo = 0x90; else if( c==0xF4 ) hi = 0x8F; }
47 0 : else return 0UL;
48 0 : if( FD_UNLIKELY( rem<n ) ) return 0UL;
49 0 : if( FD_UNLIKELY( p[1]<lo || p[1]>hi ) ) return 0UL;
50 0 : for( ulong i=2UL; i<n; i++ ) if( FD_UNLIKELY( (p[i]&0xC0)!=0x80 ) ) return 0UL;
51 0 : return n;
52 0 : }
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 0 : uint * cp ) {
60 0 : uint v = 0U;
61 0 : for( ulong i=0UL; i<4UL; i++ ) {
62 0 : char c = p[i];
63 0 : uint d;
64 0 : if( c>='0' && c<='9' ) d = (uint)( c-'0' );
65 0 : else if( c>='a' && c<='f' ) d = (uint)( c-'a' ) + 10U;
66 0 : else if( c>='A' && c<='F' ) d = (uint)( c-'A' ) + 10U;
67 0 : else return 0;
68 0 : v = (v<<4) | d;
69 0 : }
70 0 : *cp = v;
71 0 : return 1;
72 0 : }
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 0 : fd_jtok_str_t * out ) {
83 0 : char const * p = j->cur+1;
84 0 : char const * start = p;
85 0 : for(;;) {
86 0 : if( FD_UNLIKELY( p>=j->end ) ) { j->cur = p; return fail( j, FD_JTOK_ERR_SYNTAX ); }
87 0 : uchar c = (uchar)*p;
88 0 : if( c=='"' ) {
89 0 : out->ptr = start;
90 0 : out->sz = (ulong)( p-start );
91 0 : j->cur = p+1;
92 0 : return 1;
93 0 : }
94 0 : if( c=='\\' ) {
95 0 : p++;
96 0 : if( FD_UNLIKELY( p>=j->end ) ) { j->cur = p; return fail( j, FD_JTOK_ERR_SYNTAX ); }
97 0 : switch( *p ) {
98 0 : case '"': case '\\': case '/': case 'b': case 'f': case 'n': case 'r': case 't':
99 0 : p++;
100 0 : break;
101 0 : case 'u': {
102 0 : uint cp;
103 0 : p++;
104 0 : if( FD_UNLIKELY( j->end-p<4L || !hex4( p, &cp ) ) ) { j->cur = p; return fail( j, FD_JTOK_ERR_SYNTAX ); }
105 0 : p += 4;
106 0 : if( cp>=0xD800U && cp<=0xDBFFU ) {
107 0 : uint lo;
108 0 : if( FD_UNLIKELY( j->end-p<6L || p[0]!='\\' || p[1]!='u' || !hex4( p+2, &lo ) || lo<0xDC00U || lo>0xDFFFU ) ) {
109 0 : j->cur = p; return fail( j, FD_JTOK_ERR_SYNTAX );
110 0 : }
111 0 : p += 6;
112 0 : } else if( FD_UNLIKELY( cp>=0xDC00U && cp<=0xDFFFU ) ) {
113 0 : j->cur = p-4; return fail( j, FD_JTOK_ERR_SYNTAX );
114 0 : }
115 0 : break;
116 0 : }
117 0 : default:
118 0 : j->cur = p; return fail( j, FD_JTOK_ERR_SYNTAX );
119 0 : }
120 0 : continue;
121 0 : }
122 0 : if( FD_UNLIKELY( c<0x20 ) ) { j->cur = p; return fail( j, FD_JTOK_ERR_SYNTAX ); }
123 0 : if( c<0x80 ) { p++; continue; }
124 0 : ulong n = utf8_len( (uchar const *)p, (uchar const *)j->end );
125 0 : if( FD_UNLIKELY( !n ) ) { j->cur = p; return fail( j, FD_JTOK_ERR_SYNTAX ); }
126 0 : p += n;
127 0 : }
128 0 : }
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 0 : int * kind ) {
136 0 : char const * p = j->cur;
137 0 : if( p<j->end && *p=='-' ) p++;
138 0 : if( FD_UNLIKELY( p>=j->end ) ) { j->cur = p; return fail( j, FD_JTOK_ERR_SYNTAX ); }
139 0 : if( *p=='0' ) p++;
140 0 : 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 0 : int k = FD_JTOK_INT;
143 0 : if( p<j->end && *p=='.' ) {
144 0 : p++;
145 0 : if( FD_UNLIKELY( p>=j->end || !is_digit( *p ) ) ) { j->cur = p; return fail( j, FD_JTOK_ERR_SYNTAX ); }
146 0 : while( p<j->end && is_digit( *p ) ) p++;
147 0 : k = FD_JTOK_NUM;
148 0 : }
149 0 : if( p<j->end && (*p=='e' || *p=='E') ) {
150 0 : p++;
151 0 : if( p<j->end && (*p=='+' || *p=='-') ) p++;
152 0 : if( FD_UNLIKELY( p>=j->end || !is_digit( *p ) ) ) { j->cur = p; return fail( j, FD_JTOK_ERR_SYNTAX ); }
153 0 : while( p<j->end && is_digit( *p ) ) p++;
154 0 : k = FD_JTOK_NUM;
155 0 : }
156 0 : j->cur = p;
157 0 : *kind = k;
158 0 : return 1;
159 0 : }
160 :
161 : static int
162 : lex_literal( fd_jtok_t * j,
163 : char const * lit,
164 0 : ulong sz ) {
165 0 : if( FD_UNLIKELY( (ulong)( j->end-j->cur )<sz || memcmp( j->cur, lit, sz ) ) ) return fail( j, FD_JTOK_ERR_SYNTAX );
166 0 : j->cur += sz;
167 0 : return 1;
168 0 : }
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 0 : lex_scalar( fd_jtok_t * j ) {
175 0 : fd_jtok_str_t s;
176 0 : int kind;
177 0 : switch( *j->cur ) {
178 0 : case '"': return lex_string( j, &s );
179 0 : case 't': return lex_literal( j, "true", 4UL );
180 0 : case 'f': return lex_literal( j, "false", 5UL );
181 0 : case 'n': return lex_literal( j, "null", 4UL );
182 0 : default:
183 0 : if( *j->cur=='-' || is_digit( *j->cur ) ) return lex_number( j, &kind );
184 0 : return fail( j, FD_JTOK_ERR_SYNTAX );
185 0 : }
186 0 : }
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 0 : skip_value( fd_jtok_t * j ) {
194 0 : ulong stack = 0UL;
195 0 : ulong depth = 0UL;
196 0 : fd_jtok_str_t key;
197 :
198 0 : value:
199 0 : skip_ws( j );
200 0 : if( FD_UNLIKELY( j->cur>=j->end ) ) return fail( j, FD_JTOK_ERR_SYNTAX );
201 0 : if( *j->cur=='{' || *j->cur=='[' ) {
202 0 : if( FD_UNLIKELY( depth>=FD_JTOK_SKIP_DEPTH_MAX ) ) return fail( j, FD_JTOK_ERR_DEPTH );
203 0 : int is_obj = (*j->cur=='{');
204 0 : stack = (stack<<1) | (ulong)is_obj;
205 0 : depth++;
206 0 : j->cur++;
207 0 : skip_ws( j );
208 0 : if( FD_UNLIKELY( j->cur>=j->end ) ) return fail( j, FD_JTOK_ERR_SYNTAX );
209 0 : if( *j->cur==(is_obj ? '}' : ']') ) { j->cur++; goto close; }
210 0 : if( is_obj ) goto key;
211 0 : goto value;
212 0 : }
213 0 : if( FD_UNLIKELY( !lex_scalar( j ) ) ) return 0;
214 :
215 0 : after:
216 0 : if( !depth ) return 1;
217 0 : skip_ws( j );
218 0 : if( FD_UNLIKELY( j->cur>=j->end ) ) return fail( j, FD_JTOK_ERR_SYNTAX );
219 0 : {
220 0 : int is_obj = (int)(stack&1UL);
221 0 : char c = *j->cur;
222 0 : if( c==',' ) {
223 0 : j->cur++;
224 0 : if( is_obj ) goto key;
225 0 : goto value;
226 0 : }
227 0 : if( FD_UNLIKELY( c!=(is_obj ? '}' : ']') ) ) return fail( j, FD_JTOK_ERR_SYNTAX );
228 0 : j->cur++;
229 0 : }
230 :
231 0 : close:
232 0 : stack >>= 1;
233 0 : depth--;
234 0 : goto after;
235 :
236 0 : key:
237 0 : skip_ws( j );
238 0 : if( FD_UNLIKELY( j->cur>=j->end || *j->cur!='"' ) ) return fail( j, FD_JTOK_ERR_SYNTAX );
239 0 : if( FD_UNLIKELY( !lex_string( j, &key ) ) ) return 0;
240 0 : skip_ws( j );
241 0 : if( FD_UNLIKELY( j->cur>=j->end || *j->cur!=':' ) ) return fail( j, FD_JTOK_ERR_SYNTAX );
242 0 : j->cur++;
243 0 : goto value;
244 0 : }
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 0 : begin( fd_jtok_t * j ) {
252 0 : if( FD_UNLIKELY( j->err ) ) return 0;
253 0 : if( FD_UNLIKELY( !j->pending ) ) return fail( j, FD_JTOK_ERR_USAGE );
254 0 : skip_ws( j );
255 0 : if( FD_UNLIKELY( j->cur>=j->end ) ) return fail( j, FD_JTOK_ERR_SYNTAX );
256 0 : return 1;
257 0 : }
258 :
259 : /* peek_kind is fd_jtok_peek after begin. */
260 :
261 : static int
262 0 : peek_kind( fd_jtok_t * j ) {
263 0 : switch( *j->cur ) {
264 0 : case '{': return FD_JTOK_OBJ;
265 0 : case '[': return FD_JTOK_ARR;
266 0 : case '"': return FD_JTOK_STR;
267 0 : case 't': case 'f': return FD_JTOK_BOOL;
268 0 : case 'n': return FD_JTOK_NULL;
269 0 : default: {
270 0 : if( FD_UNLIKELY( !(*j->cur=='-' || is_digit( *j->cur )) ) ) return fail( j, FD_JTOK_ERR_SYNTAX );
271 0 : char const * save = j->cur;
272 0 : int kind;
273 0 : if( FD_UNLIKELY( !lex_number( j, &kind ) ) ) return 0;
274 0 : j->cur = save;
275 0 : return kind;
276 0 : }
277 0 : }
278 0 : }
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 0 : uchar out[ 4 ] ) {
289 0 : char const * p = *pp;
290 0 : if( FD_UNLIKELY( p>=end ) ) return 0UL;
291 0 : if( *p!='\\' ) { out[0] = (uchar)*p; *pp = p+1; return 1UL; }
292 0 : if( FD_UNLIKELY( end-p<2L ) ) return 0UL;
293 0 : uint cp;
294 0 : switch( p[1] ) {
295 0 : case '"': out[0] = '"'; *pp = p+2; return 1UL;
296 0 : case '\\': out[0] = '\\'; *pp = p+2; return 1UL;
297 0 : case '/': out[0] = '/'; *pp = p+2; return 1UL;
298 0 : case 'b': out[0] = '\b'; *pp = p+2; return 1UL;
299 0 : case 'f': out[0] = '\f'; *pp = p+2; return 1UL;
300 0 : case 'n': out[0] = '\n'; *pp = p+2; return 1UL;
301 0 : case 'r': out[0] = '\r'; *pp = p+2; return 1UL;
302 0 : case 't': out[0] = '\t'; *pp = p+2; return 1UL;
303 0 : case 'u':
304 0 : if( FD_UNLIKELY( end-p<6L || !hex4( p+2, &cp ) ) ) return 0UL;
305 0 : p += 6;
306 0 : if( cp>=0xD800U && cp<=0xDBFFU ) {
307 0 : uint lo;
308 0 : if( FD_UNLIKELY( end-p<6L || p[0]!='\\' || p[1]!='u' || !hex4( p+2, &lo ) || lo<0xDC00U || lo>0xDFFFU ) ) return 0UL;
309 0 : cp = 0x10000U + ((cp-0xD800U)<<10) + (lo-0xDC00U);
310 0 : p += 6;
311 0 : } else if( FD_UNLIKELY( cp>=0xDC00U && cp<=0xDFFFU ) ) {
312 0 : return 0UL;
313 0 : }
314 0 : *pp = p;
315 0 : if( cp<0x80U ) {
316 0 : out[0] = (uchar)cp;
317 0 : return 1UL;
318 0 : }
319 0 : if( cp<0x800U ) {
320 0 : out[0] = (uchar)( 0xC0U | (cp>>6) );
321 0 : out[1] = (uchar)( 0x80U | (cp&0x3FU) );
322 0 : return 2UL;
323 0 : }
324 0 : if( cp<0x10000U ) {
325 0 : out[0] = (uchar)( 0xE0U | (cp>>12) );
326 0 : out[1] = (uchar)( 0x80U | ((cp>>6)&0x3FU) );
327 0 : out[2] = (uchar)( 0x80U | (cp&0x3FU) );
328 0 : return 3UL;
329 0 : }
330 0 : out[0] = (uchar)( 0xF0U | (cp>>18) );
331 0 : out[1] = (uchar)( 0x80U | ((cp>>12)&0x3FU) );
332 0 : out[2] = (uchar)( 0x80U | ((cp>>6)&0x3FU) );
333 0 : out[3] = (uchar)( 0x80U | (cp&0x3FU) );
334 0 : return 4UL;
335 0 : default:
336 0 : return 0UL;
337 0 : }
338 0 : }
339 :
340 : /* Public API */
341 :
342 : fd_jtok_t *
343 : fd_jtok_init( fd_jtok_t * j,
344 : void const * buf,
345 0 : ulong sz ) {
346 0 : static char const empty[1] = { '\0' };
347 0 : if( FD_UNLIKELY( !buf ) ) { buf = empty; sz = 0UL; }
348 0 : j->beg = (char const *)buf;
349 0 : j->cur = j->beg;
350 0 : j->end = j->beg + sz;
351 0 : j->err = sz ? 0 : FD_JTOK_ERR_SYNTAX;
352 0 : j->pending = 1;
353 0 : j->first = 0;
354 0 : j->depth = 0UL;
355 0 : return j;
356 0 : }
357 :
358 : int
359 0 : fd_jtok_fini( fd_jtok_t * j ) {
360 0 : if( FD_UNLIKELY( j->err ) ) return j->err;
361 0 : if( FD_UNLIKELY( j->depth ) ) { fail( j, FD_JTOK_ERR_USAGE ); return j->err; }
362 0 : if( j->pending ) {
363 0 : skip_ws( j );
364 0 : if( FD_UNLIKELY( !skip_value( j ) ) ) return j->err;
365 0 : j->pending = 0;
366 0 : }
367 0 : skip_ws( j );
368 0 : if( FD_UNLIKELY( j->cur<j->end ) ) fail( j, FD_JTOK_ERR_TRAIL );
369 0 : return j->err;
370 0 : }
371 :
372 : int
373 0 : fd_jtok_peek( fd_jtok_t * j ) {
374 0 : if( FD_UNLIKELY( !begin( j ) ) ) return 0;
375 0 : return peek_kind( j );
376 0 : }
377 :
378 : static void
379 : enter( fd_jtok_t * j,
380 0 : char open ) {
381 0 : if( FD_UNLIKELY( !begin( j ) ) ) return;
382 0 : if( FD_UNLIKELY( *j->cur!=open ) ) { fail( j, FD_JTOK_ERR_TYPE ); return; }
383 0 : j->cur++;
384 0 : j->pending = 0;
385 0 : j->first = 1;
386 0 : j->depth++;
387 0 : }
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 0 : char close ) {
396 0 : if( FD_UNLIKELY( j->err ) ) return 0;
397 0 : if( FD_UNLIKELY( !j->depth ) ) return fail( j, FD_JTOK_ERR_USAGE );
398 0 : if( j->pending ) {
399 0 : skip_ws( j );
400 0 : if( FD_UNLIKELY( !skip_value( j ) ) ) return 0;
401 0 : j->pending = 0;
402 0 : }
403 0 : skip_ws( j );
404 0 : if( FD_UNLIKELY( j->cur>=j->end ) ) return fail( j, FD_JTOK_ERR_SYNTAX );
405 0 : if( *j->cur==close ) {
406 0 : j->cur++;
407 0 : j->first = 0;
408 0 : j->depth--;
409 0 : return 0;
410 0 : }
411 0 : if( j->first ) {
412 0 : j->first = 0;
413 0 : } else {
414 0 : if( FD_UNLIKELY( *j->cur!=',' ) ) return fail( j, FD_JTOK_ERR_SYNTAX );
415 0 : j->cur++;
416 0 : skip_ws( j );
417 0 : }
418 0 : j->pending = 1;
419 0 : return 1;
420 0 : }
421 :
422 : void
423 0 : fd_jtok_obj_enter( fd_jtok_t * j ) {
424 0 : enter( j, '{' );
425 0 : }
426 :
427 : int
428 : fd_jtok_obj_next( fd_jtok_t * j,
429 0 : fd_jtok_str_t * key ) {
430 0 : if( !next( j, '}' ) ) return 0;
431 0 : if( FD_UNLIKELY( j->cur>=j->end || *j->cur!='"' ) ) return fail( j, FD_JTOK_ERR_SYNTAX );
432 0 : if( FD_UNLIKELY( !lex_string( j, key ) ) ) return 0;
433 0 : skip_ws( j );
434 0 : if( FD_UNLIKELY( j->cur>=j->end || *j->cur!=':' ) ) return fail( j, FD_JTOK_ERR_SYNTAX );
435 0 : j->cur++;
436 0 : return 1;
437 0 : }
438 :
439 : void
440 0 : fd_jtok_arr_enter( fd_jtok_t * j ) {
441 0 : enter( j, '[' );
442 0 : }
443 :
444 : int
445 0 : fd_jtok_arr_next( fd_jtok_t * j ) {
446 0 : return next( j, ']' );
447 0 : }
448 :
449 : void
450 : fd_jtok_ulong( fd_jtok_t * j,
451 0 : ulong * out ) {
452 0 : if( FD_UNLIKELY( !begin( j ) ) ) return;
453 0 : int kind = peek_kind( j );
454 0 : if( FD_UNLIKELY( !kind ) ) return;
455 0 : if( FD_UNLIKELY( kind!=FD_JTOK_INT ) ) { fail( j, FD_JTOK_ERR_TYPE ); return; }
456 0 : char const * p = j->cur;
457 0 : if( FD_UNLIKELY( *p=='-' ) ) { fail( j, FD_JTOK_ERR_RANGE ); return; }
458 0 : lex_number( j, &kind );
459 0 : ulong v = 0UL;
460 0 : for( ; p<j->cur; p++ ) {
461 0 : ulong d = (ulong)( *p-'0' );
462 0 : if( FD_UNLIKELY( v>(ULONG_MAX-d)/10UL ) ) { j->cur = p; fail( j, FD_JTOK_ERR_RANGE ); return; }
463 0 : v = v*10UL + d;
464 0 : }
465 0 : j->pending = 0;
466 0 : *out = v;
467 0 : }
468 :
469 : void
470 : fd_jtok_long( fd_jtok_t * j,
471 0 : long * out ) {
472 0 : if( FD_UNLIKELY( !begin( j ) ) ) return;
473 0 : int kind = peek_kind( j );
474 0 : if( FD_UNLIKELY( !kind ) ) return;
475 0 : if( FD_UNLIKELY( kind!=FD_JTOK_INT ) ) { fail( j, FD_JTOK_ERR_TYPE ); return; }
476 0 : char const * p = j->cur;
477 0 : int neg = (*p=='-');
478 0 : if( neg ) p++;
479 0 : lex_number( j, &kind );
480 0 : ulong lim = neg ? (ulong)LONG_MAX+1UL : (ulong)LONG_MAX;
481 0 : ulong v = 0UL;
482 0 : for( ; p<j->cur; p++ ) {
483 0 : ulong d = (ulong)( *p-'0' );
484 0 : if( FD_UNLIKELY( v>(lim-d)/10UL ) ) { j->cur = p; fail( j, FD_JTOK_ERR_RANGE ); return; }
485 0 : v = v*10UL + d;
486 0 : }
487 0 : j->pending = 0;
488 0 : *out = neg ? (long)( 0UL-v ) : (long)v;
489 0 : }
490 :
491 : void
492 : fd_jtok_double( fd_jtok_t * j,
493 0 : double * out ) {
494 0 : if( FD_UNLIKELY( !begin( j ) ) ) return;
495 0 : int kind = peek_kind( j );
496 0 : if( FD_UNLIKELY( !kind ) ) return;
497 0 : if( FD_UNLIKELY( kind!=FD_JTOK_INT && kind!=FD_JTOK_NUM ) ) { fail( j, FD_JTOK_ERR_TYPE ); return; }
498 0 : char const * start = j->cur;
499 0 : lex_number( j, &kind );
500 0 : ulong sz = (ulong)( j->cur-start );
501 0 : if( FD_UNLIKELY( sz>FD_JTOK_NUM_SZ_MAX ) ) { j->cur = start; fail( j, FD_JTOK_ERR_RANGE ); return; }
502 0 : char buf[ FD_JTOK_NUM_SZ_MAX+1UL ];
503 0 : fd_memcpy( buf, start, sz );
504 0 : buf[ sz ] = '\0';
505 0 : errno = 0;
506 0 : double v = strtod( buf, NULL );
507 0 : if( FD_UNLIKELY( errno==ERANGE || !isfinite( v ) ) ) { j->cur = start; fail( j, FD_JTOK_ERR_RANGE ); return; }
508 0 : j->pending = 0;
509 0 : *out = v;
510 0 : }
511 :
512 : void
513 : fd_jtok_bool( fd_jtok_t * j,
514 0 : int * out ) {
515 0 : if( FD_UNLIKELY( !begin( j ) ) ) return;
516 0 : int v;
517 0 : switch( *j->cur ) {
518 0 : case 't': if( FD_UNLIKELY( !lex_literal( j, "true", 4UL ) ) ) return; v = 1; break;
519 0 : case 'f': if( FD_UNLIKELY( !lex_literal( j, "false", 5UL ) ) ) return; v = 0; break;
520 0 : default:
521 0 : if( peek_kind( j ) ) fail( j, FD_JTOK_ERR_TYPE );
522 0 : return;
523 0 : }
524 0 : j->pending = 0;
525 0 : *out = v;
526 0 : }
527 :
528 : void
529 0 : fd_jtok_null( fd_jtok_t * j ) {
530 0 : if( FD_UNLIKELY( !begin( j ) ) ) return;
531 0 : if( FD_UNLIKELY( *j->cur!='n' ) ) { if( peek_kind( j ) ) fail( j, FD_JTOK_ERR_TYPE ); return; }
532 0 : if( FD_UNLIKELY( !lex_literal( j, "null", 4UL ) ) ) return;
533 0 : j->pending = 0;
534 0 : }
535 :
536 : void
537 : fd_jtok_str( fd_jtok_t * j,
538 0 : fd_jtok_str_t * out ) {
539 0 : if( FD_UNLIKELY( !begin( j ) ) ) return;
540 0 : if( FD_UNLIKELY( *j->cur!='"' ) ) { if( peek_kind( j ) ) fail( j, FD_JTOK_ERR_TYPE ); return; }
541 0 : fd_jtok_str_t s;
542 0 : if( FD_UNLIKELY( !lex_string( j, &s ) ) ) return;
543 0 : j->pending = 0;
544 0 : *out = s;
545 0 : }
546 :
547 : long
548 : fd_jtok_str_decode( fd_jtok_str_t const * s,
549 : char * out,
550 0 : ulong out_sz ) {
551 0 : if( out_sz ) out[0] = '\0';
552 0 : char const * p = s->ptr;
553 0 : char const * end = s->ptr+s->sz;
554 0 : ulong o = 0UL;
555 0 : while( p<end ) {
556 0 : uchar unit[ 4 ];
557 0 : ulong n = decode_unit( &p, end, unit );
558 0 : if( FD_UNLIKELY( !n || !unit[0] || o+n+1UL>out_sz ) ) {
559 0 : if( out_sz ) out[0] = '\0';
560 0 : return -1L;
561 0 : }
562 0 : fd_memcpy( out+o, unit, n );
563 0 : o += n;
564 0 : }
565 0 : if( FD_UNLIKELY( o>=out_sz ) ) return -1L;
566 0 : out[ o ] = '\0';
567 0 : return (long)o;
568 0 : }
569 :
570 : void
571 : fd_jtok_cstr( fd_jtok_t * j,
572 : char * out,
573 0 : ulong out_sz ) {
574 0 : if( out_sz ) out[0] = '\0';
575 0 : fd_jtok_str_t s;
576 0 : char const * save = j->cur;
577 0 : fd_jtok_str( j, &s );
578 0 : if( FD_UNLIKELY( j->err ) ) return;
579 0 : if( FD_UNLIKELY( fd_jtok_str_decode( &s, out, out_sz )<0L ) ) {
580 0 : j->cur = save;
581 0 : fail( j, FD_JTOK_ERR_RANGE );
582 0 : }
583 0 : }
584 :
585 : void
586 0 : fd_jtok_skip( fd_jtok_t * j ) {
587 0 : 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 0 : ulong * sz ) {
596 0 : if( FD_UNLIKELY( !begin( j ) ) ) return;
597 0 : char const * start = j->cur;
598 0 : if( FD_UNLIKELY( !skip_value( j ) ) ) return;
599 0 : j->pending = 0;
600 0 : *ptr = start;
601 0 : *sz = (ulong)( j->cur-start );
602 0 : }
603 :
604 : int
605 : fd_jtok_str_eq( fd_jtok_str_t const * s,
606 0 : char const * lit ) {
607 0 : char const * p = s->ptr;
608 0 : char const * end = s->ptr+s->sz;
609 0 : while( p<end ) {
610 0 : uchar unit[ 4 ];
611 0 : ulong n = decode_unit( &p, end, unit );
612 0 : if( FD_UNLIKELY( !n || !unit[0] ) ) return 0;
613 0 : for( ulong i=0UL; i<n; i++ ) if( (uchar)lit[i]!=unit[i] ) return 0;
614 0 : lit += n;
615 0 : }
616 0 : return *lit=='\0';
617 0 : }
|