Line data Source code
1 : #define _DEFAULT_SOURCE
2 : #include "fd_toml.h"
3 : #include "../../util/fd_util.h"
4 : #include <ctype.h>
5 : #include <math.h>
6 :
7 : /* Implementation note:
8 :
9 : The lexer/parser of fd_toml.c is a simpler backtracking recursive
10 : descent parser. A minimal amount of lookahead tuning is implemented;
11 : mostly just fast failure paths. Obvious performance wins are
12 : possible by adding more speculative lookaheads that lead the CPU down
13 : "happy paths" such as long strings of ASCII.
14 :
15 : The indexer into fd_pod blindly inserts using fd_pod_insert, which
16 : may not be the most efficient allocation strategy. */
17 :
18 : /* fd_toml_cur_t is a cursor object. It is safe to copy this object via
19 : assignment to implement backtracking. */
20 :
21 : struct fd_toml_cur {
22 : ulong lineno;
23 : char const * data;
24 : };
25 :
26 : typedef struct fd_toml_cur fd_toml_cur_t;
27 :
28 : /* fd_toml_parser_t is the internal parser state. It implements the
29 : lexer/parser itself, logic to unescape and buffer, and logic to
30 : compose the data into an fd_pod_t. */
31 :
32 : struct fd_toml_parser {
33 : fd_toml_cur_t c;
34 : char const * data_end; /* points one past EOF */
35 : uchar * pod; /* pod provided by user */
36 : int error; /* hint: fatal pod error occurred */
37 :
38 : /* The current buffered string (either for both keys and values) */
39 :
40 : uchar * scratch; /* base of scratch buf */
41 : uchar * scratch_cur; /* next free byte in scratch buf */
42 : uchar * scratch_end; /* points one past scratch buf */
43 :
44 : /* Buffered keys */
45 :
46 : uint key_len;
47 : char key[ FD_TOML_PATH_MAX ]; /* cstr */
48 : };
49 :
50 : typedef struct fd_toml_parser fd_toml_parser_t;
51 :
52 : /* Accumulate and insert data into fd_pod *****************************/
53 :
54 : static void
55 783 : fd_toml_str_init( fd_toml_parser_t * parser ) {
56 783 : parser->scratch_cur = parser->scratch;
57 783 : }
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 9921 : int c ) {
77 :
78 9921 : 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 9921 : parser->scratch_cur[0] = (uchar)c;
84 9921 : parser->scratch_cur++;
85 9921 : return 1;
86 9921 : }
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 5916 : ulong n ) {
114 :
115 5916 : char const * p = parser->c.data;
116 5916 : char const * next = p + n;
117 5916 : 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 5916 : ulong lines = 0UL;
123 11832 : for( ; p < next; p++ ) {
124 5916 : if( *p == '\n' ) lines++;
125 5916 : }
126 :
127 5916 : parser->c.lineno += lines;
128 5916 : parser->c.data = next;
129 5916 : }
130 :
131 : static inline void
132 : fd_toml_advance_inline( fd_toml_parser_t * parser,
133 249957 : ulong n ) {
134 249957 : parser->c.data += n;
135 249957 : }
136 :
137 : static int
138 135 : fd_toml_upsert_empty_pod( fd_toml_parser_t * parser ) {
139 135 : if( !fd_pod_query_subpod( parser->pod, parser->key ) ) {
140 135 : uchar subpod_mem[ FD_POD_FOOTPRINT_MIN ];
141 135 : uchar * subpod = fd_pod_join( fd_pod_new( subpod_mem, FD_POD_FOOTPRINT_MIN ) );
142 135 : 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 135 : fd_pod_delete( fd_pod_leave( subpod ) );
147 135 : }
148 135 : return 1;
149 135 : }
150 :
151 : /* fd_toml_avail returns the number of bytes available for parsing. */
152 :
153 : static inline ulong
154 299892 : fd_toml_avail( fd_toml_parser_t const * parser ) {
155 299892 : if( FD_UNLIKELY( parser->c.data > parser->data_end ) ) {
156 0 : FD_LOG_CRIT(( "Parse cursor is out of bounds" ));
157 0 : }
158 299892 : return (ulong)parser->data_end - (ulong)parser->c.data;
159 299892 : }
160 :
161 : #define SUB_PARSE( fn_call ) \
162 19371 : __extension__ ({ \
163 19371 : fd_toml_cur_t const _macro_backtrack = parser->c; \
164 19371 : int ret = fn_call; \
165 19371 : if( !ret ) { \
166 13644 : if( parser->error ) return 0; \
167 13644 : parser->c = _macro_backtrack; \
168 13644 : } \
169 19371 : ret; \
170 19371 : })
171 :
172 : #define EXPECT_CHAR(_c) \
173 6096 : do { \
174 6096 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0; \
175 6096 : if( FD_UNLIKELY( parser->c.data[0] != (_c) ) ) return 0; \
176 6093 : fd_toml_advance_inline( parser, 1UL ); \
177 330 : } 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 7752 : fd_toml_parse_ws( fd_toml_parser_t * parser ) {
196 :
197 32838 : while( fd_toml_avail( parser ) ) {
198 32826 : char c = parser->c.data[0];
199 32826 : if( c != ' ' && c != '\t' ) break;
200 25086 : fd_toml_advance_inline( parser, 1UL );
201 25086 : }
202 :
203 7752 : return 1;
204 7752 : }
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 5424 : fd_toml_parse_comment( fd_toml_parser_t * parser ) {
214 5424 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
215 5412 : if( FD_UNLIKELY( parser->c.data[0] != '#' ) ) return 0;
216 4329 : fd_toml_advance_inline( parser, 1UL );
217 :
218 212868 : while( fd_toml_avail( parser ) ) {
219 212868 : uint c = (uchar)parser->c.data[0];
220 212868 : if( FD_LIKELY( (c==0x09) |
221 212868 : (c>=0x20 && c<0x7F) |
222 212868 : (c>=0x80) ) ) {
223 208539 : fd_toml_advance_inline( parser, 1UL );
224 208539 : } else {
225 4329 : break;
226 4329 : }
227 212868 : }
228 :
229 4329 : return 1;
230 5412 : }
231 :
232 : /* quotation-mark = %x22 */
233 :
234 : static int
235 6168 : fd_toml_parse_quotation_mark( fd_toml_parser_t * parser ) {
236 6168 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
237 6165 : if( FD_UNLIKELY( parser->c.data[0] != '"' ) ) return 0;
238 306 : fd_toml_advance_inline( parser, 1UL );
239 306 : return 1;
240 6165 : }
241 :
242 : /* basic-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii */
243 :
244 : static int
245 729 : fd_toml_parse_basic_unescaped( fd_toml_parser_t * parser ) {
246 :
247 729 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
248 :
249 729 : int c = (uchar)parser->c.data[0];
250 729 : if( FD_LIKELY( (c==' ') | (c=='\t') |
251 729 : (c==0x21) |
252 729 : (c>=0x23 && c<=0x5B) |
253 729 : (c>=0x5D && c<=0x7E) |
254 729 : (c>=0x80) ) ) { /* ok */ }
255 153 : else {
256 153 : return 0;
257 153 : }
258 :
259 576 : fd_toml_str_append_byte( parser, (uchar)c );
260 576 : fd_toml_advance( parser, 1UL );
261 576 : return 1;
262 729 : }
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 153 : fd_toml_parse_escaped( fd_toml_parser_t * parser ) {
288 :
289 153 : if( FD_UNLIKELY( fd_toml_avail( parser ) < 2UL ) ) return 0;
290 150 : if( FD_UNLIKELY( parser->c.data[0] != '\\' ) ) return 0;
291 0 : int kind = parser->c.data[1];
292 0 : fd_toml_advance_inline( parser, 2UL );
293 :
294 0 : int valid = 1;
295 0 : uint rune;
296 0 : switch( kind ) {
297 0 : case 'b':
298 0 : fd_toml_str_append_byte( parser, '\b' );
299 0 : return 1;
300 0 : case 'f':
301 0 : fd_toml_str_append_byte( parser, '\f' );
302 0 : return 1;
303 0 : case 'n':
304 0 : fd_toml_str_append_byte( parser, '\n' );
305 0 : return 1;
306 0 : case 'r':
307 0 : fd_toml_str_append_byte( parser, '\r' );
308 0 : return 1;
309 0 : case 't':
310 0 : fd_toml_str_append_byte( parser, '\t' );
311 0 : return 1;
312 0 : case '"':
313 0 : case '\\':
314 0 : fd_toml_str_append_byte( parser, kind );
315 0 : return 1;
316 0 : case 'u':
317 0 : if( FD_UNLIKELY( fd_toml_avail( parser ) < 4UL ) ) return 0;
318 0 : for( ulong j=0; j<4; j++ ) valid &= fd_isxdigit( parser->c.data[j] );
319 0 : if( FD_UNLIKELY( !valid ) ) return 0;
320 0 : rune = ( fd_toml_xdigit( parser->c.data[0] )<<12 );
321 0 : rune |= ( fd_toml_xdigit( parser->c.data[1] )<< 8 );
322 0 : rune |= ( fd_toml_xdigit( parser->c.data[2] )<< 4 );
323 0 : rune |= ( fd_toml_xdigit( parser->c.data[3] ) );
324 0 : if( FD_UNLIKELY( !fd_toml_str_append_utf8( parser, rune ) ) ) return 0;
325 0 : fd_toml_advance_inline( parser, 4UL );
326 0 : return 1;
327 0 : case 'U':
328 0 : if( FD_UNLIKELY( fd_toml_avail( parser ) < 8UL ) ) return 0;
329 0 : for( ulong j=0; j<8; j++ ) valid &= fd_isxdigit( parser->c.data[j] );
330 0 : if( FD_UNLIKELY( !valid ) ) return 0;
331 0 : rune = ( fd_toml_xdigit( parser->c.data[0] )<<28 );
332 0 : rune |= ( fd_toml_xdigit( parser->c.data[1] )<<24 );
333 0 : rune |= ( fd_toml_xdigit( parser->c.data[2] )<<20 );
334 0 : rune |= ( fd_toml_xdigit( parser->c.data[3] )<<16 );
335 0 : rune |= ( fd_toml_xdigit( parser->c.data[4] )<<12 );
336 0 : rune |= ( fd_toml_xdigit( parser->c.data[5] )<< 8 );
337 0 : rune |= ( fd_toml_xdigit( parser->c.data[6] )<< 4 );
338 0 : rune |= ( fd_toml_xdigit( parser->c.data[7] ) );
339 0 : if( FD_UNLIKELY( !fd_toml_str_append_utf8( parser, rune ) ) ) return 0;
340 0 : fd_toml_advance_inline( parser, 8UL );
341 0 : return 1;
342 0 : default:
343 0 : return 0;
344 0 : }
345 0 : }
346 :
347 : /* basic-char = basic-unescaped / escaped */
348 :
349 : static int
350 729 : fd_toml_parse_basic_char( fd_toml_parser_t * parser ) {
351 729 : if( FD_LIKELY( SUB_PARSE( fd_toml_parse_basic_unescaped( parser ) ) ) ) return 1;
352 153 : if( FD_LIKELY( SUB_PARSE( fd_toml_parse_escaped ( parser ) ) ) ) return 1;
353 153 : return 0;
354 153 : }
355 :
356 : /* basic-string = quotation-mark *basic-char quotation-mark */
357 :
358 : static int
359 6015 : fd_toml_parse_basic_string( fd_toml_parser_t * parser ) {
360 6015 : if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_quotation_mark( parser ) ) ) ) return 0;
361 153 : fd_toml_str_init( parser );
362 729 : while( SUB_PARSE( fd_toml_parse_basic_char( parser ) ) ) {}
363 153 : if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_quotation_mark( parser ) ) ) ) return 0;
364 153 : return 1;
365 153 : }
366 :
367 : /* apostrophe = %x27 ; ' apostrophe */
368 :
369 : static int
370 5862 : fd_toml_parse_apostrophe( fd_toml_parser_t * parser ) {
371 5862 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
372 5859 : if( FD_UNLIKELY( parser->c.data[0] != '\'' ) ) return 0;
373 0 : fd_toml_advance_inline( parser, 1UL );
374 0 : return 1;
375 5859 : }
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 5862 : fd_toml_parse_literal_string( fd_toml_parser_t * parser ) {
402 5862 : 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 5514 : fd_toml_parse_quoted_key( fd_toml_parser_t * parser ) {
413 5514 : if( FD_LIKELY( SUB_PARSE( fd_toml_parse_basic_string ( parser ) ) ) ) return 1;
414 5514 : if( FD_LIKELY( SUB_PARSE( fd_toml_parse_literal_string( parser ) ) ) ) return 1;
415 5514 : return 0;
416 5514 : }
417 :
418 : /* unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _ */
419 :
420 : static int
421 14703 : fd_toml_is_unquoted_key_char( int c ) {
422 14703 : return (c>='A' && c<='Z') |
423 14703 : (c>='a' && c<='z') |
424 14703 : (c>='0' && c<='9') |
425 14703 : (c=='-') |
426 14703 : (c=='_');
427 14703 : }
428 :
429 : static int
430 5514 : fd_toml_parse_unquoted_key( fd_toml_parser_t * parser ) {
431 5514 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
432 5511 : int c = (uchar)parser->c.data[0];
433 5511 : if( FD_UNLIKELY( !fd_toml_is_unquoted_key_char( c ) ) ) return 0;
434 630 : fd_toml_str_init( parser );
435 :
436 630 : fd_toml_str_append_byte( parser, c );
437 630 : fd_toml_advance_inline( parser, 1UL );
438 :
439 9192 : while( fd_toml_avail( parser ) ) {
440 9192 : c = (uchar)parser->c.data[0];
441 9192 : if( FD_LIKELY( fd_toml_is_unquoted_key_char( c ) ) ) {
442 8562 : fd_toml_str_append_byte( parser, c );
443 8562 : fd_toml_advance_inline( parser, 1UL );
444 8562 : } else {
445 630 : break;
446 630 : }
447 9192 : }
448 630 : return 1;
449 5511 : }
450 :
451 : /* simple-key = quoted-key / unquoted-key */
452 :
453 : static int
454 5514 : fd_toml_parse_simple_key( fd_toml_parser_t * parser ) {
455 5514 : if( FD_LIKELY( SUB_PARSE( fd_toml_parse_quoted_key ( parser ) ) ) ) goto add;
456 5514 : if( FD_LIKELY( SUB_PARSE( fd_toml_parse_unquoted_key( parser ) ) ) ) goto add;
457 4884 : return 0;
458 :
459 630 : add:
460 630 : do {
461 630 : uint old_key_len = parser->key_len;
462 630 : ulong suffix_len = (ulong)parser->scratch_cur - (ulong)parser->scratch;
463 630 : ulong key_len = (ulong)old_key_len + suffix_len + 1;
464 630 : 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 630 : char * key_cur = fd_cstr_init( parser->key + old_key_len );
473 630 : key_cur = fd_cstr_append_text( key_cur, (char const *)parser->scratch, suffix_len );
474 630 : fd_cstr_fini( key_cur );
475 630 : parser->key_len = (uint)( key_cur - parser->key );
476 630 : return 1;
477 630 : } while(0);
478 630 : }
479 :
480 : /* dot-sep = ws %x2E ws ; . Period */
481 :
482 : static int
483 630 : fd_toml_parse_dot_sep( fd_toml_parser_t * parser ) {
484 630 : fd_toml_parse_ws( parser );
485 630 : EXPECT_CHAR( '.' );
486 60 : fd_toml_parse_ws( parser );
487 60 : return 1;
488 630 : }
489 :
490 : /* dotted-key = simple-key 1*( dot-sep simple-key ) */
491 :
492 : static int
493 5454 : fd_toml_parse_dotted_key( fd_toml_parser_t * parser ) {
494 5454 : if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_simple_key( parser ) ) ) ) return 0;
495 630 : while( fd_toml_avail( parser ) ) {
496 630 : if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_dot_sep( parser ) ) ) ) break;
497 :
498 : /* Add trailing dot */
499 60 : if( parser->key_len + 2 > sizeof(parser->key) ) {
500 0 : parser->error = FD_TOML_ERR_KEY;
501 0 : return 0;
502 0 : }
503 60 : parser->key[ parser->key_len++ ] = '.';
504 60 : parser->key[ parser->key_len ] = '\x00';
505 :
506 60 : if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_simple_key( parser ) ) ) ) return 0;
507 60 : }
508 570 : return 1;
509 570 : }
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 5454 : fd_toml_parse_key( fd_toml_parser_t * parser ) {
517 5454 : return fd_toml_parse_dotted_key( parser );
518 5454 : }
519 :
520 : /* keyval-sep = ws %x3D ws */
521 :
522 : static int
523 468 : fd_toml_parse_keyval_sep( fd_toml_parser_t * parser ) {
524 468 : fd_toml_parse_ws( parser );
525 468 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
526 468 : if( FD_UNLIKELY( parser->c.data[0] != '=' ) ) return 0;
527 468 : fd_toml_advance_inline( parser, 1UL );
528 468 : fd_toml_parse_ws( parser );
529 468 : return 1;
530 468 : }
531 :
532 : /* ml-basic-string-delim = 3quotation-mark */
533 :
534 : static int
535 501 : fd_toml_parse_ml_basic_string_delim( fd_toml_parser_t * parser ) {
536 501 : if( FD_UNLIKELY( parser->c.data + 3 > parser->data_end ) ) return 0;
537 501 : if( FD_UNLIKELY( ( parser->c.data[0] != '"' ) |
538 501 : ( parser->c.data[1] != '"' ) |
539 501 : ( parser->c.data[2] != '"' ) ) ) return 0;
540 0 : fd_toml_advance_inline( parser, 3UL );
541 0 : return 1;
542 501 : }
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 501 : fd_toml_parse_ml_basic_string( fd_toml_parser_t * parser ) {
636 501 : 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 348 : fd_toml_parse_ml_literal_string_delim( fd_toml_parser_t * parser ) {
717 348 : if( FD_UNLIKELY( parser->c.data + 3 > parser->data_end ) ) return 0;
718 348 : if( FD_UNLIKELY( ( parser->c.data[0] != '\'' ) |
719 348 : ( parser->c.data[1] != '\'' ) |
720 348 : ( parser->c.data[2] != '\'' ) ) ) return 0;
721 0 : fd_toml_advance_inline( parser, 3UL );
722 0 : return 1;
723 348 : }
724 :
725 : /* ml-literal-string = ml-literal-string-delim [ newline ] ml-literal-body
726 : ml-literal-string-delim */
727 :
728 : static int
729 348 : fd_toml_parse_ml_literal_string( fd_toml_parser_t * parser ) {
730 348 : 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 501 : fd_toml_parse_string( fd_toml_parser_t * parser ) {
745 501 : if( FD_LIKELY( SUB_PARSE( fd_toml_parse_ml_basic_string ( parser ) ) ) ) goto add;
746 501 : if( FD_LIKELY( SUB_PARSE( fd_toml_parse_basic_string ( parser ) ) ) ) goto add;
747 348 : if( FD_LIKELY( SUB_PARSE( fd_toml_parse_ml_literal_string( parser ) ) ) ) goto add;
748 348 : if( FD_LIKELY( SUB_PARSE( fd_toml_parse_literal_string ( parser ) ) ) ) goto add;
749 348 : return 0;
750 153 : add:
751 153 : if( FD_UNLIKELY( !fd_toml_str_append_byte( parser, 0 ) ) ) return 0;
752 153 : if( FD_UNLIKELY( !fd_pod_insert(
753 153 : parser->pod, parser->key, FD_POD_VAL_TYPE_CSTR,
754 153 : (ulong)parser->scratch_cur - (ulong)parser->scratch,
755 153 : (char *)parser->scratch ) ) ) {
756 0 : parser->error = FD_TOML_ERR_POD;
757 0 : return 0;
758 0 : }
759 153 : return 1;
760 153 : }
761 :
762 : /* boolean = true / false */
763 :
764 : static int
765 348 : fd_toml_parse_boolean( fd_toml_parser_t * parser ) {
766 348 : int boolv = 0;
767 348 : if( parser->c.data + 4 > parser->data_end ) return 0;
768 348 : if( 0==memcmp( parser->c.data, "true", 4 ) ) {
769 42 : fd_toml_advance_inline( parser, 4 );
770 42 : boolv = 1;
771 42 : goto add;
772 42 : }
773 306 : if( parser->c.data + 5 > parser->data_end ) return 0;
774 306 : if( 0==memcmp( parser->c.data, "false", 5 ) ) {
775 66 : fd_toml_advance_inline( parser, 5 );
776 66 : boolv = 0;
777 66 : goto add;
778 66 : }
779 240 : return 0;
780 108 : add:
781 108 : 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 108 : return 1;
786 108 : }
787 :
788 : /* ws-comment-newline = *( wschar / [ comment ] newline ) */
789 :
790 : static int
791 72 : fd_toml_parse_ws_comment_newline_inner( fd_toml_parser_t * parser ) {
792 72 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
793 72 : int c = (uchar)parser->c.data[0];
794 72 : if( FD_UNLIKELY( c == ' ' || c == '\t' ) ) {
795 0 : fd_toml_advance_inline( parser, 1UL );
796 0 : return 1;
797 0 : }
798 144 : SUB_PARSE( fd_toml_parse_comment( parser ) );
799 72 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
800 72 : if( FD_UNLIKELY( parser->c.data[0] != '\n' ) ) return 0;
801 0 : fd_toml_advance( parser, 1UL );
802 0 : return 1;
803 72 : }
804 :
805 : static int
806 72 : fd_toml_parse_ws_comment_newline( fd_toml_parser_t * parser ) {
807 72 : while( SUB_PARSE( fd_toml_parse_ws_comment_newline_inner( parser ) ) ) {}
808 0 : return 1;
809 72 : }
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 33 : fd_toml_parse_array_values( fd_toml_parser_t * parser ) {
816 :
817 33 : uint old_len = parser->key_len;
818 33 : char * suffix_cstr = parser->key + parser->key_len;
819 33 : 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 33 : fd_toml_cur_t backtrack = parser->c;
828 33 : for( ulong j=0;; j++ ) {
829 33 : char * child_key = fd_cstr_append_char( suffix_cstr, '.' );
830 33 : child_key = fd_cstr_append_ulong_as_text( child_key, 0, 0, j, fd_ulong_base10_dig_cnt( j ) );
831 33 : fd_cstr_fini( child_key );
832 33 : parser->key_len = (uint)( child_key - parser->key );
833 :
834 33 : fd_toml_parse_ws_comment_newline( parser );
835 33 : if( FD_UNLIKELY( !fd_toml_parse_val( parser ) ) ) {
836 27 : parser->c = backtrack;
837 27 : break;
838 27 : }
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 33 : fd_cstr_fini( suffix_cstr );
856 33 : parser->key_len = old_len;
857 33 : return 1;
858 33 : }
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 240 : fd_toml_parse_array( fd_toml_parser_t * parser ) {
867 240 : uint key_len = parser->key_len;
868 :
869 240 : EXPECT_CHAR( '[' );
870 33 : fd_toml_upsert_empty_pod( parser );
871 33 : SUB_PARSE( fd_toml_parse_array_values ( parser ) );
872 33 : SUB_PARSE( fd_toml_parse_ws_comment_newline( parser ) );
873 33 : EXPECT_CHAR( ']' );
874 :
875 33 : parser->key_len = key_len;
876 33 : parser->key[ key_len ] = 0;
877 :
878 33 : return 1;
879 33 : }
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 207 : fd_toml_parse_inline_table( fd_toml_parser_t * parser ) {
921 :
922 207 : 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 324 : fd_toml_dec_t * dec ) {
966 :
967 324 : uint len;
968 324 : ulong digits = 0UL;
969 324 : int allow_underscore = 0;
970 1356 : for( len=0;; len++ ) {
971 1356 : if( FD_UNLIKELY( allow_underscore && parser->c.data[0] == '_' ) ) {
972 18 : allow_underscore = 0;
973 18 : fd_toml_advance_inline( parser, 1UL );
974 18 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
975 18 : if( FD_UNLIKELY( !fd_isdigit( parser->c.data[0] ) ) ) return 0;
976 1338 : } else {
977 1338 : int digit = (uchar)parser->c.data[0];
978 1338 : if( FD_UNLIKELY(
979 1338 : __builtin_umull_overflow( digits, 10, &digits ) ||
980 1338 : __builtin_uaddl_overflow( digits, (ulong)( digit - '0' ), &digits ) ) ) {
981 0 : parser->error = FD_TOML_ERR_RANGE;
982 0 : return 0;
983 0 : }
984 1338 : fd_toml_advance_inline( parser, 1UL );
985 1338 : if( !fd_toml_avail( parser ) ) break;
986 1338 : if( !fd_isdigit( parser->c.data[0] ) && parser->c.data[0] != '_' ) break;
987 1014 : allow_underscore = 1;
988 1014 : }
989 1356 : }
990 :
991 324 : dec->res = digits;
992 324 : dec->len = len;
993 324 : return 1;
994 324 : }
995 :
996 : static int
997 : fd_toml_parse_dec_int_( fd_toml_parser_t * parser,
998 414 : fd_toml_dec_t * dec ) {
999 414 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
1000 414 : int c = (uchar)parser->c.data[0];
1001 :
1002 414 : int neg = 0;
1003 414 : 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 414 : }
1011 :
1012 414 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
1013 414 : int first_digit = (uchar)parser->c.data[0];
1014 414 : if( first_digit == '0' ) {
1015 36 : dec->res = 0UL;
1016 36 : dec->neg = 0;
1017 36 : fd_toml_advance_inline( parser, 1UL );
1018 36 : return 1;
1019 36 : }
1020 :
1021 378 : if( FD_UNLIKELY( first_digit<='0' || first_digit>'9' ) ) return 0;
1022 :
1023 324 : dec->neg = !!neg;
1024 324 : return fd_toml_parse_zero_prefixable_int( parser, dec );
1025 378 : }
1026 :
1027 : static int
1028 207 : fd_toml_parse_dec_int( fd_toml_parser_t * parser ) {
1029 207 : fd_toml_dec_t dec = {0};
1030 207 : if( FD_UNLIKELY( !fd_toml_parse_dec_int_( parser, &dec ) ) ) return 0;
1031 180 : long val = (long)dec.res;
1032 180 : val = fd_long_if( dec.neg, -val, val );
1033 180 : 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 180 : return 1;
1038 180 : }
1039 :
1040 : /* hex-int = hex-prefix HEXDIG *( HEXDIG / underscore HEXDIG ) */
1041 :
1042 : static int
1043 207 : fd_toml_parse_hex_int( fd_toml_parser_t * parser ) {
1044 207 : if( FD_UNLIKELY( fd_toml_avail( parser ) < 3 ) ) return 0;
1045 207 : if( FD_UNLIKELY( parser->c.data[0] != '0' ) ) return 0;
1046 18 : if( FD_UNLIKELY( parser->c.data[1] != 'x' ) ) return 0;
1047 0 : if( FD_UNLIKELY( !fd_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( !fd_isxdigit( parser->c.data[0] ) ) ) return 0;
1059 0 : } else {
1060 0 : if( !fd_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 207 : fd_toml_parse_oct_int( fd_toml_parser_t * parser ) {
1090 207 : if( FD_UNLIKELY( fd_toml_avail( parser ) < 3 ) ) return 0;
1091 207 : if( FD_UNLIKELY( parser->c.data[0] != '0' ) ) return 0;
1092 18 : 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 207 : fd_toml_parse_bin_int( fd_toml_parser_t * parser ) {
1136 207 : if( FD_UNLIKELY( fd_toml_avail( parser ) < 3 ) ) return 0;
1137 207 : if( FD_UNLIKELY( parser->c.data[0] != '0' ) ) return 0;
1138 18 : 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 207 : fd_toml_parse_integer( fd_toml_parser_t * parser ) {
1179 207 : if( SUB_PARSE( fd_toml_parse_hex_int( parser ) ) ) return 1;
1180 207 : if( SUB_PARSE( fd_toml_parse_oct_int( parser ) ) ) return 1;
1181 207 : if( SUB_PARSE( fd_toml_parse_bin_int( parser ) ) ) return 1;
1182 207 : if( SUB_PARSE( fd_toml_parse_dec_int( parser ) ) ) return 1;
1183 27 : return 0;
1184 207 : }
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 180 : fd_toml_dec_t * exp ) {
1192 180 : if( FD_UNLIKELY( fd_toml_avail( parser ) < 2 ) ) return 0;
1193 180 : switch( parser->c.data[0] ) {
1194 0 : case 'e': case 'E': break;
1195 180 : default: return 0;
1196 180 : }
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 180 : fd_toml_dec_t * frac ) {
1221 180 : if( FD_UNLIKELY( fd_toml_avail( parser ) < 2 ) ) return 0;
1222 180 : 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 207 : fd_toml_parse_float_normal( fd_toml_parser_t * parser ) {
1236 :
1237 207 : fd_toml_dec_t stem = {0};
1238 207 : if( FD_UNLIKELY( !fd_toml_parse_dec_int_( parser, &stem ) ) ) return 0;
1239 180 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) return 0;
1240 180 : float res = (float)stem.res;
1241 :
1242 180 : int ok = 0;
1243 180 : fd_toml_dec_t frac_dec = {0};
1244 180 : 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 180 : if( !SUB_PARSE( fd_toml_parse_exp( parser, &exp_dec ) ) ) {
1253 180 : if( FD_LIKELY( ok ) ) goto parsed;
1254 180 : return 0;
1255 180 : }
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 207 : fd_float_parse_float_special( fd_toml_parser_t * parser ) {
1274 207 : if( FD_UNLIKELY( fd_toml_avail( parser ) < 3 ) ) return 0;
1275 207 : int c = (uchar)parser->c.data[0];
1276 :
1277 207 : 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 207 : }
1283 :
1284 207 : char const * str = parser->c.data;
1285 207 : fd_toml_advance_inline( parser, 3UL );
1286 :
1287 207 : 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 207 : 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 207 : return 0;
1300 207 : }
1301 :
1302 : /* float = float-int-part ( exp / frac [ exp ] )
1303 : float =/ special-float */
1304 :
1305 : static int
1306 207 : fd_toml_parse_float( fd_toml_parser_t * parser ) {
1307 207 : if( SUB_PARSE( fd_toml_parse_float_normal ( parser ) ) ) return 1;
1308 207 : if( SUB_PARSE( fd_float_parse_float_special( parser ) ) ) return 1;
1309 207 : return 0;
1310 207 : }
1311 :
1312 : /* val = string / boolean / array / inline-table / float / integer */
1313 :
1314 : static int
1315 501 : fd_toml_parse_val( fd_toml_parser_t * parser ) {
1316 : /* consider some lookahead for better performance */
1317 501 : if( SUB_PARSE( fd_toml_parse_string ( parser ) ) ) return 1;
1318 348 : if( SUB_PARSE( fd_toml_parse_boolean ( parser ) ) ) return 1;
1319 240 : if( SUB_PARSE( fd_toml_parse_array ( parser ) ) ) return 1;
1320 207 : if( SUB_PARSE( fd_toml_parse_inline_table( parser ) ) ) return 1;
1321 : // if( SUB_PARSE( fd_toml_parse_date_time ( parser ) ) ) return 1; /* not supported */
1322 : /* NOTE: float and integer have a common dec-int prefix -- dedup for better performance */
1323 207 : if( SUB_PARSE( fd_toml_parse_float ( parser ) ) ) return 1;
1324 207 : if( SUB_PARSE( fd_toml_parse_integer ( parser ) ) ) return 1;
1325 27 : return 0;
1326 207 : }
1327 :
1328 : /* keyval = key keyval-sep val */
1329 :
1330 : static int
1331 5352 : fd_toml_parse_keyval( fd_toml_parser_t * parser ) {
1332 5352 : uint old_key_len = parser->key_len;
1333 5352 : if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_key( parser ) ) ) ) return 0;
1334 :
1335 468 : if( FD_UNLIKELY( fd_pod_query( parser->pod, parser->key, NULL )==FD_POD_SUCCESS ) ) {
1336 0 : FD_LOG_WARNING(( "TOML parse error: duplicate key: \"%s\"", parser->key ));
1337 0 : parser->error = FD_TOML_ERR_DUP;
1338 0 : return 0;
1339 0 : }
1340 :
1341 468 : if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_keyval_sep( parser ) ) ) ) return 0;
1342 468 : if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_val ( parser ) ) ) ) return 0;
1343 :
1344 468 : FD_LOG_DEBUG(( "Added key %s", parser->key ));
1345 468 : parser->key[ old_key_len ] = 0;
1346 468 : parser->key_len = old_key_len;
1347 468 : return 1;
1348 468 : }
1349 :
1350 : /* std-table = std-table-open key std-table-close
1351 :
1352 : std-table-open = %x5B ws ; [ Left square bracket
1353 : std-table-close = ws %x5D ; ] Right square bracket */
1354 :
1355 : static int
1356 4884 : fd_toml_parse_std_table( fd_toml_parser_t * parser ) {
1357 4884 : EXPECT_CHAR( '[' );
1358 102 : fd_toml_parse_ws( parser );
1359 :
1360 102 : parser->key[ parser->key_len = 0 ] = 0;
1361 102 : if( FD_UNLIKELY( !fd_toml_parse_key( parser ) ) ) return 0;
1362 : // FIXME: consider blocking duplicate tables?
1363 : //if( FD_UNLIKELY( fd_pod_query( parser->pod, parser->key, NULL )==FD_POD_SUCCESS ) ) {
1364 : // FD_LOG_WARNING(( "Duplicate table: \"%s\"", parser->key ));
1365 : // parser->error = FD_TOML_ERR_DUP;
1366 : // return 0;
1367 : //}
1368 102 : FD_LOG_DEBUG(( "Added table %.*s", (int)parser->key_len, parser->key ));
1369 :
1370 102 : fd_toml_parse_ws( parser );
1371 102 : EXPECT_CHAR( ']' );
1372 102 : return 1;
1373 102 : }
1374 :
1375 : /* array-table = array-table = array-table-open key array-table-close
1376 :
1377 : array-table-open = %x5B.5B ws ; [[ Double left square bracket
1378 : array-table-close = ws %x5D.5D ; ]] Double right square bracket */
1379 :
1380 : static int
1381 4884 : fd_toml_parse_array_table( fd_toml_parser_t * parser ) {
1382 4884 : if( fd_toml_avail( parser ) < 2UL ) return 0;
1383 4881 : if( ( parser->c.data[0] != '[' ) |
1384 4881 : ( parser->c.data[1] != '[' ) ) return 0;
1385 0 : fd_toml_advance_inline( parser, 2UL );
1386 :
1387 0 : fd_toml_parse_ws( parser );
1388 :
1389 : /* Set parser->key to path to array */
1390 :
1391 0 : parser->key[ parser->key_len = 0 ] = 0;
1392 0 : if( FD_UNLIKELY( !SUB_PARSE( fd_toml_parse_key( parser ) ) ) ) return 0;
1393 :
1394 : /* Count number of predecessors */
1395 :
1396 0 : ulong idx = 0UL;
1397 0 : uchar const * subpod = fd_pod_query_subpod( parser->pod, parser->key );
1398 0 : if( subpod ) {
1399 0 : idx = fd_pod_cnt( subpod );
1400 0 : }
1401 :
1402 : /* Append array index to path */
1403 :
1404 0 : char * key_c = parser->key + parser->key_len;
1405 0 : if( FD_UNLIKELY( key_c + 22 > parser->key + sizeof(parser->key) ) ) {
1406 : /* array index might be OOB (see python3 -c 'print(len(str(1<<64)))') */
1407 0 : parser->error = FD_TOML_ERR_KEY;
1408 0 : return 0;
1409 0 : }
1410 0 : key_c = fd_cstr_append_char( key_c, '.' );
1411 0 : key_c = fd_cstr_append_ulong_as_text( key_c, 0, 0, idx, fd_ulong_base10_dig_cnt( idx ) );
1412 0 : fd_cstr_fini( key_c );
1413 0 : parser->key_len = (uint)( key_c - parser->key );
1414 :
1415 0 : FD_LOG_DEBUG(( "Added array table %.*s", (int)parser->key_len, parser->key ));
1416 :
1417 : /* Continue parsing */
1418 :
1419 0 : fd_toml_parse_ws( parser );
1420 :
1421 0 : if( FD_UNLIKELY( fd_toml_avail( parser ) < 2UL ) ) return 0;
1422 0 : if( FD_UNLIKELY( ( parser->c.data[0] != ']' ) |
1423 0 : ( parser->c.data[1] != ']' ) ) ) return 0;
1424 0 : fd_toml_advance_inline( parser, 2UL );
1425 0 : return 1;
1426 0 : }
1427 :
1428 : /* table = std-table / array-table */
1429 :
1430 : static int
1431 4884 : fd_toml_parse_table( fd_toml_parser_t * parser ) {
1432 4884 : if( SUB_PARSE( fd_toml_parse_array_table( parser ) ) ) goto add;
1433 4884 : if( SUB_PARSE( fd_toml_parse_std_table ( parser ) ) ) goto add;
1434 4782 : return 0;
1435 102 : add:
1436 102 : fd_toml_upsert_empty_pod( parser );
1437 : /* Add trailing dot */
1438 102 : if( parser->key_len + 2 > sizeof(parser->key) ) {
1439 0 : parser->error = FD_TOML_ERR_KEY;
1440 0 : return 0;
1441 0 : }
1442 102 : parser->key[ parser->key_len++ ] = '.';
1443 102 : parser->key[ parser->key_len ] = '\x00';
1444 102 : return 1;
1445 102 : }
1446 :
1447 : /* expression = ws [ comment ]
1448 : expression =/ ws keyval ws [ comment ]
1449 : expression =/ ws table ws [ comment ] */
1450 :
1451 : static int
1452 5352 : fd_toml_parse_expression( fd_toml_parser_t * parser ) {
1453 :
1454 5352 : fd_toml_parse_ws( parser );
1455 :
1456 5352 : if( FD_LIKELY( SUB_PARSE( fd_toml_parse_keyval( parser ) ) ) ) {
1457 468 : fd_toml_parse_ws( parser );
1458 468 : }
1459 4884 : else if( FD_LIKELY( SUB_PARSE( fd_toml_parse_table( parser ) ) ) ) {
1460 102 : fd_toml_parse_ws( parser );
1461 102 : }
1462 :
1463 5352 : SUB_PARSE( fd_toml_parse_comment( parser ) );
1464 0 : return 1;
1465 5352 : }
1466 :
1467 : /* toml = expression *( newline expression ) */
1468 :
1469 : static int
1470 12 : fd_toml_parse_toml( fd_toml_parser_t * parser ) {
1471 :
1472 12 : if( FD_UNLIKELY( !fd_toml_parse_expression( parser ) ) ) return 0;
1473 :
1474 5352 : for(;;) {
1475 5352 : if( FD_UNLIKELY( parser->error ) ) break;
1476 5352 : if( FD_UNLIKELY( !fd_toml_avail( parser ) ) ) break;
1477 5340 : if( FD_UNLIKELY( parser->c.data[0] != '\n' ) ) break;
1478 5340 : fd_toml_advance( parser, 1UL );
1479 5340 : if( FD_UNLIKELY( !fd_toml_parse_expression( parser ) ) ) return 0;
1480 5340 : }
1481 :
1482 12 : return 1;
1483 12 : }
1484 :
1485 : int
1486 : fd_toml_parse( void const * toml,
1487 : ulong toml_sz,
1488 : uchar * pod,
1489 : uchar * scratch,
1490 : ulong scratch_sz,
1491 12 : fd_toml_err_info_t * opt_err ) {
1492 :
1493 12 : static fd_toml_err_info_t _dummy_err[1];
1494 12 : if( !opt_err ) opt_err = _dummy_err;
1495 12 : opt_err->line = 0UL;
1496 :
1497 12 : if( FD_UNLIKELY( !toml_sz ) ) return FD_TOML_SUCCESS;
1498 12 : if( FD_UNLIKELY( !scratch_sz ) ) {
1499 0 : FD_LOG_WARNING(( "zero scratch_sz" ));
1500 0 : return FD_TOML_ERR_SCRATCH;
1501 0 : }
1502 :
1503 12 : fd_toml_parser_t parser[1] = {{
1504 12 : .c = {
1505 12 : .data = toml,
1506 12 : .lineno = 1UL,
1507 12 : },
1508 12 : .data_end = (char const *)toml + toml_sz,
1509 12 : .pod = pod,
1510 12 : .scratch = scratch,
1511 12 : .scratch_cur = scratch,
1512 12 : .scratch_end = scratch + scratch_sz
1513 12 : }};
1514 :
1515 12 : int ok = fd_toml_parse_toml( parser );
1516 12 : opt_err->line = parser->c.lineno;
1517 :
1518 12 : if( FD_UNLIKELY( (!ok) | (fd_toml_avail( parser ) > 0) ) ) {
1519 0 : return fd_int_if( !!parser->error, parser->error, FD_TOML_ERR_PARSE );
1520 0 : }
1521 :
1522 12 : return FD_TOML_SUCCESS;
1523 12 : }
1524 :
1525 : FD_FN_CONST char const *
1526 0 : fd_toml_strerror( int err ) {
1527 0 : switch( err ) {
1528 0 : case FD_TOML_SUCCESS: return "success";
1529 0 : case FD_TOML_ERR_POD: return "out of memory in output pod";
1530 0 : case FD_TOML_ERR_SCRATCH: return "out of memory in scratch region";
1531 0 : case FD_TOML_ERR_KEY: return "oversize key";
1532 0 : case FD_TOML_ERR_DUP: return "duplicate key";
1533 0 : case FD_TOML_ERR_RANGE: return "integer overflow";
1534 0 : case FD_TOML_ERR_PARSE: return "parse failure";
1535 0 : default: return "unknown error";
1536 0 : }
1537 0 : }
|