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