LCOV - code coverage report
Current view: top level - ballet/json - fd_jtok.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 470 0.0 %
Date: 2026-10-23 17:13:25 Functions: 0 34 0.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           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 : }

Generated by: LCOV version 1.14