LCOV - code coverage report
Current view: top level - ballet/json - fd_jtok.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 462 470 98.3 %
Date: 2026-11-12 04:25:03 Functions: 34 34 100.0 %

          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 : }

Generated by: LCOV version 1.14