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