LCOV - code coverage report
Current view: top level - ballet/toml - fd_toml.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 528 991 53.3 %
Date: 2026-09-17 04:28:31 Functions: 54 71 76.1 %

          Line data    Source code
       1             : #define _DEFAULT_SOURCE
       2             : #include "fd_toml.h"
       3             : #include "../../util/fd_util.h"
       4             : #include <ctype.h>
       5             : #include <math.h>
       6             : 
       7             : /* Implementation note:
       8             : 
       9             :    The lexer/parser of fd_toml.c is a simpler backtracking recursive
      10             :    descent parser.  A minimal amount of lookahead tuning is implemented;
      11             :    mostly just fast failure paths.  Obvious performance wins are
      12             :    possible by adding more speculative lookaheads that lead the CPU down
      13             :    "happy paths" such as long strings of ASCII.
      14             : 
      15             :    The indexer into fd_pod blindly inserts using fd_pod_insert, which
      16             :    may not be the most efficient allocation strategy. */
      17             : 
      18             : /* fd_toml_cur_t is a cursor object.  It is safe to copy this object via
      19             :    assignment to implement backtracking. */
      20             : 
      21             : struct fd_toml_cur {
      22             :   ulong        lineno;
      23             :   char const * data;
      24             : };
      25             : 
      26             : typedef struct fd_toml_cur fd_toml_cur_t;
      27             : 
      28             : /* fd_toml_parser_t is the internal parser state.  It implements the
      29             :    lexer/parser itself, logic to unescape and buffer, and logic to
      30             :    compose the data into an fd_pod_t. */
      31             : 
      32             : struct fd_toml_parser {
      33             :   fd_toml_cur_t c;
      34             :   char const *  data_end;     /* points one past EOF */
      35             :   uchar *       pod;          /* pod provided by user */
      36             :   int           error;        /* hint: fatal pod error occurred */
      37             : 
      38             :   /* The current buffered string (either for both keys and values) */
      39             : 
      40             :   uchar *       scratch;      /* base of scratch buf */
      41             :   uchar *       scratch_cur;  /* next free byte in scratch buf */
      42             :   uchar *       scratch_end;  /* points one past scratch buf */
      43             : 
      44             :   /* Buffered keys */
      45             : 
      46             :   uint          key_len;
      47             :   char          key[ FD_TOML_PATH_MAX ];  /* cstr */
      48             : };
      49             : 
      50             : typedef struct fd_toml_parser fd_toml_parser_t;
      51             : 
      52             : /* Accumulate and insert data into fd_pod *****************************/
      53             : 
      54             : static void
      55        4605 : fd_toml_str_init( fd_toml_parser_t * parser ) {
      56        4605 :   parser->scratch_cur = parser->scratch;
      57        4605 : }
      58             : 
      59             : static int
      60             : fd_toml_str_append( fd_toml_parser_t * parser,
      61             :                     void const *       data,
      62           0 :                     ulong              sz ) {
      63             : 
      64           0 :   if( FD_UNLIKELY( parser->scratch_cur + sz >= parser->scratch_end ) ) {
      65           0 :     parser->error = FD_TOML_ERR_SCRATCH;
      66           0 :     return 0;
      67           0 :   }
      68             : 
      69           0 :   fd_memcpy( parser->scratch_cur, data, sz );
      70           0 :   parser->scratch_cur += sz;
      71           0 :   return 1;
      72           0 : }
      73             : 
      74             : static int
      75             : fd_toml_str_append_byte( fd_toml_parser_t * parser,
      76       54120 :                          int                c ) {
      77             : 
      78       54120 :   if( FD_UNLIKELY( parser->scratch_cur >= parser->scratch_end ) ) {
      79           0 :     parser->error = FD_TOML_ERR_SCRATCH;
      80           0 :     return 0;
      81           0 :   }
      82             : 
      83       54120 :   parser->scratch_cur[0] = (uchar)c;
      84       54120 :   parser->scratch_cur++;
      85       54120 :   return 1;
      86       54120 : }
      87             : 
      88             : /* fd_toml_str_append_utf8 appends the UTF-8 encoding of the given
      89             :    Unicode code point (<=UINT_MAX).  If rune is not a valid code point,
      90             :    writes the replacement code point instead. */
      91             : 
      92             : static int
      93             : fd_toml_str_append_utf8( fd_toml_parser_t * parser,
      94           0 :                          long               rune ) {
      95             : 
      96           0 :   if( FD_UNLIKELY( parser->scratch_cur + 4 >= parser->scratch_end ) ) {
      97           0 :     parser->error = FD_TOML_ERR_SCRATCH;
      98           0 :     return 0;
      99           0 :   }
     100             : 
     101           0 :   parser->scratch_cur = (uchar *)fd_cstr_append_utf8( (char *)parser->scratch_cur, (uint)rune );
     102           0 :   return 1;
     103           0 : }
     104             : 
     105             : /* Backtracking recursive-descent parser ******************************/
     106             : 
     107             : /* fd_toml_advance advances the parser cursor by 'n' chars.  Counts line
     108             :    numbers while advancing.  If you now for sure that the next 'n' chars
     109             :    don't contain any new lines, use fd_toml_advance_inline instead. */
     110             : 
     111             : static void /* consider aggressive inline */
     112             : fd_toml_advance( fd_toml_parser_t * parser,
     113       31605 :                  ulong              n ) {
     114             : 
     115       31605 :   char const * p    = parser->c.data;
     116       31605 :   char const * next = p + n;
     117       31605 :   if( FD_UNLIKELY( next > parser->data_end ) ) {
     118           0 :     FD_LOG_CRIT(( "fd_toml_advance out of bounds" ));
     119           0 :   }
     120             : 
     121             :   /* consider unroll */
     122       31605 :   ulong lines = 0UL;
     123       63210 :   for( ; p < next; p++ ) {
     124       31605 :     if( *p == '\n' ) lines++;
     125       31605 :   }
     126             : 
     127       31605 :   parser->c.lineno += lines;
     128       31605 :   parser->c.data    = next;
     129       31605 : }
     130             : 
     131             : static inline void
     132             : fd_toml_advance_inline( fd_toml_parser_t * parser,
     133     1168860 :                         ulong              n ) {
     134     1168860 :   parser->c.data += n;
     135     1168860 : }
     136             : 
     137             : static int
     138         732 : fd_toml_upsert_empty_pod( fd_toml_parser_t * parser ) {
     139         732 :   if( !fd_pod_query_subpod( parser->pod, parser->key ) ) {
     140         732 :     uchar   subpod_mem[ FD_POD_FOOTPRINT_MIN ];
     141         732 :     uchar * subpod = fd_pod_join( fd_pod_new( subpod_mem, FD_POD_FOOTPRINT_MIN ) );
     142         732 :     if( FD_UNLIKELY( !fd_pod_insert( parser->pod, parser->key, FD_POD_VAL_TYPE_SUBPOD, FD_POD_FOOTPRINT_MIN, subpod ) ) ) {
     143           0 :       parser->error = FD_TOML_ERR_POD;
     144           0 :       return 0;
     145           0 :     }
     146         732 :     fd_pod_delete( fd_pod_leave( subpod ) );
     147         732 :   }
     148         732 :   return 1;
     149         732 : }
     150             : 
     151             : /* fd_toml_avail returns the number of bytes available for parsing. */
     152             : 
     153             : static inline ulong
     154     1415268 : fd_toml_avail( fd_toml_parser_t const * parser ) {
     155     1415268 :   if( FD_UNLIKELY( parser->c.data > parser->data_end ) ) {
     156           0 :     FD_LOG_CRIT(( "Parse cursor is out of bounds" ));
     157           0 :   }
     158     1415268 :   return (ulong)parser->data_end - (ulong)parser->c.data;
     159     1415268 : }
     160             : 
     161             : #define SUB_PARSE( fn_call )                          \
     162       97245 :   __extension__ ({                                    \
     163       97245 :     fd_toml_cur_t const _macro_backtrack = parser->c; \
     164       97245 :     int ret = fn_call;                                \
     165       97245 :     if( !ret ) {                                      \
     166       66105 :       if( parser->error ) return 0;                   \
     167       66105 :       parser->c = _macro_backtrack;                   \
     168       66105 :     }                                                 \
     169       97245 :     ret;                                              \
     170       97245 :   })
     171             : 
     172             : #define EXPECT_CHAR(_c)                                      \
     173       29772 :   do {                                                       \
     174       29772 :     if( FD_UNLIKELY( !fd_toml_avail( parser )  ) ) return 0; \
     175       29772 :     if( FD_UNLIKELY( parser->c.data[0] != (_c) ) ) return 0; \
     176       29748 :     fd_toml_advance_inline( parser, 1UL );                   \
     177        2178 :   } while(0);
     178             : 
     179             : /* Begin fd_toml_parse_{...} functions.  All these functions attempt
     180             :    take a single argument, the parser.  Each function attempts to match
     181             :    a token and returns 1 on success.  If the token was not matched,
     182             :    returns 0.  On success, the cursor is advanced to one past the read
     183             :    token.  On failure, the cursor may arbitrarily advance within bounds.
     184             :    Parsers can gracefully recover from failure (backtrack) by restoring
     185             :    the fd_toml_cur_t object to its original state. */
     186             : 
     187             : static int fd_toml_parse_keyval( fd_toml_parser_t * parser );
     188             : static int fd_toml_parse_val   ( fd_toml_parser_t * parser );
     189             : 
     190             : /* ws = *wschar
     191             :    wschar =  %x20  ; Space
     192             :    wschar =/ %x09  ; Horizontal tab */
     193             : 
     194             : static int
     195       38664 : fd_toml_parse_ws( fd_toml_parser_t * parser ) {
     196             : 
     197      163512 :   while( fd_toml_avail( parser ) ) {
     198      163398 :     char c = parser->c.data[0];
     199      163398 :     if( c != ' ' && c != '\t' ) break;
     200      124848 :     fd_toml_advance_inline( parser, 1UL );
     201      124848 :   }
     202             : 
     203       38664 :   return 1;
     204       38664 : }
     205             : 
     206             : /* comment-start-symbol = %x23
     207             :    non-ascii = %x80-D7FF / %xE000-10FFFF
     208             :    non-eol = %x09 / %x20-7F / non-ascii
     209             : 
     210             :    comment = comment-start-symbol *non-eol */
     211             : 
     212             : static int
     213       25677 : fd_toml_parse_comment( fd_toml_parser_t * parser ) {
     214       25677 :   if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
     215       25563 :   if( FD_UNLIKELY( parser->c.data[0] != '#' ) ) return 0;
     216       20226 :   fd_toml_advance_inline( parser, 1UL );
     217             : 
     218      981726 :   while( fd_toml_avail( parser ) ) {
     219      981726 :     uint c = (uchar)parser->c.data[0];
     220      981726 :     if( FD_LIKELY( (c==0x09) |
     221      981726 :                    (c>=0x20 && c<0x7F) |
     222      981726 :                    (c>=0x80) ) ) {
     223      961500 :       fd_toml_advance_inline( parser, 1UL );
     224      961500 :     } else {
     225       20226 :       break;
     226       20226 :     }
     227      981726 :   }
     228             : 
     229       20226 :   return 1;
     230       25563 : }
     231             : 
     232             : /* quotation-mark = %x22 */
     233             : 
     234             : static int
     235       30057 : fd_toml_parse_quotation_mark( fd_toml_parser_t * parser ) {
     236       30057 :   if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
     237       30033 :   if( FD_UNLIKELY( parser->c.data[0] != '"' ) ) return 0;
     238        1806 :   fd_toml_advance_inline( parser, 1UL );
     239        1806 :   return 1;
     240       30033 : }
     241             : 
     242             : /* basic-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii */
     243             : 
     244             : static int
     245        7338 : fd_toml_parse_basic_unescaped( fd_toml_parser_t * parser ) {
     246             : 
     247        7338 :   if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
     248             : 
     249        7338 :   int c = (uchar)parser->c.data[0];
     250        7338 :   if( FD_LIKELY( (c==' ') | (c=='\t') |
     251        7338 :                  (c==0x21)            |
     252        7338 :                  (c>=0x23 && c<=0x5B)  |
     253        7338 :                  (c>=0x5D && c<=0x7E)  |
     254        7338 :                  (c>=0x80) ) ) { /* ok */ }
     255         903 :   else {
     256         903 :     return 0;
     257         903 :   }
     258             : 
     259        6435 :   fd_toml_str_append_byte( parser, (uchar)c );
     260        6435 :   fd_toml_advance( parser, 1UL );
     261        6435 :   return 1;
     262        7338 : }
     263             : 
     264             : /* fd_toml_xdigit converts a char to a hex digit.  Assumes that the
     265             :    char matches [0-9a-fA-F] */
     266             : 
     267             : FD_FN_CONST static inline uint
     268           0 : fd_toml_xdigit( int c ) {
     269           0 :   c = tolower( c );
     270           0 :   c = fd_int_if( c>'9', c-'a'+10, c-'0' );
     271           0 :   return (uint)c;
     272           0 : }
     273             : 
     274             : /* escaped = escape escape-seq-char
     275             :    escape = %x5C                   ; \
     276             :    escape-seq-char =  %x22         ; "    quotation mark  U+0022
     277             :    escape-seq-char =/ %x5C         ; \    reverse solidus U+005C
     278             :    escape-seq-char =/ %x62         ; b    backspace       U+0008
     279             :    escape-seq-char =/ %x66         ; f    form feed       U+000C
     280             :    escape-seq-char =/ %x6E         ; n    line feed       U+000A
     281             :    escape-seq-char =/ %x72         ; r    carriage return U+000D
     282             :    escape-seq-char =/ %x74         ; t    tab             U+0009
     283             :    escape-seq-char =/ %x75 4HEXDIG ; uXXXX                U+XXXX
     284             :    escape-seq-char =/ %x55 8HEXDIG ; UXXXXXXXX            U+XXXXXXXX */
     285             : 
     286             : static int
     287         903 : fd_toml_parse_escaped( fd_toml_parser_t * parser ) {
     288             : 
     289         903 :   if( FD_UNLIKELY( fd_toml_avail( parser ) < 2UL ) ) return 0;
     290         891 :   if( FD_UNLIKELY( parser->c.data[0] != '\\'     ) ) return 0;
     291           0 :   int kind = parser->c.data[1];
     292           0 :   fd_toml_advance_inline( parser, 2UL );
     293             : 
     294           0 :   int valid = 1;
     295           0 :   uint rune;
     296           0 :   switch( kind ) {
     297           0 :   case 'b':
     298           0 :     fd_toml_str_append_byte( parser, '\b' );
     299           0 :     return 1;
     300           0 :   case 'f':
     301           0 :     fd_toml_str_append_byte( parser, '\f' );
     302           0 :     return 1;
     303           0 :   case 'n':
     304           0 :     fd_toml_str_append_byte( parser, '\n' );
     305           0 :     return 1;
     306           0 :   case 'r':
     307           0 :     fd_toml_str_append_byte( parser, '\r' );
     308           0 :     return 1;
     309           0 :   case 't':
     310           0 :     fd_toml_str_append_byte( parser, '\t' );
     311           0 :     return 1;
     312           0 :   case '"':
     313           0 :   case '\\':
     314           0 :     fd_toml_str_append_byte( parser, kind );
     315           0 :     return 1;
     316           0 :   case 'u':
     317           0 :     if( FD_UNLIKELY( fd_toml_avail( parser ) < 4UL ) ) return 0;
     318           0 :     for( ulong j=0; j<4; j++ ) valid &= fd_isxdigit( parser->c.data[j] );
     319           0 :     if( FD_UNLIKELY( !valid ) ) return 0;
     320           0 :     rune  = ( fd_toml_xdigit( parser->c.data[0] )<<12 );
     321           0 :     rune |= ( fd_toml_xdigit( parser->c.data[1] )<< 8 );
     322           0 :     rune |= ( fd_toml_xdigit( parser->c.data[2] )<< 4 );
     323           0 :     rune |= ( fd_toml_xdigit( parser->c.data[3] )     );
     324           0 :     if( FD_UNLIKELY( !fd_toml_str_append_utf8( parser, rune ) ) ) return 0;
     325           0 :     fd_toml_advance_inline( parser, 4UL );
     326           0 :     return 1;
     327           0 :   case 'U':
     328           0 :     if( FD_UNLIKELY( fd_toml_avail( parser ) < 8UL ) ) return 0;
     329           0 :     for( ulong j=0; j<8; j++ ) valid &= fd_isxdigit( parser->c.data[j] );
     330           0 :     if( FD_UNLIKELY( !valid ) ) return 0;
     331           0 :     rune  = ( fd_toml_xdigit( parser->c.data[0] )<<28 );
     332           0 :     rune |= ( fd_toml_xdigit( parser->c.data[1] )<<24 );
     333           0 :     rune |= ( fd_toml_xdigit( parser->c.data[2] )<<20 );
     334           0 :     rune |= ( fd_toml_xdigit( parser->c.data[3] )<<16 );
     335           0 :     rune |= ( fd_toml_xdigit( parser->c.data[4] )<<12 );
     336           0 :     rune |= ( fd_toml_xdigit( parser->c.data[5] )<< 8 );
     337           0 :     rune |= ( fd_toml_xdigit( parser->c.data[6] )<< 4 );
     338           0 :     rune |= ( fd_toml_xdigit( parser->c.data[7] )     );
     339           0 :     if( FD_UNLIKELY( !fd_toml_str_append_utf8( parser, rune ) ) ) return 0;
     340           0 :     fd_toml_advance_inline( parser, 8UL );
     341           0 :     return 1;
     342           0 :   default:
     343           0 :     return 0;
     344           0 :   }
     345           0 : }
     346             : 
     347             : /* basic-char = basic-unescaped / escaped */
     348             : 
     349             : static int
     350        7338 : fd_toml_parse_basic_char( fd_toml_parser_t * parser ) {
     351        7338 :   if( FD_LIKELY( SUB_PARSE( fd_toml_parse_basic_unescaped( parser ) ) ) ) return 1;
     352         903 :   if( FD_LIKELY( SUB_PARSE( fd_toml_parse_escaped        ( parser ) ) ) ) return 1;
     353         903 :   return 0;
     354         903 : }
     355             : 
     356             : /* basic-string = quotation-mark *basic-char quotation-mark */
     357             : 
     358             : static int
     359       29154 : fd_toml_parse_basic_string( fd_toml_parser_t * parser ) {
     360       29154 :   if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_quotation_mark( parser ) ) ) ) return 0;
     361         903 :   fd_toml_str_init( parser );
     362        7338 :   while( SUB_PARSE( fd_toml_parse_basic_char( parser ) ) ) {}
     363         903 :   if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_quotation_mark( parser ) ) ) ) return 0;
     364         903 :   return 1;
     365         903 : }
     366             : 
     367             : /* apostrophe = %x27 ; ' apostrophe */
     368             : 
     369             : static int
     370       28251 : fd_toml_parse_apostrophe( fd_toml_parser_t * parser ) {
     371       28251 :   if( FD_UNLIKELY( !fd_toml_avail( parser )  ) ) return 0;
     372       28227 :   if( FD_UNLIKELY( parser->c.data[0] != '\'' ) ) return 0;
     373           0 :   fd_toml_advance_inline( parser, 1UL );
     374           0 :   return 1;
     375       28227 : }
     376             : 
     377             : /* literal-char = %x09 / %x20-26 / %x28-7E / non-ascii */
     378             : 
     379             : static int
     380           0 : fd_toml_parse_literal_char( fd_toml_parser_t * parser ) {
     381             : 
     382           0 :   if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
     383             : 
     384           0 :   int c = (uchar)parser->c.data[0];
     385           0 :   if( FD_LIKELY( (c==0x09) |
     386           0 :                  (c>=0x20 && c<=0x26) |
     387           0 :                  (c>=0x28 && c<=0x7E) |
     388           0 :                  (c>=0x80) ) ) { /* ok */ }
     389           0 :   else {
     390           0 :     return 0;
     391           0 :   }
     392             : 
     393           0 :   fd_toml_str_append_byte( parser, c );
     394           0 :   fd_toml_advance( parser, 1UL );
     395           0 :   return 1;
     396           0 : }
     397             : 
     398             : /* literal-string = apostrophe *literal-char apostrophe */
     399             : 
     400             : static int
     401       28251 : fd_toml_parse_literal_string( fd_toml_parser_t * parser ) {
     402       28251 :   if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_apostrophe( parser ) ) ) ) return 0;
     403           0 :   fd_toml_str_init( parser );
     404           0 :   while( SUB_PARSE( fd_toml_parse_literal_char( parser ) ) ) {}
     405           0 :   if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_apostrophe( parser ) ) ) ) return 0;
     406           0 :   return 1;
     407           0 : }
     408             : 
     409             : /* quoted-key = basic-string / literal-string */
     410             : 
     411             : static int
     412       26562 : fd_toml_parse_quoted_key( fd_toml_parser_t * parser ) {
     413       26562 :   if( FD_LIKELY( SUB_PARSE( fd_toml_parse_basic_string  ( parser ) ) ) ) return 1;
     414       26562 :   if( FD_LIKELY( SUB_PARSE( fd_toml_parse_literal_string( parser ) ) ) ) return 1;
     415       26562 :   return 0;
     416       26562 : }
     417             : 
     418             : /* unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _ */
     419             : 
     420             : static int
     421       73320 : fd_toml_is_unquoted_key_char( int c ) {
     422       73320 :   return (c>='A' && c<='Z') |
     423       73320 :          (c>='a' && c<='z') |
     424       73320 :          (c>='0' && c<='9') |
     425       73320 :          (c=='-') |
     426       73320 :          (c=='_');
     427       73320 : }
     428             : 
     429             : static int
     430       26562 : fd_toml_parse_unquoted_key( fd_toml_parser_t * parser ) {
     431       26562 :   if( FD_UNLIKELY( !fd_toml_avail( parser )           ) ) return 0;
     432       26538 :   int c = (uchar)parser->c.data[0];
     433       26538 :   if( FD_UNLIKELY( !fd_toml_is_unquoted_key_char( c ) ) ) return 0;
     434        3702 :   fd_toml_str_init( parser );
     435             : 
     436        3702 :   fd_toml_str_append_byte( parser, c );
     437        3702 :   fd_toml_advance_inline( parser, 1UL );
     438             : 
     439       46782 :   while( fd_toml_avail( parser ) ) {
     440       46782 :     c = (uchar)parser->c.data[0];
     441       46782 :     if( FD_LIKELY( fd_toml_is_unquoted_key_char( c ) ) ) {
     442       43080 :       fd_toml_str_append_byte( parser, c );
     443       43080 :       fd_toml_advance_inline( parser, 1UL );
     444       43080 :     } else {
     445        3702 :       break;
     446        3702 :     }
     447       46782 :   }
     448        3702 :   return 1;
     449       26538 : }
     450             : 
     451             : /* simple-key = quoted-key / unquoted-key */
     452             : 
     453             : static int
     454       26562 : fd_toml_parse_simple_key( fd_toml_parser_t * parser ) {
     455       26562 :   if( FD_LIKELY( SUB_PARSE( fd_toml_parse_quoted_key  ( parser ) ) ) ) goto add;
     456       26562 :   if( FD_LIKELY( SUB_PARSE( fd_toml_parse_unquoted_key( parser ) ) ) ) goto add;
     457       22860 :   return 0;
     458             : 
     459        3702 : add:
     460        3702 :   do {
     461        3702 :     uint  old_key_len = parser->key_len;
     462        3702 :     ulong suffix_len  = (ulong)parser->scratch_cur - (ulong)parser->scratch;
     463        3702 :     ulong key_len     = (ulong)old_key_len + suffix_len + 1;
     464        3702 :     if( FD_UNLIKELY( key_len > sizeof(parser->key)  ) ) {
     465           0 :       FD_LOG_WARNING(( "TOML parse error: key is too long: \"%.*s%.*s\"",
     466           0 :                       (int)old_key_len, parser->key,
     467           0 :                       (int)suffix_len,  (char *)parser->scratch ));
     468           0 :       parser->error = FD_TOML_ERR_KEY;
     469           0 :       return 0;
     470           0 :     }
     471             : 
     472        3702 :     char * key_cur = fd_cstr_init( parser->key + old_key_len );
     473        3702 :     key_cur = fd_cstr_append_text( key_cur, (char const *)parser->scratch, suffix_len );
     474        3702 :     fd_cstr_fini( key_cur );
     475        3702 :     parser->key_len = (uint)( key_cur - parser->key );
     476        3702 :     return 1;
     477        3702 :   } while(0);
     478        3702 : }
     479             : 
     480             : /* dot-sep = ws %x2E ws  ; . Period */
     481             : 
     482             : static int
     483        3702 : fd_toml_parse_dot_sep( fd_toml_parser_t * parser ) {
     484        3702 :   fd_toml_parse_ws( parser );
     485        3702 :   EXPECT_CHAR( '.' );
     486         714 :   fd_toml_parse_ws( parser );
     487         714 :   return 1;
     488        3702 : }
     489             : 
     490             : /* dotted-key = simple-key 1*( dot-sep simple-key ) */
     491             : 
     492             : static int
     493       25848 : fd_toml_parse_dotted_key( fd_toml_parser_t * parser ) {
     494       25848 :   if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_simple_key( parser ) ) ) ) return 0;
     495        3702 :   while( fd_toml_avail( parser ) ) {
     496        3702 :     if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_dot_sep( parser ) ) ) ) break;
     497             : 
     498             :     /* Add trailing dot */
     499         714 :     if( parser->key_len + 2 > sizeof(parser->key) ) {
     500           0 :       parser->error = FD_TOML_ERR_KEY;
     501           0 :       return 0;
     502           0 :     }
     503         714 :     parser->key[ parser->key_len++ ] = '.';
     504         714 :     parser->key[ parser->key_len   ] = '\x00';
     505             : 
     506         714 :     if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_simple_key( parser ) ) ) ) return 0;
     507         714 :   }
     508        2988 :   return 1;
     509        2988 : }
     510             : 
     511             : /* key = simple-key / dotted-key
     512             : 
     513             :    Doing simple-key *( dot-sep simple-key ) instead to simplify code */
     514             : 
     515             : static int
     516       25848 : fd_toml_parse_key( fd_toml_parser_t * parser ) {
     517       25848 :   return fd_toml_parse_dotted_key( parser );
     518       25848 : }
     519             : 
     520             : /* keyval-sep = ws %x3D ws */
     521             : 
     522             : static int
     523        2424 : fd_toml_parse_keyval_sep( fd_toml_parser_t * parser ) {
     524        2424 :   fd_toml_parse_ws( parser );
     525        2424 :   if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
     526        2424 :   if( FD_UNLIKELY( parser->c.data[0] != '=' ) ) return 0;
     527        2424 :   fd_toml_advance_inline( parser, 1UL );
     528        2424 :   fd_toml_parse_ws( parser );
     529        2424 :   return 1;
     530        2424 : }
     531             : 
     532             : /* ml-basic-string-delim = 3quotation-mark */
     533             : 
     534             : static int
     535        2592 : fd_toml_parse_ml_basic_string_delim( fd_toml_parser_t * parser ) {
     536        2592 :   if( FD_UNLIKELY( parser->c.data + 3 > parser->data_end ) ) return 0;
     537        2580 :   if( FD_UNLIKELY( ( parser->c.data[0] != '"' ) |
     538        2580 :                    ( parser->c.data[1] != '"' ) |
     539        2580 :                    ( parser->c.data[2] != '"' ) ) ) return 0;
     540           0 :   fd_toml_advance_inline( parser, 3UL );
     541           0 :   return 1;
     542        2580 : }
     543             : 
     544             : /* mlb-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii */
     545             : 
     546             : static int
     547           0 : fd_toml_parse_mlb_unescaped( fd_toml_parser_t * parser ) {
     548           0 :   return fd_toml_parse_basic_unescaped( parser );
     549           0 : }
     550             : 
     551             : /* mlb-escaped-nl = escape ws newline *( wschar / newline ) */
     552             : 
     553             : static int
     554           0 : fd_toml_parse_mlb_escaped_nl( fd_toml_parser_t * parser ) {
     555           0 :   if( FD_UNLIKELY( fd_toml_avail( parser ) < 2UL ) ) return 0;
     556           0 :   if( FD_UNLIKELY( parser->c.data[0] != '\\'     ) ) return 0;
     557           0 :   fd_toml_advance_inline( parser, 1UL );
     558           0 :   SUB_PARSE( fd_toml_parse_ws( parser ) );
     559           0 :   if( FD_UNLIKELY( !fd_toml_avail( parser )      ) ) return 0;
     560           0 :   if( FD_UNLIKELY( parser->c.data[0] != '\n'     ) ) return 0;
     561           0 :   while( fd_toml_avail( parser ) ) {
     562           0 :     int c = (uchar)parser->c.data[0];
     563           0 :     if( (c==' ') | (c=='\t') | (c=='\n') ) {
     564           0 :       fd_toml_advance( parser, 1UL );
     565           0 :     } else {
     566           0 :       break;
     567           0 :     }
     568           0 :   }
     569           0 :   return 1;
     570           0 : }
     571             : 
     572             : /* mlb-content = mlb-char / newline / mlb-escaped-nl
     573             :    mlb-char = mlb-unescaped / escaped */
     574             : 
     575             : static int
     576           0 : fd_toml_parse_mlb_content( fd_toml_parser_t * parser ) {
     577           0 :   if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
     578           0 :   if( FD_LIKELY( SUB_PARSE( fd_toml_parse_mlb_unescaped( parser ) ) ) ) return 1;
     579           0 :   if( FD_LIKELY( SUB_PARSE( fd_toml_parse_escaped      ( parser ) ) ) ) return 1;
     580           0 :   if( FD_LIKELY( parser->c.data[0] == '\n' ) ) {
     581           0 :     fd_toml_str_append_byte( parser, '\n' );
     582           0 :     fd_toml_advance( parser, 1UL );
     583           0 :     return 1;
     584           0 :   }
     585           0 :   if( FD_LIKELY( SUB_PARSE( fd_toml_parse_mlb_escaped_nl( parser ) ) ) ) return 1;
     586           0 :   return 0;
     587           0 : }
     588             : 
     589             : /* mlb-quotes = 1*2quotation-mark
     590             :    Note: This is used to allow normal quotes (", "") inside a multiline
     591             :          basic comment (""") */
     592             : 
     593             : static int
     594           0 : fd_toml_parse_mlb_quotes( fd_toml_parser_t * parser ) {
     595             : 
     596             :   /* Count number of quotes */
     597           0 :   char const * begin = parser->c.data;
     598           0 :   ulong quote_cnt = 0UL;
     599           0 :   while( fd_toml_avail( parser ) && parser->c.data[0] == '"' ) {
     600           0 :     fd_toml_advance_inline( parser, 1UL );
     601           0 :     quote_cnt++;
     602           0 :   }
     603             : 
     604           0 :   if( !quote_cnt || quote_cnt > 5 ) return 0;
     605           0 :   if( quote_cnt < 3 ) {
     606           0 :     fd_toml_str_append( parser, begin, quote_cnt );
     607           0 :     return 1;
     608           0 :   }
     609           0 :   if( quote_cnt==3 ) return 0;
     610             : 
     611             :   /* Backtrack by 3 quotes, as those might be the multiline */
     612           0 :   parser->c.data -= 3;
     613           0 :   quote_cnt      -= 3;
     614           0 :   fd_toml_str_append( parser, begin, quote_cnt );
     615           0 :   return 1;
     616           0 : }
     617             : 
     618             : /* ml-basic-body = *mlb-content *( mlb-quotes 1*mlb-content ) [ mlb-quotes ] */
     619             : 
     620             : static int
     621           0 : fd_toml_parse_ml_basic_body( fd_toml_parser_t * parser ) {
     622           0 :   while( SUB_PARSE( fd_toml_parse_mlb_content( parser ) ) ) {}
     623           0 :   for(;;) {
     624           0 :     if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_mlb_quotes ( parser ) ) ) ) break;
     625           0 :     if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_mlb_content( parser ) ) ) ) break;
     626           0 :     while( SUB_PARSE( fd_toml_parse_mlb_content( parser ) ) ) {}
     627           0 :   }
     628           0 :   SUB_PARSE( fd_toml_parse_mlb_quotes( parser ) );
     629           0 :   return 1;
     630           0 : }
     631             : 
     632             : /* ml-basic-string = ml-basic-string-delim [ newline ] ml-basic-body ml-basic-string-delim */
     633             : 
     634             : static int
     635        2592 : fd_toml_parse_ml_basic_string( fd_toml_parser_t * parser ) {
     636        2592 :   if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_ml_basic_string_delim( parser ) ) ) ) return 0;
     637           0 :   if( FD_UNLIKELY( !fd_toml_avail( parser ) ) )                                    return 0;
     638           0 :   if( parser->c.data[0] == '\n' ) {
     639           0 :     fd_toml_advance( parser, 1UL );
     640           0 :   }
     641           0 :   fd_toml_str_init( parser );
     642           0 :   if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_ml_basic_body        ( parser ) ) ) ) return 0;
     643           0 :   if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_ml_basic_string_delim( parser ) ) ) ) return 0;
     644           0 :   return 1;
     645           0 : }
     646             : 
     647             : /* mll-quotes = 1*2apostrophe
     648             :    Note: This is used to allow normal quotes (', '') inside a multiline
     649             :          literal comment (''') */
     650             : 
     651             : static int
     652           0 : fd_toml_parse_mll_quotes( fd_toml_parser_t * parser ) {
     653             : 
     654             :   /* Count number of quotes */
     655           0 :   char const * begin = parser->c.data;
     656           0 :   ulong quote_cnt = 0UL;
     657           0 :   while( fd_toml_avail( parser ) && parser->c.data[0] == '\'' ) {
     658           0 :     fd_toml_advance_inline( parser, 1UL );
     659           0 :     quote_cnt++;
     660           0 :   }
     661             : 
     662           0 :   if( !quote_cnt || quote_cnt > 5 ) return 0;
     663           0 :   if( quote_cnt < 3 ) {
     664           0 :     fd_toml_str_append( parser, begin, quote_cnt );
     665           0 :     return 1;
     666           0 :   }
     667           0 :   if( quote_cnt==3 ) return 0;
     668             : 
     669             :   /* Backtrack by 3 quotes, as those might be the multiline */
     670           0 :   parser->c.data -= 3;
     671           0 :   quote_cnt      -= 3;
     672           0 :   fd_toml_str_append( parser, begin, quote_cnt );
     673           0 :   return 1;
     674           0 : }
     675             : 
     676             : /* mll-content = mll-char / newline
     677             :    mll-char = %x09 / %x20-26 / %x28-7E / non-ascii */
     678             : 
     679             : static int
     680           0 : fd_toml_parse_mll_content( fd_toml_parser_t * parser ) {
     681           0 :   if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
     682             : 
     683           0 :   int c = (uchar)parser->c.data[0];
     684           0 :   if( FD_LIKELY( (c==0x09) |
     685           0 :                  (c>=0x20 && c<=0x26) |
     686           0 :                  (c>=0x28 && c<=0x7E) |
     687           0 :                  (c>=0x80) |
     688           0 :                  (c=='\n') ) ) {
     689             :     /* ok */
     690           0 :   } else {
     691           0 :     return 0;
     692           0 :   }
     693           0 :   if( FD_UNLIKELY( !fd_toml_str_append_byte( parser, c ) ) ) return 0;
     694             : 
     695           0 :   fd_toml_advance( parser, 1UL );
     696           0 :   return 1;
     697           0 : }
     698             : 
     699             : /* ml-literal-body = *mll-content *( mll-quotes 1*mll-content ) [ mll-quotes ] */
     700             : 
     701             : static int
     702           0 : fd_toml_parse_ml_literal_body( fd_toml_parser_t * parser ) {
     703           0 :   while( SUB_PARSE( fd_toml_parse_mll_content( parser ) ) ) {}
     704           0 :   for(;;) {
     705           0 :     if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_mll_quotes ( parser ) ) ) ) break;
     706           0 :     if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_mll_content( parser ) ) ) ) break;
     707           0 :     while( SUB_PARSE( fd_toml_parse_mll_content( parser ) ) ) {}
     708           0 :   }
     709           0 :   SUB_PARSE( fd_toml_parse_mll_quotes( parser ) );
     710           0 :   return 1;
     711           0 : }
     712             : 
     713             : /* ml-literal-string-delim = 3apostrophe */
     714             : 
     715             : static int
     716        1689 : fd_toml_parse_ml_literal_string_delim( fd_toml_parser_t * parser ) {
     717        1689 :   if( FD_UNLIKELY( parser->c.data + 3 > parser->data_end ) ) return 0;
     718        1677 :   if( FD_UNLIKELY( ( parser->c.data[0] != '\'' ) |
     719        1677 :                    ( parser->c.data[1] != '\'' ) |
     720        1677 :                    ( parser->c.data[2] != '\'' ) ) ) return 0;
     721           0 :   fd_toml_advance_inline( parser, 3UL );
     722           0 :   return 1;
     723        1677 : }
     724             : 
     725             : /* ml-literal-string = ml-literal-string-delim [ newline ] ml-literal-body
     726             :                        ml-literal-string-delim */
     727             : 
     728             : static int
     729        1689 : fd_toml_parse_ml_literal_string( fd_toml_parser_t * parser ) {
     730        1689 :   if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_ml_literal_string_delim( parser ) ) ) ) return 0;
     731           0 :   if( FD_UNLIKELY( !fd_toml_avail( parser ) ) )                                      return 0;
     732           0 :     if( parser->c.data[0] == '\n' ) {
     733           0 :     fd_toml_advance( parser, 1UL );
     734           0 :   }
     735           0 :   fd_toml_str_init( parser );
     736           0 :   if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_ml_literal_body        ( parser ) ) ) ) return 0;
     737           0 :   if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_ml_literal_string_delim( parser ) ) ) ) return 0;
     738           0 :   return 1;
     739           0 : }
     740             : 
     741             : /* string = ml-basic-string / basic-string / ml-literal-string / literal-string */
     742             : 
     743             : static int
     744        2592 : fd_toml_parse_string( fd_toml_parser_t * parser ) {
     745        2592 :   if( FD_LIKELY( SUB_PARSE( fd_toml_parse_ml_basic_string  ( parser ) ) ) ) goto add;
     746        2592 :   if( FD_LIKELY( SUB_PARSE( fd_toml_parse_basic_string     ( parser ) ) ) ) goto add;
     747        1689 :   if( FD_LIKELY( SUB_PARSE( fd_toml_parse_ml_literal_string( parser ) ) ) ) goto add;
     748        1689 :   if( FD_LIKELY( SUB_PARSE( fd_toml_parse_literal_string   ( parser ) ) ) ) goto add;
     749        1689 :   return 0;
     750         903 : add:
     751         903 :   if( FD_UNLIKELY( !fd_toml_str_append_byte( parser, 0 ) ) ) return 0;
     752         903 :   if( FD_UNLIKELY( !fd_pod_insert(
     753         903 :       parser->pod, parser->key, FD_POD_VAL_TYPE_CSTR,
     754         903 :       (ulong)parser->scratch_cur - (ulong)parser->scratch,
     755         903 :       (char *)parser->scratch ) ) ) {
     756           0 :     parser->error = FD_TOML_ERR_POD;
     757           0 :     return 0;
     758           0 :   }
     759         903 :   return 1;
     760         903 : }
     761             : 
     762             : /* boolean = true / false */
     763             : 
     764             : static int
     765        1689 : fd_toml_parse_boolean( fd_toml_parser_t * parser ) {
     766        1689 :   int boolv = 0;
     767        1689 :   if( parser->c.data + 4 > parser->data_end ) return 0;
     768        1656 :   if( 0==memcmp( parser->c.data, "true", 4 ) ) {
     769         192 :     fd_toml_advance_inline( parser, 4 );
     770         192 :     boolv = 1;
     771         192 :     goto add;
     772         192 :   }
     773        1464 :   if( parser->c.data + 5 > parser->data_end ) return 0;
     774        1452 :   if( 0==memcmp( parser->c.data, "false", 5 ) ) {
     775         174 :     fd_toml_advance_inline( parser, 5 );
     776         174 :     boolv = 0;
     777         174 :     goto add;
     778         174 :   }
     779        1278 :   return 0;
     780         366 : add:
     781         366 :   if( FD_UNLIKELY( !fd_pod_insert_int( parser->pod, parser->key, boolv ) ) ) {
     782           0 :     parser->error = FD_TOML_ERR_POD;
     783           0 :     return 0;
     784           0 :   }
     785         366 :   return 1;
     786         366 : }
     787             : 
     788             : /* ws-comment-newline = *( wschar / [ comment ] newline ) */
     789             : 
     790             : static int
     791         393 : fd_toml_parse_ws_comment_newline_inner( fd_toml_parser_t * parser ) {
     792         393 :   if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
     793         393 :   int c = (uchar)parser->c.data[0];
     794         393 :   if( FD_UNLIKELY( c == ' ' || c == '\t' ) ) {
     795           0 :     fd_toml_advance_inline( parser, 1UL );
     796           0 :     return 1;
     797           0 :   }
     798         786 :   SUB_PARSE( fd_toml_parse_comment( parser ) );
     799         786 :   if( FD_UNLIKELY( !fd_toml_avail( parser )  ) ) return 0;
     800         393 :   if( FD_UNLIKELY( parser->c.data[0] != '\n' ) ) return 0;
     801           0 :   fd_toml_advance( parser, 1UL );
     802           0 :   return 1;
     803         393 : }
     804             : 
     805             : static int
     806         393 : fd_toml_parse_ws_comment_newline( fd_toml_parser_t * parser ) {
     807         393 :   while( SUB_PARSE( fd_toml_parse_ws_comment_newline_inner( parser ) ) ) {}
     808         393 :   return 1;
     809         393 : }
     810             : 
     811             : /* array-values =  ws-comment-newline val ws-comment-newline array-sep array-values
     812             :    array-values =/ ws-comment-newline val ws-comment-newline [ array-sep ] */
     813             : 
     814             : static int
     815         168 : fd_toml_parse_array_values( fd_toml_parser_t * parser ) {
     816             : 
     817         168 :   uint   old_len     = parser->key_len;
     818         168 :   char * suffix_cstr = parser->key + parser->key_len;
     819         168 :   if( FD_UNLIKELY( suffix_cstr + 22 > parser->key + sizeof(parser->key) ) ) {
     820             :     /* array index might be OOB (see python3 -c 'print(len(str(1<<64)))') */
     821           0 :     parser->error = FD_TOML_ERR_KEY;
     822           0 :     return 0;
     823           0 :   }
     824             : 
     825             :   /* Unrolled tail recursion with backtracking */
     826             : 
     827         168 :   fd_toml_cur_t backtrack = parser->c;
     828         168 :   for( ulong j=0;; j++ ) {
     829         168 :     char * child_key = fd_cstr_append_char( suffix_cstr, '.' );
     830         168 :            child_key = fd_cstr_append_ulong_as_text( child_key, 0, 0, j, fd_ulong_base10_dig_cnt( j ) );
     831         168 :     fd_cstr_fini( child_key );
     832         168 :     parser->key_len = (uint)( child_key - parser->key );
     833             : 
     834         168 :     fd_toml_parse_ws_comment_newline( parser );
     835         168 :     if( FD_UNLIKELY( !fd_toml_parse_val( parser ) ) ) {
     836         111 :       parser->c = backtrack;
     837         111 :       break;
     838         111 :     }
     839             : 
     840          57 :     FD_LOG_DEBUG(( "Added key %s", parser->key ));
     841             : 
     842          57 :     fd_toml_parse_ws_comment_newline( parser );
     843             : 
     844          57 :     backtrack = parser->c;
     845          57 :     if( fd_toml_avail( parser ) && parser->c.data[0] == ',' ) {
     846           0 :       fd_toml_advance_inline( parser, 1UL );
     847          57 :     } else {
     848          57 :       break;
     849          57 :     }
     850           0 :     backtrack = parser->c;
     851           0 :   }
     852             : 
     853             :   /* Undo array index */
     854             : 
     855         168 :   fd_cstr_fini( suffix_cstr );
     856         168 :   parser->key_len = old_len;
     857         168 :   return 1;
     858         168 : }
     859             : 
     860             : /* array = array-open [ array-values ] ws-comment-newline array-close
     861             : 
     862             :    array-open =  %x5B ; [
     863             :    array-close = %x5D ; ] */
     864             : 
     865             : static int
     866        1323 : fd_toml_parse_array( fd_toml_parser_t * parser ) {
     867        1323 :   uint key_len = parser->key_len;
     868             : 
     869        1323 :   EXPECT_CHAR( '[' );
     870         168 :   fd_toml_upsert_empty_pod( parser );
     871         168 :   SUB_PARSE( fd_toml_parse_array_values      ( parser ) );
     872         168 :   SUB_PARSE( fd_toml_parse_ws_comment_newline( parser ) );
     873         168 :   EXPECT_CHAR( ']' );
     874             : 
     875         168 :   parser->key_len        = key_len;
     876         168 :   parser->key[ key_len ] = 0;
     877             : 
     878         168 :   return 1;
     879         168 : }
     880             : 
     881             : /* inline-table-sep   = ws %x2C ws  ; , Comma */
     882             : 
     883             : static int
     884           0 : fd_toml_parse_inline_table_sep( fd_toml_parser_t * parser ) {
     885           0 :   fd_toml_parse_ws( parser );
     886           0 :   if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
     887           0 :   if( FD_UNLIKELY( parser->c.data[0] != ',' ) ) return 0;
     888           0 :   fd_toml_advance_inline( parser, 1UL );
     889           0 :   fd_toml_parse_ws( parser );
     890           0 :   return 1;
     891           0 : }
     892             : 
     893             : /* inline-table-keyvals = keyval [ inline-table-sep inline-table-keyvals ] */
     894             : 
     895             : static int
     896           0 : fd_toml_parse_inline_table_keyvals( fd_toml_parser_t * parser ) {
     897             : 
     898             :   /* Unrolled tail recursion with backtracking */
     899             : 
     900           0 :   if( !fd_toml_parse_keyval( parser ) ) return 0;
     901           0 :   fd_toml_cur_t backtrack = parser->c;
     902           0 :   for(;;) {
     903           0 :     if( !fd_toml_parse_inline_table_sep( parser ) ) {
     904           0 :       parser->c = backtrack;
     905           0 :       break;
     906           0 :     }
     907           0 :     if( !fd_toml_parse_keyval( parser ) ) return 0;
     908           0 :     backtrack = parser->c;
     909           0 :   }
     910             : 
     911           0 :   return 1;
     912           0 : }
     913             : 
     914             : /* inline-table = inline-table-open [ inline-table-keyvals ] inline-table-close
     915             : 
     916             :    inline-table-open  = %x7B ws ; {
     917             :    inline-table-close = ws %x7D ; } */
     918             : 
     919             : static int
     920        1155 : fd_toml_parse_inline_table( fd_toml_parser_t * parser ) {
     921             : 
     922        1155 :   EXPECT_CHAR( '{' );
     923           0 :   fd_toml_parse_ws( parser );
     924             : 
     925           0 :   uint old_key_len = parser->key_len;
     926           0 :   if( parser->key_len + 2 > sizeof(parser->key) ) {
     927           0 :     parser->error = FD_TOML_ERR_KEY;
     928           0 :     return 0;
     929           0 :   }
     930             : 
     931           0 :   parser->key[ parser->key_len   ] = '\x00';
     932           0 :   fd_toml_upsert_empty_pod( parser );
     933             : 
     934           0 :   parser->key[ parser->key_len++ ] = '.';
     935           0 :   parser->key[ parser->key_len   ] = '\x00';
     936             : 
     937           0 :   while( SUB_PARSE( fd_toml_parse_inline_table_keyvals( parser ) ) ) {}
     938             : 
     939           0 :   fd_toml_parse_ws( parser );
     940           0 :   EXPECT_CHAR( '}' );
     941             : 
     942           0 :   parser->key_len            = old_key_len;
     943           0 :   parser->key[ old_key_len ] = '\x00';
     944           0 :   return 1;
     945           0 : }
     946             : 
     947             : /* dec-int = [ minus / plus ] unsigned-dec-int
     948             :    unsigned-dec-int = DIGIT / digit1-9 1*( DIGIT / underscore DIGIT ) */
     949             : 
     950             : struct fd_toml_dec {
     951             :   ulong res;
     952             :   uint  len;
     953             :   uchar neg : 1;
     954             : };
     955             : 
     956             : typedef struct fd_toml_dec fd_toml_dec_t;
     957             : 
     958             : /* zero-prefixable-int = DIGIT *( DIGIT / underscore DIGIT )
     959             : 
     960             :    fd_toml_parse_zero_prefixable_int parses [0-9](_[0-9]|[0-9])*
     961             :    Assumes the first digit has been validated prior to call. */
     962             : 
     963             : static int
     964             : fd_toml_parse_zero_prefixable_int( fd_toml_parser_t * parser,
     965        1917 :                                    fd_toml_dec_t *    dec ) {
     966             : 
     967        1917 :   uint  len    = 0U;
     968        1917 :   ulong digits = 0UL;
     969        1917 :   int allow_underscore = 0;
     970        7392 :   for(;;) {
     971        7392 :     if( FD_UNLIKELY( allow_underscore && parser->c.data[0] == '_' ) ) {
     972         135 :       allow_underscore = 0;
     973         135 :       fd_toml_advance_inline( parser, 1UL );
     974         135 :       if( FD_UNLIKELY( !fd_toml_avail( parser )         ) ) return 0;
     975         135 :       if( FD_UNLIKELY( !fd_isdigit( parser->c.data[0] ) ) ) return 0;
     976        7257 :     } else {
     977        7257 :       int digit = (uchar)parser->c.data[0];
     978        7257 :       if( FD_UNLIKELY(
     979        7257 :           __builtin_umull_overflow( digits, 10, &digits ) ||
     980        7257 :           __builtin_uaddl_overflow( digits, (ulong)( digit - '0' ), &digits ) ) ) {
     981           0 :         parser->error = FD_TOML_ERR_RANGE;
     982           0 :         return 0;
     983           0 :       }
     984        7257 :       len++;
     985        7257 :       fd_toml_advance_inline( parser, 1UL );
     986        7257 :       if( !fd_toml_avail( parser ) ) break;
     987        7191 :       if( !fd_isdigit( parser->c.data[0] ) && parser->c.data[0] != '_' ) break;
     988        5340 :       allow_underscore = 1;
     989        5340 :     }
     990        7392 :   }
     991             : 
     992        1917 :   dec->res = digits;
     993        1917 :   dec->len = len;
     994        1917 :   return 1;
     995        1917 : }
     996             : 
     997             : static int
     998             : fd_toml_parse_dec_int_( fd_toml_parser_t * parser,
     999        2262 :                         fd_toml_dec_t *    dec ) {
    1000        2262 :   if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
    1001        2262 :   int c = (uchar)parser->c.data[0];
    1002             : 
    1003        2262 :   int neg = 0;
    1004        2262 :   switch( c ) {
    1005           9 :   case '-':
    1006           9 :     neg = 1;
    1007           9 :     __attribute__((fallthrough));
    1008           9 :   case '+':
    1009           9 :     fd_toml_advance_inline( parser, 1UL );
    1010           9 :     break;
    1011        2262 :   }
    1012             : 
    1013        2262 :   if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
    1014        2262 :   int first_digit = (uchar)parser->c.data[0];
    1015        2262 :   if( first_digit == '0' ) {
    1016         177 :     dec->res = 0UL;
    1017         177 :     dec->neg = !!neg;
    1018         177 :     fd_toml_advance_inline( parser, 1UL );
    1019         177 :     return 1;
    1020         177 :   }
    1021             : 
    1022        2085 :   if( FD_UNLIKELY( first_digit<='0' || first_digit>'9' ) ) return 0;
    1023             : 
    1024        1863 :   dec->neg = !!neg;
    1025        1863 :   return fd_toml_parse_zero_prefixable_int( parser, dec );
    1026        2085 : }
    1027             : 
    1028             : static int
    1029        1107 : fd_toml_parse_dec_int( fd_toml_parser_t * parser ) {
    1030        1107 :   fd_toml_dec_t dec = {0};
    1031        1107 :   if( FD_UNLIKELY( !fd_toml_parse_dec_int_( parser, &dec ) ) ) return 0;
    1032         996 :   long val = (long)dec.res;
    1033         996 :        val = fd_long_if( dec.neg, -val, val );
    1034         996 :   if( FD_UNLIKELY( !fd_pod_insert_long( parser->pod, parser->key, val ) ) ) {
    1035           0 :     parser->error = FD_TOML_ERR_POD;
    1036           0 :     return 0;
    1037           0 :   }
    1038         996 :   return 1;
    1039         996 : }
    1040             : 
    1041             : /* hex-int = hex-prefix HEXDIG *( HEXDIG / underscore HEXDIG ) */
    1042             : 
    1043             : static int
    1044        1107 : fd_toml_parse_hex_int( fd_toml_parser_t * parser ) {
    1045        1107 :   if( FD_UNLIKELY( fd_toml_avail( parser ) < 3       ) ) return 0;
    1046        1095 :   if( FD_UNLIKELY( parser->c.data[0] != '0'          ) ) return 0;
    1047          78 :   if( FD_UNLIKELY( parser->c.data[1] != 'x'          ) ) return 0;
    1048           0 :   if( FD_UNLIKELY( !fd_isxdigit( parser->c.data[2] ) ) ) return 0;  /* at least one digit */
    1049           0 :   fd_toml_advance_inline( parser, 2UL );
    1050             : 
    1051           0 :   ulong res = 0UL;
    1052           0 :   int allow_underscore = 0;
    1053           0 :   for(;;) {
    1054           0 :     int digit = (uchar)parser->c.data[0];
    1055           0 :     if( FD_UNLIKELY( allow_underscore && digit == '_' ) ) {
    1056           0 :       allow_underscore = 0;
    1057           0 :       fd_toml_advance_inline( parser, 1UL );
    1058           0 :       if( FD_UNLIKELY( !fd_toml_avail( parser )          ) ) return 0;
    1059           0 :       if( FD_UNLIKELY( !fd_isxdigit( parser->c.data[0] ) ) ) return 0;
    1060           0 :     } else {
    1061           0 :       if( !fd_isxdigit( digit ) ) break;
    1062           0 :       if( FD_UNLIKELY( res>>60 ) ) {
    1063           0 :         parser->error = FD_TOML_ERR_RANGE;
    1064           0 :         return 0;
    1065           0 :       }
    1066           0 :       res <<= 4;
    1067           0 :       res  |= fd_toml_xdigit( digit );
    1068           0 :       fd_toml_advance_inline( parser, 1UL );
    1069           0 :       if( !fd_toml_avail( parser ) ) break;
    1070           0 :       allow_underscore = 1;
    1071           0 :     }
    1072           0 :   }
    1073             : 
    1074           0 :   if( FD_UNLIKELY( !fd_pod_insert_long( parser->pod, parser->key, (long)res ) ) ) {
    1075           0 :     parser->error = FD_TOML_ERR_POD;
    1076           0 :     return 0;
    1077           0 :   }
    1078             : 
    1079           0 :   return 1;
    1080           0 : }
    1081             : 
    1082             : /* oct-int = oct-prefix digit0-7 *( digit0-7 / underscore digit0-7 ) */
    1083             : 
    1084             : static inline int
    1085           0 : fd_toml_is_odigit( int c ) {
    1086           0 :   return c>='0' && c<'8';
    1087           0 : }
    1088             : 
    1089             : static int
    1090        1107 : fd_toml_parse_oct_int( fd_toml_parser_t * parser ) {
    1091        1107 :   if( FD_UNLIKELY( fd_toml_avail( parser ) < 3             ) ) return 0;
    1092        1095 :   if( FD_UNLIKELY( parser->c.data[0] != '0'                ) ) return 0;
    1093          78 :   if( FD_UNLIKELY( parser->c.data[1] != 'o'                ) ) return 0;
    1094           0 :   if( FD_UNLIKELY( !fd_toml_is_odigit( parser->c.data[2] ) ) ) return 0;  /* at least one digit */
    1095           0 :   fd_toml_advance_inline( parser, 2UL );
    1096             : 
    1097           0 :   ulong res = 0UL;
    1098           0 :   int allow_underscore = 0;
    1099           0 :   for(;;) {
    1100           0 :     int digit = (uchar)parser->c.data[0];
    1101           0 :     if( allow_underscore && digit == '_' ) {
    1102           0 :       allow_underscore = 0;
    1103           0 :       fd_toml_advance_inline( parser, 1UL );
    1104           0 :       if( FD_UNLIKELY( !fd_toml_avail( parser )                ) ) return 0;
    1105           0 :       if( FD_UNLIKELY( !fd_toml_is_odigit( parser->c.data[0] ) ) ) return 0;
    1106           0 :     } else {
    1107           0 :       if( !fd_toml_is_odigit( digit ) ) break;
    1108           0 :       if( FD_UNLIKELY( res>>61 ) ) {
    1109           0 :         parser->error = FD_TOML_ERR_RANGE;
    1110           0 :         return 0;
    1111           0 :       }
    1112           0 :       res <<= 3;
    1113           0 :       res  |= (ulong)( digit - '0' );
    1114           0 :       fd_toml_advance_inline( parser, 1UL );
    1115           0 :       if( !fd_toml_avail( parser ) ) break;
    1116           0 :       allow_underscore = 1;
    1117           0 :     }
    1118           0 :   }
    1119             : 
    1120           0 :   if( FD_UNLIKELY( !fd_pod_insert_long( parser->pod, parser->key, (long)res ) ) ) {
    1121           0 :     parser->error = FD_TOML_ERR_POD;
    1122           0 :     return 0;
    1123           0 :   }
    1124             : 
    1125           0 :   return 1;
    1126           0 : }
    1127             : 
    1128             : /* bin-int = bin-prefix digit0-1 *( digit0-1 / underscore digit0-1 ) */
    1129             : 
    1130             : static inline int
    1131           0 : fd_toml_is_bdigit( int c ) {
    1132           0 :   return c=='0' || c=='1';
    1133           0 : }
    1134             : 
    1135             : static int
    1136        1107 : fd_toml_parse_bin_int( fd_toml_parser_t * parser ) {
    1137        1107 :   if( FD_UNLIKELY( fd_toml_avail( parser ) < 3             ) ) return 0;
    1138        1095 :   if( FD_UNLIKELY( parser->c.data[0] != '0'                ) ) return 0;
    1139          78 :   if( FD_UNLIKELY( parser->c.data[1] != 'b'                ) ) return 0;
    1140           0 :   if( FD_UNLIKELY( !fd_toml_is_bdigit( parser->c.data[2] ) ) ) return 0;  /* at least one digit */
    1141           0 :   fd_toml_advance_inline( parser, 2UL );
    1142             : 
    1143             :   /* TODO OVERFLOW DETECTION */
    1144             : 
    1145           0 :   ulong res = 0UL;
    1146           0 :   int allow_underscore = 0;
    1147           0 :   for(;;) {
    1148           0 :     int digit = (uchar)parser->c.data[0];
    1149           0 :     if( FD_UNLIKELY( allow_underscore && digit == '_' ) ) {
    1150           0 :       allow_underscore = 0;
    1151           0 :       fd_toml_advance_inline( parser, 1UL );
    1152           0 :       if( FD_UNLIKELY( !fd_toml_avail( parser )                ) ) return 0;
    1153           0 :       if( FD_UNLIKELY( !fd_toml_is_bdigit( parser->c.data[0] ) ) ) return 0;
    1154           0 :     } else {
    1155           0 :       if( !fd_toml_is_bdigit( digit ) ) break;
    1156           0 :       if( FD_UNLIKELY( res>>63 ) ) {
    1157           0 :         parser->error = FD_TOML_ERR_RANGE;
    1158           0 :         return 0;
    1159           0 :       }
    1160           0 :       res <<= 1;
    1161           0 :       res  |= (ulong)( digit - '0' );
    1162           0 :       fd_toml_advance_inline( parser, 1UL );
    1163           0 :       if( !fd_toml_avail( parser ) ) break;
    1164           0 :       allow_underscore = 1;
    1165           0 :     }
    1166           0 :   }
    1167             : 
    1168           0 :   if( FD_UNLIKELY( !fd_pod_insert_long( parser->pod, parser->key, (long)res ) ) ) {
    1169           0 :     parser->error = FD_TOML_ERR_POD;
    1170           0 :     return 0;
    1171           0 :   }
    1172             : 
    1173           0 :   return 1;
    1174           0 : }
    1175             : 
    1176             : /* integer = dec-int / hex-int / oct-int / bin-int */
    1177             : 
    1178             : static int
    1179        1107 : fd_toml_parse_integer( fd_toml_parser_t * parser ) {
    1180        1107 :   if( SUB_PARSE( fd_toml_parse_hex_int( parser ) ) ) return 1;
    1181        1107 :   if( SUB_PARSE( fd_toml_parse_oct_int( parser ) ) ) return 1;
    1182        1107 :   if( SUB_PARSE( fd_toml_parse_bin_int( parser ) ) ) return 1;
    1183        1107 :   if( SUB_PARSE( fd_toml_parse_dec_int( parser ) ) ) return 1;
    1184         111 :   return 0;
    1185        1107 : }
    1186             : 
    1187             : /* exp = "e" float-exp-part
    1188             :    float-exp-part = [ minus / plus ] zero-prefixable-int */
    1189             : 
    1190             : static int
    1191             : fd_toml_parse_exp( fd_toml_parser_t * parser,
    1192        1035 :                    fd_toml_dec_t *    exp ) {
    1193        1035 :   if( FD_UNLIKELY( fd_toml_avail( parser ) < 2 ) ) return 0;
    1194         990 :   switch( parser->c.data[0] ) {
    1195           9 :     case 'e': case 'E': break;
    1196         981 :     default:            return 0;
    1197         990 :   }
    1198           9 :   fd_toml_advance_inline( parser, 1UL );
    1199             : 
    1200           9 :   switch( parser->c.data[0] ) {
    1201           3 :   case '-':
    1202           3 :     exp->neg = 1;
    1203           3 :     __attribute__((fallthrough));
    1204           3 :   case '+':
    1205           3 :     fd_toml_advance_inline( parser, 1UL );
    1206           3 :     if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
    1207           3 :     break;
    1208           9 :   }
    1209             : 
    1210           9 :   int first_digit = (uchar)parser->c.data[0];
    1211           9 :   if( FD_UNLIKELY( first_digit<'0' || first_digit>'9'                ) ) return 0;
    1212           9 :   if( FD_UNLIKELY( !fd_toml_parse_zero_prefixable_int( parser, exp ) ) ) return 0;
    1213           9 :   return 1;
    1214           9 : }
    1215             : 
    1216             : /* frac = decimal-point zero-prefixable-int
    1217             :    decimal-point = %x2E */
    1218             : 
    1219             : static int
    1220             : fd_toml_parse_frac( fd_toml_parser_t * parser,
    1221        1035 :                     fd_toml_dec_t *    frac ) {
    1222        1035 :   if( FD_UNLIKELY( fd_toml_avail( parser ) < 2                        ) ) return 0;
    1223        1029 :   if( FD_UNLIKELY( parser->c.data[0] != '.'                           ) ) return 0;
    1224          45 :   fd_toml_advance_inline( parser, 1UL );
    1225             : 
    1226          45 :   int first_digit = (uchar)parser->c.data[0];
    1227          45 :   if( FD_UNLIKELY( first_digit<'0' || first_digit>'9'                 ) ) return 0;
    1228          45 :   if( FD_UNLIKELY( !fd_toml_parse_zero_prefixable_int( parser, frac ) ) ) return 0;
    1229          45 :   return 1;
    1230          45 : }
    1231             : 
    1232             : /* float = float-int-part ( exp / frac [ exp ] )
    1233             :    float-int-part = dec-int */
    1234             : 
    1235             : static int
    1236        1155 : fd_toml_parse_float_normal( fd_toml_parser_t * parser ) {
    1237             : 
    1238        1155 :   fd_toml_dec_t stem = {0};
    1239        1155 :   if( FD_UNLIKELY( !fd_toml_parse_dec_int_( parser, &stem ) ) ) return 0;
    1240        1044 :   if( FD_UNLIKELY( !fd_toml_avail( parser )                 ) ) return 0;
    1241        1035 :   float res = (float)stem.res;
    1242             : 
    1243        1035 :   int ok = 0;
    1244        1035 :   fd_toml_dec_t frac_dec = {0};
    1245        1035 :   if( SUB_PARSE( fd_toml_parse_frac( parser, &frac_dec ) ) ) {
    1246          45 :     float frac = (float)frac_dec.res;
    1247         132 :     while( frac_dec.len-- ) frac /= 10.0f;  /* use pow? */
    1248          45 :     res += frac;
    1249          45 :     ok   = 1;
    1250          45 :   }
    1251             : 
    1252        1035 :   fd_toml_dec_t exp_dec = {0};
    1253        1035 :   if( !SUB_PARSE( fd_toml_parse_exp( parser, &exp_dec ) ) ) {
    1254        1026 :     if( FD_LIKELY( ok ) ) goto parsed;
    1255         987 :     return 0;
    1256        1026 :   }
    1257             : 
    1258           9 :   float exp = powf( exp_dec.neg ? 0.1f : 10.0f, (float)exp_dec.res );
    1259           9 :   res *= exp;
    1260             : 
    1261          48 : parsed:
    1262          48 :   if( stem.neg ) res = -res;
    1263          48 :   if( FD_UNLIKELY( !fd_pod_insert_float( parser->pod, parser->key, res ) ) ) {
    1264           0 :     parser->error = FD_TOML_ERR_POD;
    1265           0 :     return 0;
    1266           0 :   }
    1267          48 :   return 1;
    1268          48 : }
    1269             : 
    1270             : /* special-float = [ minus / plus ] ( inf / nan )
    1271             :    inf = %x69.6e.66  ; inf
    1272             :    nan = %x6e.61.6e  ; nan */
    1273             : 
    1274             : static int
    1275        1107 : fd_float_parse_float_special( fd_toml_parser_t * parser ) {
    1276        1107 :   if( FD_UNLIKELY( fd_toml_avail( parser ) < 3 ) ) return 0;
    1277        1095 :   int c = (uchar)parser->c.data[0];
    1278             : 
    1279        1095 :   switch( c ) {
    1280           0 :   case '-': case '+':
    1281           0 :     fd_toml_advance_inline( parser, 1UL );
    1282           0 :     if( FD_UNLIKELY( fd_toml_avail( parser ) < 3 ) ) return 0;
    1283           0 :     break;
    1284        1095 :   }
    1285             : 
    1286        1095 :   char const * str = parser->c.data;
    1287        1095 :   fd_toml_advance_inline( parser, 3UL );
    1288             : 
    1289        1095 :   if( 0==strncasecmp( str, "inf", 3 ) ) {
    1290           0 :     FD_LOG_WARNING(( "TOML parse error: float infinity is unsupported" ));
    1291           0 :     parser->error = FD_TOML_ERR_RANGE;
    1292           0 :     return 0;
    1293           0 :   }
    1294             : 
    1295        1095 :   if( 0==strncasecmp( str, "nan", 3 ) ) {
    1296           0 :     FD_LOG_WARNING(( "TOML parse error: float NaN is unsupported" ));
    1297           0 :     parser->error = FD_TOML_ERR_RANGE;
    1298           0 :     return 1;
    1299           0 :   }
    1300             : 
    1301        1095 :   return 0;
    1302        1095 : }
    1303             : 
    1304             : /* float = float-int-part ( exp / frac [ exp ] )
    1305             :    float =/ special-float */
    1306             : 
    1307             : static int
    1308        1155 : fd_toml_parse_float( fd_toml_parser_t * parser ) {
    1309        1155 :   if( SUB_PARSE( fd_toml_parse_float_normal  ( parser ) ) ) return 1;
    1310        1107 :   if( SUB_PARSE( fd_float_parse_float_special( parser ) ) ) return 1;
    1311        1107 :   return 0;
    1312        1107 : }
    1313             : 
    1314             : /* val = string / boolean / array / inline-table / float / integer */
    1315             : 
    1316             : static int
    1317        2592 : fd_toml_parse_val( fd_toml_parser_t * parser ) {
    1318             :   /* consider some lookahead for better performance */
    1319        2592 :   if( SUB_PARSE( fd_toml_parse_string      ( parser ) ) ) return 1;
    1320        1689 :   if( SUB_PARSE( fd_toml_parse_boolean     ( parser ) ) ) return 1;
    1321        1323 :   if( SUB_PARSE( fd_toml_parse_array       ( parser ) ) ) return 1;
    1322        1155 :   if( SUB_PARSE( fd_toml_parse_inline_table( parser ) ) ) return 1;
    1323             :   // if( SUB_PARSE( fd_toml_parse_date_time   ( parser ) ) ) return 1; /* not supported */
    1324             :   /* NOTE: float and integer have a common dec-int prefix -- dedup for better performance */
    1325        1155 :   if( SUB_PARSE( fd_toml_parse_float       ( parser ) ) ) return 1;
    1326        1107 :   if( SUB_PARSE( fd_toml_parse_integer     ( parser ) ) ) return 1;
    1327         111 :   return 0;
    1328        1107 : }
    1329             : 
    1330             : /* keyval = key keyval-sep val */
    1331             : 
    1332             : static int
    1333       25284 : fd_toml_parse_keyval( fd_toml_parser_t * parser ) {
    1334       25284 :   uint old_key_len = parser->key_len;
    1335       25284 :   if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_key( parser ) ) ) ) return 0;
    1336             : 
    1337        2424 :   if( FD_UNLIKELY( fd_pod_query( parser->pod, parser->key, NULL )==FD_POD_SUCCESS ) ) {
    1338           0 :     FD_LOG_WARNING(( "TOML parse error: duplicate key: \"%s\"", parser->key ));
    1339           0 :     parser->error = FD_TOML_ERR_DUP;
    1340           0 :     return 0;
    1341           0 :   }
    1342             : 
    1343        2424 :   if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_keyval_sep( parser ) ) ) ) return 0;
    1344        2424 :   if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_val       ( parser ) ) ) ) return 0;
    1345             : 
    1346        2424 :   FD_LOG_DEBUG(( "Added key %s", parser->key ));
    1347        2424 :   parser->key[ old_key_len ] = 0;
    1348        2424 :   parser->key_len            = old_key_len;
    1349        2424 :   return 1;
    1350        2424 : }
    1351             : 
    1352             : /* std-table = std-table-open key std-table-close
    1353             : 
    1354             :    std-table-open  = %x5B ws     ; [ Left square bracket
    1355             :    std-table-close = ws %x5D     ; ] Right square bracket */
    1356             : 
    1357             : static int
    1358       22860 : fd_toml_parse_std_table( fd_toml_parser_t * parser ) {
    1359       22860 :   EXPECT_CHAR( '[' );
    1360         564 :   fd_toml_parse_ws( parser );
    1361             : 
    1362         564 :   parser->key[ parser->key_len = 0 ] = 0;
    1363         564 :   if( FD_UNLIKELY( !fd_toml_parse_key( parser ) ) ) return 0;
    1364             :   // FIXME: consider blocking duplicate tables?
    1365             :   //if( FD_UNLIKELY( fd_pod_query( parser->pod, parser->key, NULL )==FD_POD_SUCCESS ) ) {
    1366             :   //  FD_LOG_WARNING(( "Duplicate table: \"%s\"", parser->key ));
    1367             :   //  parser->error = FD_TOML_ERR_DUP;
    1368             :   //  return 0;
    1369             :   //}
    1370         564 :   FD_LOG_DEBUG(( "Added table %.*s", (int)parser->key_len, parser->key ));
    1371             : 
    1372         564 :   fd_toml_parse_ws( parser );
    1373         564 :   EXPECT_CHAR( ']' );
    1374         564 :   return 1;
    1375         564 : }
    1376             : 
    1377             : /* array-table = array-table = array-table-open key array-table-close
    1378             : 
    1379             :    array-table-open  = %x5B.5B ws  ; [[ Double left square bracket
    1380             :    array-table-close = ws %x5D.5D  ; ]] Double right square bracket */
    1381             : 
    1382             : static int
    1383       22860 : fd_toml_parse_array_table( fd_toml_parser_t * parser ) {
    1384       22860 :   if( fd_toml_avail( parser ) < 2UL ) return 0;
    1385       22836 :   if( ( parser->c.data[0] != '[' ) |
    1386       22836 :       ( parser->c.data[1] != '[' ) ) return 0;
    1387           0 :   fd_toml_advance_inline( parser, 2UL );
    1388             : 
    1389           0 :   fd_toml_parse_ws( parser );
    1390             : 
    1391             :   /* Set parser->key to path to array */
    1392             : 
    1393           0 :   parser->key[ parser->key_len = 0 ] = 0;
    1394           0 :   if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_key( parser ) ) ) ) return 0;
    1395             : 
    1396             :   /* Count number of predecessors */
    1397             : 
    1398           0 :   ulong idx = 0UL;
    1399           0 :   uchar const * subpod = fd_pod_query_subpod( parser->pod, parser->key );
    1400           0 :   if( subpod ) {
    1401           0 :     idx = fd_pod_cnt( subpod );
    1402           0 :   }
    1403             : 
    1404             :   /* Append array index to path */
    1405             : 
    1406           0 :   char * key_c = parser->key + parser->key_len;
    1407           0 :   if( FD_UNLIKELY( key_c + 22 > parser->key + sizeof(parser->key) ) ) {
    1408             :     /* array index might be OOB (see python3 -c 'print(len(str(1<<64)))') */
    1409           0 :     parser->error = FD_TOML_ERR_KEY;
    1410           0 :     return 0;
    1411           0 :   }
    1412           0 :   key_c = fd_cstr_append_char( key_c, '.' );
    1413           0 :   key_c = fd_cstr_append_ulong_as_text( key_c, 0, 0, idx, fd_ulong_base10_dig_cnt( idx ) );
    1414           0 :   fd_cstr_fini( key_c );
    1415           0 :   parser->key_len = (uint)( key_c - parser->key );
    1416             : 
    1417           0 :   FD_LOG_DEBUG(( "Added array table %.*s", (int)parser->key_len, parser->key ));
    1418             : 
    1419             :   /* Continue parsing */
    1420             : 
    1421           0 :   fd_toml_parse_ws( parser );
    1422             : 
    1423           0 :   if( FD_UNLIKELY( fd_toml_avail( parser ) < 2UL ) ) return 0;
    1424           0 :   if( FD_UNLIKELY( ( parser->c.data[0] != ']' ) |
    1425           0 :                    ( parser->c.data[1] != ']' ) ) ) return 0;
    1426           0 :   fd_toml_advance_inline( parser, 2UL );
    1427           0 :   return 1;
    1428           0 : }
    1429             : 
    1430             : /* table = std-table / array-table */
    1431             : 
    1432             : static int
    1433       22860 : fd_toml_parse_table( fd_toml_parser_t * parser ) {
    1434       22860 :   if( SUB_PARSE( fd_toml_parse_array_table( parser ) ) ) goto add;
    1435       22860 :   if( SUB_PARSE( fd_toml_parse_std_table  ( parser ) ) ) goto add;
    1436       22296 :   return 0;
    1437         564 : add:
    1438         564 :   fd_toml_upsert_empty_pod( parser );
    1439             :   /* Add trailing dot */
    1440         564 :   if( parser->key_len + 2 > sizeof(parser->key) ) {
    1441           0 :     parser->error = FD_TOML_ERR_KEY;
    1442           0 :     return 0;
    1443           0 :   }
    1444         564 :   parser->key[ parser->key_len++ ] = '.';
    1445         564 :   parser->key[ parser->key_len   ] = '\x00';
    1446         564 :   return 1;
    1447         564 : }
    1448             : 
    1449             : /* expression =  ws [ comment ]
    1450             :    expression =/ ws keyval ws [ comment ]
    1451             :    expression =/ ws table ws [ comment ] */
    1452             : 
    1453             : static int
    1454       25284 : fd_toml_parse_expression( fd_toml_parser_t * parser ) {
    1455             : 
    1456       25284 :   fd_toml_parse_ws( parser );
    1457             : 
    1458       25284 :   if( FD_LIKELY( SUB_PARSE( fd_toml_parse_keyval( parser ) ) ) ) {
    1459        2424 :     fd_toml_parse_ws( parser );
    1460        2424 :   }
    1461       22860 :   else if( FD_LIKELY( SUB_PARSE( fd_toml_parse_table( parser ) ) ) ) {
    1462         564 :     fd_toml_parse_ws( parser );
    1463         564 :   }
    1464             : 
    1465       25284 :   SUB_PARSE( fd_toml_parse_comment( parser ) );
    1466       25284 :   return 1;
    1467       25284 : }
    1468             : 
    1469             : /* toml = expression *( newline expression ) */
    1470             : 
    1471             : static int
    1472         114 : fd_toml_parse_toml( fd_toml_parser_t * parser ) {
    1473             : 
    1474         114 :   if( FD_UNLIKELY( !fd_toml_parse_expression( parser ) ) ) return 0;
    1475             : 
    1476       25284 :   for(;;) {
    1477       25284 :     if( FD_UNLIKELY( parser->error             ) ) break;
    1478       25284 :     if( FD_UNLIKELY( !fd_toml_avail( parser )  ) ) break;
    1479       25170 :     if( FD_UNLIKELY( parser->c.data[0] != '\n' ) ) break;
    1480       25170 :     fd_toml_advance( parser, 1UL );
    1481       25170 :     if( FD_UNLIKELY( !fd_toml_parse_expression( parser ) ) ) return 0;
    1482       25170 :   }
    1483             : 
    1484         114 :   return 1;
    1485         114 : }
    1486             : 
    1487             : int
    1488             : fd_toml_parse( void const *         toml,
    1489             :                ulong                toml_sz,
    1490             :                uchar *              pod,
    1491             :                uchar *              scratch,
    1492             :                ulong                scratch_sz,
    1493         114 :                fd_toml_err_info_t * opt_err ) {
    1494             : 
    1495         114 :   static fd_toml_err_info_t _dummy_err[1];
    1496         114 :   if( !opt_err ) opt_err = _dummy_err;
    1497         114 :   opt_err->line = 0UL;
    1498             : 
    1499         114 :   if( FD_UNLIKELY( !toml_sz    ) ) return FD_TOML_SUCCESS;
    1500         114 :   if( FD_UNLIKELY( !scratch_sz ) ) {
    1501           0 :     FD_LOG_WARNING(( "zero scratch_sz" ));
    1502           0 :     return FD_TOML_ERR_SCRATCH;
    1503           0 :   }
    1504             : 
    1505         114 :   fd_toml_parser_t parser[1] = {{
    1506         114 :     .c = {
    1507         114 :       .data   = toml,
    1508         114 :       .lineno = 1UL,
    1509         114 :     },
    1510         114 :     .data_end    = (char const *)toml + toml_sz,
    1511         114 :     .pod         = pod,
    1512         114 :     .scratch     = scratch,
    1513         114 :     .scratch_cur = scratch,
    1514         114 :     .scratch_end = scratch + scratch_sz
    1515         114 :   }};
    1516             : 
    1517         114 :   int ok = fd_toml_parse_toml( parser );
    1518         114 :   opt_err->line = parser->c.lineno;
    1519             : 
    1520         114 :   if( FD_UNLIKELY( (!ok) | (fd_toml_avail( parser ) > 0) ) ) {
    1521           0 :     return fd_int_if( !!parser->error, parser->error, FD_TOML_ERR_PARSE );
    1522           0 :   }
    1523             : 
    1524         114 :   return FD_TOML_SUCCESS;
    1525         114 : }
    1526             : 
    1527             : FD_FN_CONST char const *
    1528           0 : fd_toml_strerror( int err ) {
    1529           0 :   switch( err ) {
    1530           0 :   case FD_TOML_SUCCESS:     return "success";
    1531           0 :   case FD_TOML_ERR_POD:     return "out of memory in output pod";
    1532           0 :   case FD_TOML_ERR_SCRATCH: return "out of memory in scratch region";
    1533           0 :   case FD_TOML_ERR_KEY:     return "oversize key";
    1534           0 :   case FD_TOML_ERR_DUP:     return "duplicate key";
    1535           0 :   case FD_TOML_ERR_RANGE:   return "integer overflow";
    1536           0 :   case FD_TOML_ERR_PARSE:   return "parse failure";
    1537           0 :   default:                  return "unknown error";
    1538           0 :   }
    1539           0 : }

Generated by: LCOV version 1.14