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_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 1422 : fd_toml_str_init( fd_toml_parser_t * parser ) {
56 1422 : parser->scratch_cur = parser->scratch;
57 1422 : }
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 17862 : int c ) {
77 :
78 17862 : 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 17862 : parser->scratch_cur[0] = (uchar)c;
84 17862 : parser->scratch_cur++;
85 17862 : return 1;
86 17862 : }
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 10716 : ulong n ) {
114 :
115 10716 : char const * p = parser->c.data;
116 10716 : char const * next = p + n;
117 10716 : 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 10716 : ulong lines = 0UL;
123 21432 : for( ; p < next; p++ ) {
124 10716 : if( *p == '\n' ) lines++;
125 10716 : }
126 :
127 10716 : parser->c.lineno += lines;
128 10716 : parser->c.data = next;
129 10716 : }
130 :
131 : static inline void
132 : fd_toml_advance_inline( fd_toml_parser_t * parser,
133 438351 : ulong n ) {
134 438351 : parser->c.data += n;
135 438351 : }
136 :
137 : static int
138 228 : fd_toml_upsert_empty_pod( fd_toml_parser_t * parser ) {
139 228 : if( !fd_pod_query_subpod( parser->pod, parser->key ) ) {
140 228 : uchar subpod_mem[ FD_POD_FOOTPRINT_MIN ];
141 228 : uchar * subpod = fd_pod_join( fd_pod_new( subpod_mem, FD_POD_FOOTPRINT_MIN ) );
142 228 : 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 228 : fd_pod_delete( fd_pod_leave( subpod ) );
147 228 : }
148 228 : return 1;
149 228 : }
150 :
151 : /* fd_toml_avail returns the number of bytes available for parsing. */
152 :
153 : static inline ulong
154 527586 : fd_toml_avail( fd_toml_parser_t const * parser ) {
155 527586 : if( FD_UNLIKELY( parser->c.data > parser->data_end ) ) {
156 0 : FD_LOG_CRIT(( "Parse cursor is out of bounds" ));
157 0 : }
158 527586 : return (ulong)parser->data_end - (ulong)parser->c.data;
159 527586 : }
160 :
161 : #define SUB_PARSE( fn_call ) \
162 35937 : __extension__ ({ \
163 35937 : fd_toml_cur_t const _macro_backtrack = parser->c; \
164 35937 : int ret = fn_call; \
165 35937 : if( !ret ) { \
166 25596 : if( parser->error ) return 0; \
167 25596 : parser->c = _macro_backtrack; \
168 25596 : } \
169 35937 : ret; \
170 35937 : })
171 :
172 : #define EXPECT_CHAR(_c) \
173 10647 : do { \
174 10647 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0; \
175 10647 : if( FD_UNLIKELY( parser->c.data[0] != (_c) ) ) return 0; \
176 10641 : fd_toml_advance_inline( parser, 1UL ); \
177 564 : } 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 13695 : fd_toml_parse_ws( fd_toml_parser_t * parser ) {
196 :
197 56226 : while( fd_toml_avail( parser ) ) {
198 56211 : char c = parser->c.data[0];
199 56211 : if( c != ' ' && c != '\t' ) break;
200 42531 : fd_toml_advance_inline( parser, 1UL );
201 42531 : }
202 :
203 13695 : return 1;
204 13695 : }
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 9465 : fd_toml_parse_comment( fd_toml_parser_t * parser ) {
214 9465 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
215 9450 : if( FD_UNLIKELY( parser->c.data[0] != '#' ) ) return 0;
216 7524 : fd_toml_advance_inline( parser, 1UL );
217 :
218 374688 : while( fd_toml_avail( parser ) ) {
219 374688 : uint c = (uchar)parser->c.data[0];
220 374688 : if( FD_LIKELY( (c==0x09) |
221 374688 : (c>=0x20 && c<0x7F) |
222 374688 : (c>=0x80) ) ) {
223 367164 : fd_toml_advance_inline( parser, 1UL );
224 367164 : } else {
225 7524 : break;
226 7524 : }
227 374688 : }
228 :
229 7524 : return 1;
230 9450 : }
231 :
232 : /* quotation-mark = %x22 */
233 :
234 : static int
235 10833 : fd_toml_parse_quotation_mark( fd_toml_parser_t * parser ) {
236 10833 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
237 10827 : if( FD_UNLIKELY( parser->c.data[0] != '"' ) ) return 0;
238 570 : fd_toml_advance_inline( parser, 1UL );
239 570 : return 1;
240 10827 : }
241 :
242 : /* basic-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii */
243 :
244 : static int
245 1653 : fd_toml_parse_basic_unescaped( fd_toml_parser_t * parser ) {
246 :
247 1653 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
248 :
249 1653 : int c = (uchar)parser->c.data[0];
250 1653 : if( FD_LIKELY( (c==' ') | (c=='\t') |
251 1653 : (c==0x21) |
252 1653 : (c>=0x23 && c<=0x5B) |
253 1653 : (c>=0x5D && c<=0x7E) |
254 1653 : (c>=0x80) ) ) { /* ok */ }
255 285 : else {
256 285 : return 0;
257 285 : }
258 :
259 1368 : fd_toml_str_append_byte( parser, (uchar)c );
260 1368 : fd_toml_advance( parser, 1UL );
261 1368 : return 1;
262 1653 : }
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 285 : fd_toml_parse_escaped( fd_toml_parser_t * parser ) {
288 :
289 285 : if( FD_UNLIKELY( fd_toml_avail( parser ) < 2UL ) ) return 0;
290 282 : 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 &= ( !!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 &= ( !!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 1653 : fd_toml_parse_basic_char( fd_toml_parser_t * parser ) {
351 1653 : if( FD_LIKELY( SUB_PARSE( fd_toml_parse_basic_unescaped( parser ) ) ) ) return 1;
352 285 : if( FD_LIKELY( SUB_PARSE( fd_toml_parse_escaped ( parser ) ) ) ) return 1;
353 285 : return 0;
354 285 : }
355 :
356 : /* basic-string = quotation-mark *basic-char quotation-mark */
357 :
358 : static int
359 10548 : fd_toml_parse_basic_string( fd_toml_parser_t * parser ) {
360 10548 : if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_quotation_mark( parser ) ) ) ) return 0;
361 285 : fd_toml_str_init( parser );
362 1653 : while( SUB_PARSE( fd_toml_parse_basic_char( parser ) ) ) {}
363 285 : if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_quotation_mark( parser ) ) ) ) return 0;
364 285 : return 1;
365 285 : }
366 :
367 : /* apostrophe = %x27 ; ' apostrophe */
368 :
369 : static int
370 10263 : fd_toml_parse_apostrophe( fd_toml_parser_t * parser ) {
371 10263 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
372 10257 : if( FD_UNLIKELY( parser->c.data[0] != '\'' ) ) return 0;
373 0 : fd_toml_advance_inline( parser, 1UL );
374 0 : return 1;
375 10257 : }
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 10263 : fd_toml_parse_literal_string( fd_toml_parser_t * parser ) {
402 10263 : 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 9651 : fd_toml_parse_quoted_key( fd_toml_parser_t * parser ) {
413 9651 : if( FD_LIKELY( SUB_PARSE( fd_toml_parse_basic_string ( parser ) ) ) ) return 1;
414 9651 : if( FD_LIKELY( SUB_PARSE( fd_toml_parse_literal_string( parser ) ) ) ) return 1;
415 9651 : return 0;
416 9651 : }
417 :
418 : /* unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _ */
419 :
420 : static int
421 25854 : fd_toml_is_unquoted_key_char( int c ) {
422 25854 : return (c>='A' && c<='Z') |
423 25854 : (c>='a' && c<='z') |
424 25854 : (c>='0' && c<='9') |
425 25854 : (c=='-') |
426 25854 : (c=='_');
427 25854 : }
428 :
429 : static int
430 9651 : fd_toml_parse_unquoted_key( fd_toml_parser_t * parser ) {
431 9651 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
432 9645 : int c = (uchar)parser->c.data[0];
433 9645 : if( FD_UNLIKELY( !fd_toml_is_unquoted_key_char( c ) ) ) return 0;
434 1137 : fd_toml_str_init( parser );
435 :
436 1137 : fd_toml_str_append_byte( parser, c );
437 1137 : fd_toml_advance_inline( parser, 1UL );
438 :
439 16209 : while( fd_toml_avail( parser ) ) {
440 16209 : c = (uchar)parser->c.data[0];
441 16209 : if( FD_LIKELY( fd_toml_is_unquoted_key_char( c ) ) ) {
442 15072 : fd_toml_str_append_byte( parser, c );
443 15072 : fd_toml_advance_inline( parser, 1UL );
444 15072 : } else {
445 1137 : break;
446 1137 : }
447 16209 : }
448 1137 : return 1;
449 9645 : }
450 :
451 : /* simple-key = quoted-key / unquoted-key */
452 :
453 : static int
454 9651 : fd_toml_parse_simple_key( fd_toml_parser_t * parser ) {
455 9651 : if( FD_LIKELY( SUB_PARSE( fd_toml_parse_quoted_key ( parser ) ) ) ) goto add;
456 9651 : if( FD_LIKELY( SUB_PARSE( fd_toml_parse_unquoted_key( parser ) ) ) ) goto add;
457 8514 : return 0;
458 :
459 1137 : add:
460 1137 : do {
461 1137 : uint old_key_len = parser->key_len;
462 1137 : ulong suffix_len = (ulong)parser->scratch_cur - (ulong)parser->scratch;
463 1137 : ulong key_len = (ulong)old_key_len + suffix_len + 1;
464 1137 : 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 1137 : char * key_cur = fd_cstr_init( parser->key + old_key_len );
473 1137 : key_cur = fd_cstr_append_text( key_cur, (char const *)parser->scratch, suffix_len );
474 1137 : fd_cstr_fini( key_cur );
475 1137 : parser->key_len = (uint)( key_cur - parser->key );
476 1137 : return 1;
477 1137 : } while(0);
478 1137 : }
479 :
480 : /* dot-sep = ws %x2E ws ; . Period */
481 :
482 : static int
483 1137 : fd_toml_parse_dot_sep( fd_toml_parser_t * parser ) {
484 1137 : fd_toml_parse_ws( parser );
485 1137 : EXPECT_CHAR( '.' );
486 108 : fd_toml_parse_ws( parser );
487 108 : return 1;
488 1137 : }
489 :
490 : /* dotted-key = simple-key 1*( dot-sep simple-key ) */
491 :
492 : static int
493 9543 : fd_toml_parse_dotted_key( fd_toml_parser_t * parser ) {
494 9543 : if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_simple_key( parser ) ) ) ) return 0;
495 1137 : while( fd_toml_avail( parser ) ) {
496 1137 : if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_dot_sep( parser ) ) ) ) break;
497 :
498 : /* Add trailing dot */
499 108 : if( parser->key_len + 2 > sizeof(parser->key) ) {
500 0 : parser->error = FD_TOML_ERR_KEY;
501 0 : return 0;
502 0 : }
503 108 : parser->key[ parser->key_len++ ] = '.';
504 108 : parser->key[ parser->key_len ] = '\x00';
505 :
506 108 : if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_simple_key( parser ) ) ) ) return 0;
507 108 : }
508 1029 : return 1;
509 1029 : }
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 9543 : fd_toml_parse_key( fd_toml_parser_t * parser ) {
517 9543 : return fd_toml_parse_dotted_key( parser );
518 9543 : }
519 :
520 : /* keyval-sep = ws %x3D ws */
521 :
522 : static int
523 849 : fd_toml_parse_keyval_sep( fd_toml_parser_t * parser ) {
524 849 : fd_toml_parse_ws( parser );
525 849 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
526 849 : if( FD_UNLIKELY( parser->c.data[0] != '=' ) ) return 0;
527 849 : fd_toml_advance_inline( parser, 1UL );
528 849 : fd_toml_parse_ws( parser );
529 849 : return 1;
530 849 : }
531 :
532 : /* ml-basic-string-delim = 3quotation-mark */
533 :
534 : static int
535 897 : fd_toml_parse_ml_basic_string_delim( fd_toml_parser_t * parser ) {
536 897 : if( FD_UNLIKELY( parser->c.data + 3 > parser->data_end ) ) return 0;
537 897 : if( FD_UNLIKELY( ( parser->c.data[0] != '"' ) |
538 897 : ( parser->c.data[1] != '"' ) |
539 897 : ( parser->c.data[2] != '"' ) ) ) return 0;
540 0 : fd_toml_advance_inline( parser, 3UL );
541 0 : return 1;
542 897 : }
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 897 : fd_toml_parse_ml_basic_string( fd_toml_parser_t * parser ) {
636 897 : 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 612 : fd_toml_parse_ml_literal_string_delim( fd_toml_parser_t * parser ) {
717 612 : if( FD_UNLIKELY( parser->c.data + 3 > parser->data_end ) ) return 0;
718 612 : if( FD_UNLIKELY( ( parser->c.data[0] != '\'' ) |
719 612 : ( parser->c.data[1] != '\'' ) |
720 612 : ( parser->c.data[2] != '\'' ) ) ) return 0;
721 0 : fd_toml_advance_inline( parser, 3UL );
722 0 : return 1;
723 612 : }
724 :
725 : /* ml-literal-string = ml-literal-string-delim [ newline ] ml-literal-body
726 : ml-literal-string-delim */
727 :
728 : static int
729 612 : fd_toml_parse_ml_literal_string( fd_toml_parser_t * parser ) {
730 612 : 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 897 : fd_toml_parse_string( fd_toml_parser_t * parser ) {
745 897 : if( FD_LIKELY( SUB_PARSE( fd_toml_parse_ml_basic_string ( parser ) ) ) ) goto add;
746 897 : if( FD_LIKELY( SUB_PARSE( fd_toml_parse_basic_string ( parser ) ) ) ) goto add;
747 612 : if( FD_LIKELY( SUB_PARSE( fd_toml_parse_ml_literal_string( parser ) ) ) ) goto add;
748 612 : if( FD_LIKELY( SUB_PARSE( fd_toml_parse_literal_string ( parser ) ) ) ) goto add;
749 612 : return 0;
750 285 : add:
751 285 : if( FD_UNLIKELY( !fd_toml_str_append_byte( parser, 0 ) ) ) return 0;
752 285 : if( FD_UNLIKELY( !fd_pod_insert(
753 285 : parser->pod, parser->key, FD_POD_VAL_TYPE_CSTR,
754 285 : (ulong)parser->scratch_cur - (ulong)parser->scratch,
755 285 : (char *)parser->scratch ) ) ) {
756 0 : parser->error = FD_TOML_ERR_POD;
757 0 : return 0;
758 0 : }
759 285 : return 1;
760 285 : }
761 :
762 : /* boolean = true / false */
763 :
764 : static int
765 612 : fd_toml_parse_boolean( fd_toml_parser_t * parser ) {
766 612 : int boolv = 0;
767 612 : if( parser->c.data + 4 > parser->data_end ) return 0;
768 612 : if( 0==memcmp( parser->c.data, "true", 4 ) ) {
769 84 : fd_toml_advance_inline( parser, 4 );
770 84 : boolv = 1;
771 84 : goto add;
772 84 : }
773 528 : if( parser->c.data + 5 > parser->data_end ) return 0;
774 528 : if( 0==memcmp( parser->c.data, "false", 5 ) ) {
775 120 : fd_toml_advance_inline( parser, 5 );
776 120 : boolv = 0;
777 120 : goto add;
778 120 : }
779 408 : return 0;
780 204 : add:
781 204 : 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 204 : return 1;
786 204 : }
787 :
788 : /* ws-comment-newline = *( wschar / [ comment ] newline ) */
789 :
790 : static int
791 102 : fd_toml_parse_ws_comment_newline_inner( fd_toml_parser_t * parser ) {
792 102 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
793 102 : int c = (uchar)parser->c.data[0];
794 102 : if( FD_UNLIKELY( c == ' ' || c == '\t' ) ) {
795 0 : fd_toml_advance_inline( parser, 1UL );
796 0 : return 1;
797 0 : }
798 204 : SUB_PARSE( fd_toml_parse_comment( parser ) );
799 102 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
800 102 : if( FD_UNLIKELY( parser->c.data[0] != '\n' ) ) return 0;
801 0 : fd_toml_advance( parser, 1UL );
802 0 : return 1;
803 102 : }
804 :
805 : static int
806 102 : fd_toml_parse_ws_comment_newline( fd_toml_parser_t * parser ) {
807 102 : while( SUB_PARSE( fd_toml_parse_ws_comment_newline_inner( parser ) ) ) {}
808 0 : return 1;
809 102 : }
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 48 : fd_toml_parse_array_values( fd_toml_parser_t * parser ) {
816 :
817 48 : uint old_len = parser->key_len;
818 48 : char * suffix_cstr = parser->key + parser->key_len;
819 48 : 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 48 : fd_toml_cur_t backtrack = parser->c;
828 48 : for( ulong j=0;; j++ ) {
829 48 : char * child_key = fd_cstr_append_char( suffix_cstr, '.' );
830 48 : child_key = fd_cstr_append_ulong_as_text( child_key, 0, 0, j, fd_ulong_base10_dig_cnt( j ) );
831 48 : fd_cstr_fini( child_key );
832 48 : parser->key_len = (uint)( child_key - parser->key );
833 :
834 48 : fd_toml_parse_ws_comment_newline( parser );
835 48 : if( FD_UNLIKELY( !fd_toml_parse_val( parser ) ) ) {
836 42 : parser->c = backtrack;
837 42 : break;
838 42 : }
839 :
840 6 : FD_LOG_DEBUG(( "Added key %s", parser->key ));
841 :
842 6 : fd_toml_parse_ws_comment_newline( parser );
843 :
844 6 : backtrack = parser->c;
845 6 : if( fd_toml_avail( parser ) && parser->c.data[0] == ',' ) {
846 0 : fd_toml_advance_inline( parser, 1UL );
847 6 : } else {
848 6 : break;
849 6 : }
850 0 : backtrack = parser->c;
851 0 : }
852 :
853 : /* Undo array index */
854 :
855 48 : fd_cstr_fini( suffix_cstr );
856 48 : parser->key_len = old_len;
857 48 : return 1;
858 48 : }
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 408 : fd_toml_parse_array( fd_toml_parser_t * parser ) {
867 408 : uint key_len = parser->key_len;
868 :
869 408 : EXPECT_CHAR( '[' );
870 48 : fd_toml_upsert_empty_pod( parser );
871 48 : SUB_PARSE( fd_toml_parse_array_values ( parser ) );
872 48 : SUB_PARSE( fd_toml_parse_ws_comment_newline( parser ) );
873 48 : EXPECT_CHAR( ']' );
874 :
875 48 : parser->key_len = key_len;
876 48 : parser->key[ key_len ] = 0;
877 :
878 48 : return 1;
879 48 : }
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 360 : fd_toml_parse_inline_table( fd_toml_parser_t * parser ) {
921 :
922 360 : 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 564 : fd_toml_dec_t * dec ) {
966 :
967 564 : uint len;
968 564 : ulong digits = 0UL;
969 564 : int allow_underscore = 0;
970 2304 : for( len=0;; len++ ) {
971 2304 : if( FD_UNLIKELY( allow_underscore && parser->c.data[0] == '_' ) ) {
972 36 : allow_underscore = 0;
973 36 : fd_toml_advance_inline( parser, 1UL );
974 36 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
975 36 : if( FD_UNLIKELY( !isdigit( parser->c.data[0] ) ) ) return 0;
976 2268 : } else {
977 2268 : int digit = (uchar)parser->c.data[0];
978 2268 : if( FD_UNLIKELY(
979 2268 : __builtin_umull_overflow( digits, 10, &digits ) ||
980 2268 : __builtin_uaddl_overflow( digits, (ulong)( digit - '0' ), &digits ) ) ) {
981 0 : parser->error = FD_TOML_ERR_RANGE;
982 0 : return 0;
983 0 : }
984 2268 : fd_toml_advance_inline( parser, 1UL );
985 2268 : if( !fd_toml_avail( parser ) ) break;
986 2268 : if( !isdigit( parser->c.data[0] ) && parser->c.data[0] != '_' ) break;
987 1704 : allow_underscore = 1;
988 1704 : }
989 2304 : }
990 :
991 564 : dec->res = digits;
992 564 : dec->len = len;
993 564 : return 1;
994 564 : }
995 :
996 : static int
997 : fd_toml_parse_dec_int_( fd_toml_parser_t * parser,
998 720 : fd_toml_dec_t * dec ) {
999 720 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
1000 720 : int c = (uchar)parser->c.data[0];
1001 :
1002 720 : int neg = 0;
1003 720 : switch( c ) {
1004 0 : case '-':
1005 0 : neg = 1;
1006 0 : __attribute__((fallthrough));
1007 0 : case '+':
1008 0 : fd_toml_advance_inline( parser, 1UL );
1009 0 : break;
1010 720 : }
1011 :
1012 720 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
1013 720 : int first_digit = (uchar)parser->c.data[0];
1014 720 : if( first_digit == '0' ) {
1015 72 : dec->res = 0UL;
1016 72 : dec->neg = 0;
1017 72 : fd_toml_advance_inline( parser, 1UL );
1018 72 : return 1;
1019 72 : }
1020 :
1021 648 : if( FD_UNLIKELY( first_digit<='0' || first_digit>'9' ) ) return 0;
1022 :
1023 564 : dec->neg = !!neg;
1024 564 : return fd_toml_parse_zero_prefixable_int( parser, dec );
1025 648 : }
1026 :
1027 : static int
1028 360 : fd_toml_parse_dec_int( fd_toml_parser_t * parser ) {
1029 360 : fd_toml_dec_t dec = {0};
1030 360 : if( FD_UNLIKELY( !fd_toml_parse_dec_int_( parser, &dec ) ) ) return 0;
1031 318 : long val = (long)dec.res;
1032 318 : val = fd_long_if( dec.neg, -val, val );
1033 318 : if( FD_UNLIKELY( !fd_pod_insert_long( parser->pod, parser->key, val ) ) ) {
1034 0 : parser->error = FD_TOML_ERR_POD;
1035 0 : return 0;
1036 0 : }
1037 318 : return 1;
1038 318 : }
1039 :
1040 : /* hex-int = hex-prefix HEXDIG *( HEXDIG / underscore HEXDIG ) */
1041 :
1042 : static int
1043 360 : fd_toml_parse_hex_int( fd_toml_parser_t * parser ) {
1044 360 : if( FD_UNLIKELY( fd_toml_avail( parser ) < 3 ) ) return 0;
1045 360 : if( FD_UNLIKELY( parser->c.data[0] != '0' ) ) return 0;
1046 36 : if( FD_UNLIKELY( parser->c.data[1] != 'x' ) ) return 0;
1047 0 : if( FD_UNLIKELY( !isxdigit( parser->c.data[2] ) ) ) return 0; /* at least one digit */
1048 0 : fd_toml_advance_inline( parser, 2UL );
1049 :
1050 0 : ulong res = 0UL;
1051 0 : int allow_underscore = 0;
1052 0 : for(;;) {
1053 0 : int digit = (uchar)parser->c.data[0];
1054 0 : if( FD_UNLIKELY( allow_underscore && digit == '_' ) ) {
1055 0 : allow_underscore = 0;
1056 0 : fd_toml_advance_inline( parser, 1UL );
1057 0 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
1058 0 : if( FD_UNLIKELY( !isxdigit( parser->c.data[0] ) ) ) return 0;
1059 0 : } else {
1060 0 : if( !isxdigit( digit ) ) break;
1061 0 : if( FD_UNLIKELY( res>>60 ) ) {
1062 0 : parser->error = FD_TOML_ERR_RANGE;
1063 0 : return 0;
1064 0 : }
1065 0 : res <<= 4;
1066 0 : res |= fd_toml_xdigit( digit );
1067 0 : fd_toml_advance_inline( parser, 1UL );
1068 0 : if( !fd_toml_avail( parser ) ) break;
1069 0 : allow_underscore = 1;
1070 0 : }
1071 0 : }
1072 :
1073 0 : if( FD_UNLIKELY( !fd_pod_insert_long( parser->pod, parser->key, (long)res ) ) ) {
1074 0 : parser->error = FD_TOML_ERR_POD;
1075 0 : return 0;
1076 0 : }
1077 :
1078 0 : return 1;
1079 0 : }
1080 :
1081 : /* oct-int = oct-prefix digit0-7 *( digit0-7 / underscore digit0-7 ) */
1082 :
1083 : static inline int
1084 0 : fd_toml_is_odigit( int c ) {
1085 0 : return c>='0' && c<'8';
1086 0 : }
1087 :
1088 : static int
1089 360 : fd_toml_parse_oct_int( fd_toml_parser_t * parser ) {
1090 360 : if( FD_UNLIKELY( fd_toml_avail( parser ) < 3 ) ) return 0;
1091 360 : if( FD_UNLIKELY( parser->c.data[0] != '0' ) ) return 0;
1092 36 : if( FD_UNLIKELY( parser->c.data[1] != 'o' ) ) return 0;
1093 0 : if( FD_UNLIKELY( !fd_toml_is_odigit( parser->c.data[2] ) ) ) return 0; /* at least one digit */
1094 0 : fd_toml_advance_inline( parser, 2UL );
1095 :
1096 0 : ulong res = 0UL;
1097 0 : int allow_underscore = 0;
1098 0 : for(;;) {
1099 0 : int digit = (uchar)parser->c.data[0];
1100 0 : if( allow_underscore && digit == '_' ) {
1101 0 : allow_underscore = 0;
1102 0 : fd_toml_advance_inline( parser, 1UL );
1103 0 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
1104 0 : if( FD_UNLIKELY( !fd_toml_is_odigit( parser->c.data[0] ) ) ) return 0;
1105 0 : } else {
1106 0 : if( !fd_toml_is_odigit( digit ) ) break;
1107 0 : if( FD_UNLIKELY( res>>61 ) ) {
1108 0 : parser->error = FD_TOML_ERR_RANGE;
1109 0 : return 0;
1110 0 : }
1111 0 : res <<= 3;
1112 0 : res |= (ulong)( digit - '0' );
1113 0 : fd_toml_advance_inline( parser, 1UL );
1114 0 : if( !fd_toml_avail( parser ) ) break;
1115 0 : allow_underscore = 1;
1116 0 : }
1117 0 : }
1118 :
1119 0 : if( FD_UNLIKELY( !fd_pod_insert_long( parser->pod, parser->key, (long)res ) ) ) {
1120 0 : parser->error = FD_TOML_ERR_POD;
1121 0 : return 0;
1122 0 : }
1123 :
1124 0 : return 1;
1125 0 : }
1126 :
1127 : /* bin-int = bin-prefix digit0-1 *( digit0-1 / underscore digit0-1 ) */
1128 :
1129 : static inline int
1130 0 : fd_toml_is_bdigit( int c ) {
1131 0 : return c=='0' || c=='1';
1132 0 : }
1133 :
1134 : static int
1135 360 : fd_toml_parse_bin_int( fd_toml_parser_t * parser ) {
1136 360 : if( FD_UNLIKELY( fd_toml_avail( parser ) < 3 ) ) return 0;
1137 360 : if( FD_UNLIKELY( parser->c.data[0] != '0' ) ) return 0;
1138 36 : if( FD_UNLIKELY( parser->c.data[1] != 'b' ) ) return 0;
1139 0 : if( FD_UNLIKELY( !fd_toml_is_bdigit( parser->c.data[2] ) ) ) return 0; /* at least one digit */
1140 0 : fd_toml_advance_inline( parser, 2UL );
1141 :
1142 : /* TODO OVERFLOW DETECTION */
1143 :
1144 0 : ulong res = 0UL;
1145 0 : int allow_underscore = 0;
1146 0 : for(;;) {
1147 0 : int digit = (uchar)parser->c.data[0];
1148 0 : if( FD_UNLIKELY( allow_underscore && digit == '_' ) ) {
1149 0 : allow_underscore = 0;
1150 0 : fd_toml_advance_inline( parser, 1UL );
1151 0 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
1152 0 : if( FD_UNLIKELY( !fd_toml_is_bdigit( parser->c.data[0] ) ) ) return 0;
1153 0 : } else {
1154 0 : if( !fd_toml_is_bdigit( digit ) ) break;
1155 0 : if( FD_UNLIKELY( res>>63 ) ) {
1156 0 : parser->error = FD_TOML_ERR_RANGE;
1157 0 : return 0;
1158 0 : }
1159 0 : res <<= 1;
1160 0 : res |= (ulong)( digit - '0' );
1161 0 : fd_toml_advance_inline( parser, 1UL );
1162 0 : if( !fd_toml_avail( parser ) ) break;
1163 0 : allow_underscore = 1;
1164 0 : }
1165 0 : }
1166 :
1167 0 : if( FD_UNLIKELY( !fd_pod_insert_long( parser->pod, parser->key, (long)res ) ) ) {
1168 0 : parser->error = FD_TOML_ERR_POD;
1169 0 : return 0;
1170 0 : }
1171 :
1172 0 : return 1;
1173 0 : }
1174 :
1175 : /* integer = dec-int / hex-int / oct-int / bin-int */
1176 :
1177 : static int
1178 360 : fd_toml_parse_integer( fd_toml_parser_t * parser ) {
1179 360 : if( SUB_PARSE( fd_toml_parse_hex_int( parser ) ) ) return 1;
1180 360 : if( SUB_PARSE( fd_toml_parse_oct_int( parser ) ) ) return 1;
1181 360 : if( SUB_PARSE( fd_toml_parse_bin_int( parser ) ) ) return 1;
1182 360 : if( SUB_PARSE( fd_toml_parse_dec_int( parser ) ) ) return 1;
1183 42 : return 0;
1184 360 : }
1185 :
1186 : /* exp = "e" float-exp-part
1187 : float-exp-part = [ minus / plus ] zero-prefixable-int */
1188 :
1189 : static int
1190 : fd_toml_parse_exp( fd_toml_parser_t * parser,
1191 318 : fd_toml_dec_t * exp ) {
1192 318 : if( FD_UNLIKELY( fd_toml_avail( parser ) < 2 ) ) return 0;
1193 318 : switch( parser->c.data[0] ) {
1194 0 : case 'e': case 'E': break;
1195 318 : default: return 0;
1196 318 : }
1197 0 : fd_toml_advance_inline( parser, 1UL );
1198 :
1199 0 : switch( parser->c.data[0] ) {
1200 0 : case '-':
1201 0 : exp->neg = 1;
1202 0 : __attribute__((fallthrough));
1203 0 : case '+':
1204 0 : fd_toml_advance_inline( parser, 1UL );
1205 0 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
1206 0 : break;
1207 0 : }
1208 :
1209 0 : int first_digit = (uchar)parser->c.data[0];
1210 0 : if( FD_UNLIKELY( first_digit<'0' || first_digit>'9' ) ) return 0;
1211 0 : if( FD_UNLIKELY( !fd_toml_parse_zero_prefixable_int( parser, exp ) ) ) return 0;
1212 0 : return 1;
1213 0 : }
1214 :
1215 : /* frac = decimal-point zero-prefixable-int
1216 : decimal-point = %x2E */
1217 :
1218 : static int
1219 : fd_toml_parse_frac( fd_toml_parser_t * parser,
1220 318 : fd_toml_dec_t * frac ) {
1221 318 : if( FD_UNLIKELY( fd_toml_avail( parser ) < 2 ) ) return 0;
1222 318 : if( FD_UNLIKELY( parser->c.data[0] != '.' ) ) return 0;
1223 0 : fd_toml_advance_inline( parser, 1UL );
1224 :
1225 0 : int first_digit = (uchar)parser->c.data[0];
1226 0 : if( FD_UNLIKELY( first_digit<'0' || first_digit>'9' ) ) return 0;
1227 0 : if( FD_UNLIKELY( !fd_toml_parse_zero_prefixable_int( parser, frac ) ) ) return 0;
1228 0 : return 1;
1229 0 : }
1230 :
1231 : /* float = float-int-part ( exp / frac [ exp ] )
1232 : float-int-part = dec-int */
1233 :
1234 : static int
1235 360 : fd_toml_parse_float_normal( fd_toml_parser_t * parser ) {
1236 :
1237 360 : fd_toml_dec_t stem = {0};
1238 360 : if( FD_UNLIKELY( !fd_toml_parse_dec_int_( parser, &stem ) ) ) return 0;
1239 318 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
1240 318 : float res = (float)stem.res;
1241 :
1242 318 : int ok = 0;
1243 318 : fd_toml_dec_t frac_dec = {0};
1244 318 : if( SUB_PARSE( fd_toml_parse_frac( parser, &frac_dec ) ) ) {
1245 0 : float frac = (float)frac_dec.res;
1246 0 : while( frac_dec.len-- ) frac /= 10.0f; /* use pow? */
1247 0 : res += frac;
1248 0 : ok = 1;
1249 0 : }
1250 :
1251 0 : fd_toml_dec_t exp_dec = {0};
1252 318 : if( !SUB_PARSE( fd_toml_parse_exp( parser, &exp_dec ) ) ) {
1253 318 : if( FD_LIKELY( ok ) ) goto parsed;
1254 318 : return 0;
1255 318 : }
1256 :
1257 0 : float exp = powf( exp_dec.neg ? 0.1f : 10.0f, (float)exp_dec.res );
1258 0 : res *= exp;
1259 :
1260 0 : parsed:
1261 0 : if( FD_UNLIKELY( !fd_pod_insert_float( parser->pod, parser->key, res ) ) ) {
1262 0 : parser->error = FD_TOML_ERR_POD;
1263 0 : return 0;
1264 0 : }
1265 0 : return 1;
1266 0 : }
1267 :
1268 : /* special-float = [ minus / plus ] ( inf / nan )
1269 : inf = %x69.6e.66 ; inf
1270 : nan = %x6e.61.6e ; nan */
1271 :
1272 : static int
1273 360 : fd_float_parse_float_special( fd_toml_parser_t * parser ) {
1274 360 : if( FD_UNLIKELY( fd_toml_avail( parser ) < 3 ) ) return 0;
1275 360 : int c = (uchar)parser->c.data[0];
1276 :
1277 360 : switch( c ) {
1278 0 : case '-': case '+':
1279 0 : fd_toml_advance_inline( parser, 1UL );
1280 0 : if( FD_UNLIKELY( fd_toml_avail( parser ) < 3 ) ) return 0;
1281 0 : break;
1282 360 : }
1283 :
1284 360 : char const * str = parser->c.data;
1285 360 : fd_toml_advance_inline( parser, 3UL );
1286 :
1287 360 : if( 0==strncasecmp( str, "inf", 3 ) ) {
1288 0 : FD_LOG_WARNING(( "TOML parse error: float infinity is unsupported" ));
1289 0 : parser->error = FD_TOML_ERR_RANGE;
1290 0 : return 0;
1291 0 : }
1292 :
1293 360 : if( 0==strncasecmp( str, "nan", 3 ) ) {
1294 0 : FD_LOG_WARNING(( "TOML parse error: float NaN is unsupported" ));
1295 0 : parser->error = FD_TOML_ERR_RANGE;
1296 0 : return 1;
1297 0 : }
1298 :
1299 360 : return 0;
1300 360 : }
1301 :
1302 : /* float = float-int-part ( exp / frac [ exp ] )
1303 : float =/ special-float */
1304 :
1305 : static int
1306 360 : fd_toml_parse_float( fd_toml_parser_t * parser ) {
1307 360 : if( SUB_PARSE( fd_toml_parse_float_normal ( parser ) ) ) return 1;
1308 360 : if( SUB_PARSE( fd_float_parse_float_special( parser ) ) ) return 1;
1309 360 : return 0;
1310 360 : }
1311 :
1312 : /* full-date = date-fullyear "-" date-month "-" date-mday
1313 : date-fullyear = 4DIGIT
1314 : date-month = 2DIGIT ; 01-12
1315 : date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on month/year */
1316 :
1317 : static int
1318 : fd_toml_parse_full_date( fd_toml_parser_t * parser,
1319 1080 : struct tm * time ) {
1320 1080 : if( FD_UNLIKELY( fd_toml_avail( parser ) < 10 ) ) return 0;
1321 :
1322 1080 : if( ( !isdigit( parser->c.data[0] ) ) |
1323 1080 : ( !isdigit( parser->c.data[1] ) ) |
1324 1080 : ( !isdigit( parser->c.data[2] ) ) |
1325 1080 : ( !isdigit( parser->c.data[3] ) ) |
1326 1080 : ( parser->c.data[4] != '-' ) |
1327 1080 : ( !isdigit( parser->c.data[5] ) ) |
1328 1080 : ( !isdigit( parser->c.data[6] ) ) |
1329 1080 : ( parser->c.data[7] != '-' ) |
1330 1080 : ( !isdigit( parser->c.data[8] ) ) |
1331 1080 : ( !isdigit( parser->c.data[9] ) ) ) {
1332 1080 : return 0;
1333 1080 : }
1334 :
1335 0 : char cstr[ 11 ];
1336 0 : memcpy( cstr, parser->c.data, 10 );
1337 0 : cstr[10] = '\x00';
1338 0 : fd_toml_advance_inline( parser, 10UL );
1339 :
1340 0 : if( FD_UNLIKELY( !strptime( cstr, "%Y-%m-%d", time ) ) ) {
1341 0 : FD_LOG_WARNING(( "TOML parse error: invalid date format" ));
1342 0 : return 0;
1343 0 : }
1344 0 : return 1;
1345 0 : }
1346 :
1347 : /* time-delim = "T" / %x20 ; T, t, or space */
1348 :
1349 : static int
1350 0 : fd_toml_parse_time_delim( fd_toml_parser_t * parser ) {
1351 0 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
1352 0 : switch( parser->c.data[0] ) {
1353 0 : case 'T': case 't': case ' ': break;
1354 0 : default: return 0;
1355 0 : }
1356 0 : fd_toml_advance_inline( parser, 1UL );
1357 0 : return 1;
1358 0 : }
1359 :
1360 : /* time-secfrac = "." 1*DIGIT */
1361 :
1362 : static int
1363 : fd_toml_parse_time_secfrac( fd_toml_parser_t * parser,
1364 0 : ulong * pnanos ) {
1365 0 : if( fd_toml_avail( parser ) < 2 ) return 0;
1366 0 : if( parser->c.data[0] != '.' ) return 0;
1367 0 : if( FD_UNLIKELY( !isdigit( parser->c.data[1] ) ) ) return 0;
1368 0 : fd_toml_advance_inline( parser, 1UL );
1369 :
1370 0 : ulong secfrac = 0UL;
1371 0 : ulong len = 0UL;
1372 0 : do {
1373 0 : int digit = (uchar)parser->c.data[0];
1374 0 : secfrac = secfrac * 10UL + (ulong)( digit - '0' );
1375 0 : fd_toml_advance_inline( parser, 1UL );
1376 0 : len++;
1377 0 : } while( fd_toml_avail( parser ) && isdigit( parser->c.data[0] ) );
1378 0 : if( FD_UNLIKELY( len > 9 ) ) {
1379 0 : FD_LOG_WARNING(( "TOML parse error: invalid time fraction format" ));
1380 0 : return 0;
1381 0 : }
1382 :
1383 0 : while( len++ < 9 ) secfrac *= 10UL;
1384 0 : *pnanos = secfrac;
1385 0 : return 1;
1386 0 : }
1387 :
1388 : /* partial-time = time-hour ":" time-minute ":" time-second [ time-secfrac ]
1389 : time-hour = 2DIGIT ; 00-23
1390 : time-minute = 2DIGIT ; 00-59
1391 : time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second rules */
1392 :
1393 : static int
1394 : fd_toml_parse_partial_time( fd_toml_parser_t * parser,
1395 360 : ulong * pnanos ) {
1396 :
1397 360 : if( FD_UNLIKELY( fd_toml_avail( parser ) < 8 ) ) return 0;
1398 360 : if( ( !isdigit( parser->c.data[0] ) ) |
1399 360 : ( !isdigit( parser->c.data[1] ) ) |
1400 360 : ( parser->c.data[2] != ':' ) |
1401 360 : ( !isdigit( parser->c.data[3] ) ) |
1402 360 : ( !isdigit( parser->c.data[4] ) ) |
1403 360 : ( parser->c.data[5] != ':' ) |
1404 360 : ( !isdigit( parser->c.data[6] ) ) |
1405 360 : ( !isdigit( parser->c.data[7] ) ) ) {
1406 360 : return 0;
1407 360 : }
1408 :
1409 0 : char cstr[ 9 ];
1410 0 : memcpy( cstr, parser->c.data, 8 );
1411 0 : cstr[8] = '\x00';
1412 0 : fd_toml_advance_inline( parser, 8UL );
1413 :
1414 0 : struct tm time[1];
1415 0 : if( FD_UNLIKELY( !strptime( cstr, "%H:%M:%S", time ) ) ) {
1416 0 : FD_LOG_WARNING(( "TOML parse error: invalid time format" ));
1417 0 : return 0;
1418 0 : }
1419 :
1420 0 : ulong res = 0UL;
1421 0 : res += (ulong)time->tm_hour * 3600UL;
1422 0 : res += (ulong)time->tm_min * 60UL;
1423 0 : res += (ulong)time->tm_sec;
1424 0 : res *= (ulong)1e9;
1425 0 : ulong ns_frac;
1426 0 : if( SUB_PARSE( fd_toml_parse_time_secfrac( parser, &ns_frac ) ) ) {
1427 0 : res += ns_frac;
1428 0 : }
1429 0 : *pnanos = res;
1430 0 : return 1;
1431 0 : }
1432 :
1433 : /* time-numoffset = ( "+" / "-" ) time-hour ":" time-minute */
1434 :
1435 : static int
1436 : fd_toml_parse_time_numoffset( fd_toml_parser_t * parser,
1437 0 : long * psec ) {
1438 0 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
1439 :
1440 0 : int neg = 0;
1441 0 : switch( parser->c.data[0] ) {
1442 0 : case '+': neg = 0; break;
1443 0 : case '-': neg = 1; break;
1444 0 : default: return 0;
1445 0 : }
1446 0 : fd_toml_advance_inline( parser, 1UL );
1447 :
1448 0 : if( FD_UNLIKELY( fd_toml_avail( parser ) < 5 ) ) return 0;
1449 0 : if( ( !isdigit( parser->c.data[0] ) ) |
1450 0 : ( !isdigit( parser->c.data[1] ) ) |
1451 0 : ( parser->c.data[2] != ':' ) |
1452 0 : ( !isdigit( parser->c.data[3] ) ) |
1453 0 : ( !isdigit( parser->c.data[4] ) ) ) {
1454 0 : FD_LOG_WARNING(( "TOML parse error: invalid time offset format" ));
1455 0 : return 0;
1456 0 : }
1457 :
1458 0 : char cstr[ 6 ];
1459 0 : memcpy( cstr, parser->c.data, 5 );
1460 0 : cstr[5] = '\x00';
1461 0 : fd_toml_advance_inline( parser, 5UL );
1462 :
1463 0 : struct tm time;
1464 0 : if( FD_UNLIKELY( !strptime( cstr, "%H:%M", &time ) ) ) {
1465 0 : FD_LOG_WARNING(( "TOML parse error: invalid time offset format" ));
1466 0 : return 0;
1467 0 : }
1468 0 : long abs_sec = (long)time.tm_hour * 3600L + (long)time.tm_min * 60L;
1469 0 : *psec = neg ? -abs_sec : abs_sec;
1470 0 : return 1;
1471 0 : }
1472 :
1473 : /* time-offset = "Z" / time-numoffset */
1474 :
1475 : static int
1476 : fd_toml_parse_time_offset( fd_toml_parser_t * parser,
1477 0 : long * psec ) {
1478 0 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
1479 :
1480 0 : switch( parser->c.data[0] ) {
1481 0 : case 'Z': case 'z':
1482 0 : *psec = 0;
1483 0 : fd_toml_advance_inline( parser, 1UL );
1484 0 : return 1;
1485 0 : }
1486 :
1487 0 : return fd_toml_parse_time_numoffset( parser, psec );
1488 0 : }
1489 :
1490 : /* full-time = partial-time time-offset */
1491 :
1492 : static int
1493 : fd_toml_parse_full_time( fd_toml_parser_t * parser,
1494 0 : ulong * pnanos ) {
1495 0 : long off_sec = 0;
1496 0 : if( FD_UNLIKELY( !fd_toml_parse_partial_time( parser, pnanos ) ) ) return 0;
1497 0 : if( FD_UNLIKELY( !fd_toml_parse_time_offset ( parser, &off_sec ) ) ) return 0;
1498 0 : *pnanos += (ulong)off_sec;
1499 0 : return 1;
1500 0 : }
1501 :
1502 : /* offset-date-time = full-date time-delim full-time */
1503 :
1504 : static int
1505 : fd_toml_parse_offset_date_time( fd_toml_parser_t * parser,
1506 360 : ulong * pnanos ) {
1507 360 : struct tm date = {0};
1508 :
1509 360 : if( FD_UNLIKELY( !fd_toml_parse_full_date ( parser, &date ) ) ) return 0;
1510 0 : if( FD_UNLIKELY( !fd_toml_parse_time_delim( parser ) ) ) return 0;
1511 0 : if( FD_UNLIKELY( !fd_toml_parse_full_time ( parser, pnanos ) ) ) return 0;
1512 :
1513 0 : *pnanos += (ulong)timegm( &date ) * (ulong)1e9;
1514 0 : return 1;
1515 0 : }
1516 :
1517 : /* local-date-time = full-date time-delim partial-time */
1518 :
1519 : static int
1520 : fd_toml_parse_local_date_time( fd_toml_parser_t * parser,
1521 360 : ulong * pnanos ) {
1522 :
1523 360 : struct tm date = {0};
1524 360 : if( FD_UNLIKELY( !fd_toml_parse_full_date ( parser, &date ) ) ) return 0;
1525 0 : if( FD_UNLIKELY( !fd_toml_parse_time_delim ( parser ) ) ) return 0;
1526 0 : if( FD_UNLIKELY( !fd_toml_parse_partial_time( parser, pnanos ) ) ) return 0;
1527 :
1528 0 : *pnanos = (ulong)mktime( &date ) * (ulong)1e9;
1529 0 : return 1;
1530 0 : }
1531 :
1532 : /* local-date = full-date */
1533 :
1534 : static int
1535 : fd_toml_parse_local_date( fd_toml_parser_t * parser,
1536 360 : ulong * pnanos ) {
1537 360 : struct tm date = {0};
1538 360 : if( FD_UNLIKELY( !fd_toml_parse_full_date( parser, &date ) ) ) return 0;
1539 0 : *pnanos = (ulong)mktime( &date ) * (ulong)1e9;
1540 0 : return 1;
1541 360 : }
1542 :
1543 : /* local-time = partial-time */
1544 :
1545 : static int
1546 : fd_toml_parse_local_time( fd_toml_parser_t * parser,
1547 360 : ulong * pnanos ) {
1548 360 : return fd_toml_parse_partial_time( parser, pnanos );
1549 360 : }
1550 :
1551 : /* date-time = offset-date-time / local-date-time / local-date / local-time */
1552 :
1553 : static int
1554 360 : fd_toml_parse_date_time( fd_toml_parser_t * parser ) {
1555 360 : ulong unix_nanos;
1556 360 : if( SUB_PARSE( fd_toml_parse_offset_date_time( parser, &unix_nanos ) ) ) goto add;
1557 360 : if( SUB_PARSE( fd_toml_parse_local_date_time ( parser, &unix_nanos ) ) ) goto add;
1558 360 : if( SUB_PARSE( fd_toml_parse_local_date ( parser, &unix_nanos ) ) ) goto add;
1559 360 : if( SUB_PARSE( fd_toml_parse_local_time ( parser, &unix_nanos ) ) ) goto add;
1560 360 : return 0;
1561 0 : add:
1562 0 : if( FD_UNLIKELY( !fd_pod_insert_ulong( parser->pod, parser->key, unix_nanos ) ) ) {
1563 0 : parser->error = FD_TOML_ERR_POD;
1564 0 : return 0;
1565 0 : }
1566 0 : return 1;
1567 0 : }
1568 :
1569 : /* val = string / boolean / array / inline-table / date-time / float / integer */
1570 :
1571 : static int
1572 897 : fd_toml_parse_val( fd_toml_parser_t * parser ) {
1573 : /* consider consider some lookahead for better performance */
1574 897 : if( SUB_PARSE( fd_toml_parse_string ( parser ) ) ) return 1;
1575 612 : if( SUB_PARSE( fd_toml_parse_boolean ( parser ) ) ) return 1;
1576 408 : if( SUB_PARSE( fd_toml_parse_array ( parser ) ) ) return 1;
1577 360 : if( SUB_PARSE( fd_toml_parse_inline_table( parser ) ) ) return 1;
1578 360 : if( SUB_PARSE( fd_toml_parse_date_time ( parser ) ) ) return 1;
1579 : /* NOTE: float and integer have a common dec-int prefix -- dedup for better performance */
1580 360 : if( SUB_PARSE( fd_toml_parse_float ( parser ) ) ) return 1;
1581 360 : if( SUB_PARSE( fd_toml_parse_integer ( parser ) ) ) return 1;
1582 42 : return 0;
1583 360 : }
1584 :
1585 : /* keyval = key keyval-sep val */
1586 :
1587 : static int
1588 9363 : fd_toml_parse_keyval( fd_toml_parser_t * parser ) {
1589 9363 : uint old_key_len = parser->key_len;
1590 9363 : if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_key( parser ) ) ) ) return 0;
1591 :
1592 849 : if( FD_UNLIKELY( fd_pod_query( parser->pod, parser->key, NULL )==FD_POD_SUCCESS ) ) {
1593 0 : FD_LOG_WARNING(( "TOML parse error: duplicate key: \"%s\"", parser->key ));
1594 0 : parser->error = FD_TOML_ERR_DUP;
1595 0 : return 0;
1596 0 : }
1597 :
1598 849 : if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_keyval_sep( parser ) ) ) ) return 0;
1599 849 : if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_val ( parser ) ) ) ) return 0;
1600 :
1601 849 : FD_LOG_DEBUG(( "Added key %s", parser->key ));
1602 849 : parser->key[ old_key_len ] = 0;
1603 849 : parser->key_len = old_key_len;
1604 849 : return 1;
1605 849 : }
1606 :
1607 : /* std-table = std-table-open key std-table-close
1608 :
1609 : std-table-open = %x5B ws ; [ Left square bracket
1610 : std-table-close = ws %x5D ; ] Right square bracket */
1611 :
1612 : static int
1613 8514 : fd_toml_parse_std_table( fd_toml_parser_t * parser ) {
1614 8514 : EXPECT_CHAR( '[' );
1615 180 : fd_toml_parse_ws( parser );
1616 :
1617 180 : parser->key[ parser->key_len = 0 ] = 0;
1618 180 : if( FD_UNLIKELY( !fd_toml_parse_key( parser ) ) ) return 0;
1619 : // FIXME: consider blocking duplicate tables?
1620 : //if( FD_UNLIKELY( fd_pod_query( parser->pod, parser->key, NULL )==FD_POD_SUCCESS ) ) {
1621 : // FD_LOG_WARNING(( "Duplicate table: \"%s\"", parser->key ));
1622 : // parser->error = FD_TOML_ERR_DUP;
1623 : // return 0;
1624 : //}
1625 180 : FD_LOG_DEBUG(( "Added table %.*s", (int)parser->key_len, parser->key ));
1626 :
1627 180 : fd_toml_parse_ws( parser );
1628 180 : EXPECT_CHAR( ']' );
1629 180 : return 1;
1630 180 : }
1631 :
1632 : /* array-table = array-table = array-table-open key array-table-close
1633 :
1634 : array-table-open = %x5B.5B ws ; [[ Double left square bracket
1635 : array-table-close = ws %x5D.5D ; ]] Double right square bracket */
1636 :
1637 : static int
1638 8514 : fd_toml_parse_array_table( fd_toml_parser_t * parser ) {
1639 8514 : if( fd_toml_avail( parser ) < 2UL ) return 0;
1640 8508 : if( ( parser->c.data[0] != '[' ) |
1641 8508 : ( parser->c.data[1] != '[' ) ) return 0;
1642 0 : fd_toml_advance_inline( parser, 2UL );
1643 :
1644 0 : fd_toml_parse_ws( parser );
1645 :
1646 : /* Set parser->key to path to array */
1647 :
1648 0 : parser->key[ parser->key_len = 0 ] = 0;
1649 0 : if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_key( parser ) ) ) ) return 0;
1650 :
1651 : /* Count number of predecessors */
1652 :
1653 0 : ulong idx = 0UL;
1654 0 : uchar const * subpod = fd_pod_query_subpod( parser->pod, parser->key );
1655 0 : if( subpod ) {
1656 0 : idx = fd_pod_cnt( subpod );
1657 0 : }
1658 :
1659 : /* Append array index to path */
1660 :
1661 0 : char * key_c = parser->key + parser->key_len;
1662 0 : if( FD_UNLIKELY( key_c + 22 > parser->key + sizeof(parser->key) ) ) {
1663 : /* array index might be OOB (see python3 -c 'print(len(str(1<<64)))') */
1664 0 : parser->error = FD_TOML_ERR_KEY;
1665 0 : return 0;
1666 0 : }
1667 0 : key_c = fd_cstr_append_char( key_c, '.' );
1668 0 : key_c = fd_cstr_append_ulong_as_text( key_c, 0, 0, idx, fd_ulong_base10_dig_cnt( idx ) );
1669 0 : fd_cstr_fini( key_c );
1670 0 : parser->key_len = (uint)( key_c - parser->key );
1671 :
1672 0 : FD_LOG_DEBUG(( "Added array table %.*s", (int)parser->key_len, parser->key ));
1673 :
1674 : /* Continue parsing */
1675 :
1676 0 : fd_toml_parse_ws( parser );
1677 :
1678 0 : if( FD_UNLIKELY( fd_toml_avail( parser ) < 2UL ) ) return 0;
1679 0 : if( FD_UNLIKELY( ( parser->c.data[0] != ']' ) |
1680 0 : ( parser->c.data[1] != ']' ) ) ) return 0;
1681 0 : fd_toml_advance_inline( parser, 2UL );
1682 0 : return 1;
1683 0 : }
1684 :
1685 : /* table = std-table / array-table */
1686 :
1687 : static int
1688 8514 : fd_toml_parse_table( fd_toml_parser_t * parser ) {
1689 8514 : if( SUB_PARSE( fd_toml_parse_array_table( parser ) ) ) goto add;
1690 8514 : if( SUB_PARSE( fd_toml_parse_std_table ( parser ) ) ) goto add;
1691 8334 : return 0;
1692 180 : add:
1693 180 : fd_toml_upsert_empty_pod( parser );
1694 : /* Add trailing dot */
1695 180 : if( parser->key_len + 2 > sizeof(parser->key) ) {
1696 0 : parser->error = FD_TOML_ERR_KEY;
1697 0 : return 0;
1698 0 : }
1699 180 : parser->key[ parser->key_len++ ] = '.';
1700 180 : parser->key[ parser->key_len ] = '\x00';
1701 180 : return 1;
1702 180 : }
1703 :
1704 : /* expression = ws [ comment ]
1705 : expression =/ ws keyval ws [ comment ]
1706 : expression =/ ws table ws [ comment ] */
1707 :
1708 : static int
1709 9363 : fd_toml_parse_expression( fd_toml_parser_t * parser ) {
1710 :
1711 9363 : fd_toml_parse_ws( parser );
1712 :
1713 9363 : if( FD_LIKELY( SUB_PARSE( fd_toml_parse_keyval( parser ) ) ) ) {
1714 849 : fd_toml_parse_ws( parser );
1715 849 : }
1716 8514 : else if( FD_LIKELY( SUB_PARSE( fd_toml_parse_table( parser ) ) ) ) {
1717 180 : fd_toml_parse_ws( parser );
1718 180 : }
1719 :
1720 9363 : SUB_PARSE( fd_toml_parse_comment( parser ) );
1721 0 : return 1;
1722 9363 : }
1723 :
1724 : /* toml = expression *( newline expression ) */
1725 :
1726 : static int
1727 15 : fd_toml_parse_toml( fd_toml_parser_t * parser ) {
1728 :
1729 15 : if( FD_UNLIKELY( !fd_toml_parse_expression( parser ) ) ) return 0;
1730 :
1731 9363 : for(;;) {
1732 9363 : if( FD_UNLIKELY( parser->error ) ) break;
1733 9363 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) break;
1734 9348 : if( FD_UNLIKELY( parser->c.data[0] != '\n' ) ) break;
1735 9348 : fd_toml_advance( parser, 1UL );
1736 9348 : if( FD_UNLIKELY( !fd_toml_parse_expression( parser ) ) ) return 0;
1737 9348 : }
1738 :
1739 15 : return 1;
1740 15 : }
1741 :
1742 : int
1743 : fd_toml_parse( void const * toml,
1744 : ulong toml_sz,
1745 : uchar * pod,
1746 : uchar * scratch,
1747 : ulong scratch_sz,
1748 15 : fd_toml_err_info_t * opt_err ) {
1749 :
1750 15 : static fd_toml_err_info_t _dummy_err[1];
1751 15 : if( !opt_err ) opt_err = _dummy_err;
1752 15 : opt_err->line = 0UL;
1753 :
1754 15 : if( FD_UNLIKELY( !toml_sz ) ) return FD_TOML_SUCCESS;
1755 15 : if( FD_UNLIKELY( !scratch_sz ) ) {
1756 0 : FD_LOG_WARNING(( "zero scratch_sz" ));
1757 0 : return FD_TOML_ERR_SCRATCH;
1758 0 : }
1759 :
1760 15 : fd_toml_parser_t parser[1] = {{
1761 15 : .c = {
1762 15 : .data = toml,
1763 15 : .lineno = 1UL,
1764 15 : },
1765 15 : .data_end = (char const *)toml + toml_sz,
1766 15 : .pod = pod,
1767 15 : .scratch = scratch,
1768 15 : .scratch_cur = scratch,
1769 15 : .scratch_end = scratch + scratch_sz
1770 15 : }};
1771 :
1772 :
1773 15 : int ok = fd_toml_parse_toml( parser );
1774 15 : opt_err->line = parser->c.lineno;
1775 :
1776 15 : if( FD_UNLIKELY( (!ok) | (fd_toml_avail( parser ) > 0) ) ) {
1777 0 : return fd_int_if( !!parser->error, parser->error, FD_TOML_ERR_PARSE );
1778 0 : }
1779 :
1780 15 : return FD_TOML_SUCCESS;
1781 15 : }
1782 :
1783 : FD_FN_CONST char const *
1784 0 : fd_toml_strerror( int err ) {
1785 0 : switch( err ) {
1786 0 : case FD_TOML_SUCCESS: return "success";
1787 0 : case FD_TOML_ERR_POD: return "out of memory in output pod";
1788 0 : case FD_TOML_ERR_SCRATCH: return "out of memory in scratch region";
1789 0 : case FD_TOML_ERR_KEY: return "oversize key";
1790 0 : case FD_TOML_ERR_DUP: return "duplicate key";
1791 0 : case FD_TOML_ERR_RANGE: return "integer overflow";
1792 0 : case FD_TOML_ERR_PARSE: return "parse failure";
1793 0 : default: return "unknown error";
1794 0 : }
1795 0 : }
|