1#include "test/jemalloc_test.h"
2
3#include "jemalloc/internal/util.h"
4
5typedef enum {
6	TOKEN_TYPE_NONE,
7	TOKEN_TYPE_ERROR,
8	TOKEN_TYPE_EOI,
9	TOKEN_TYPE_NULL,
10	TOKEN_TYPE_FALSE,
11	TOKEN_TYPE_TRUE,
12	TOKEN_TYPE_LBRACKET,
13	TOKEN_TYPE_RBRACKET,
14	TOKEN_TYPE_LBRACE,
15	TOKEN_TYPE_RBRACE,
16	TOKEN_TYPE_COLON,
17	TOKEN_TYPE_COMMA,
18	TOKEN_TYPE_STRING,
19	TOKEN_TYPE_NUMBER
20} token_type_t;
21
22typedef struct parser_s parser_t;
23typedef struct {
24	parser_t	*parser;
25	token_type_t	token_type;
26	size_t		pos;
27	size_t		len;
28	size_t		line;
29	size_t		col;
30} token_t;
31
32struct parser_s {
33	bool verbose;
34	char	*buf; /* '\0'-terminated. */
35	size_t	len; /* Number of characters preceding '\0' in buf. */
36	size_t	pos;
37	size_t	line;
38	size_t	col;
39	token_t	token;
40};
41
42static void
43token_init(token_t *token, parser_t *parser, token_type_t token_type,
44    size_t pos, size_t len, size_t line, size_t col) {
45	token->parser = parser;
46	token->token_type = token_type;
47	token->pos = pos;
48	token->len = len;
49	token->line = line;
50	token->col = col;
51}
52
53static void
54token_error(token_t *token) {
55	if (!token->parser->verbose) {
56		return;
57	}
58	switch (token->token_type) {
59	case TOKEN_TYPE_NONE:
60		not_reached();
61	case TOKEN_TYPE_ERROR:
62		malloc_printf("%zu:%zu: Unexpected character in token: ",
63		    token->line, token->col);
64		break;
65	default:
66		malloc_printf("%zu:%zu: Unexpected token: ", token->line,
67		    token->col);
68		break;
69	}
70	UNUSED ssize_t err = malloc_write_fd(STDERR_FILENO,
71	    &token->parser->buf[token->pos], token->len);
72	malloc_printf("\n");
73}
74
75static void
76parser_init(parser_t *parser, bool verbose) {
77	parser->verbose = verbose;
78	parser->buf = NULL;
79	parser->len = 0;
80	parser->pos = 0;
81	parser->line = 1;
82	parser->col = 0;
83}
84
85static void
86parser_fini(parser_t *parser) {
87	if (parser->buf != NULL) {
88		dallocx(parser->buf, MALLOCX_TCACHE_NONE);
89	}
90}
91
92static bool
93parser_append(parser_t *parser, const char *str) {
94	size_t len = strlen(str);
95	char *buf = (parser->buf == NULL) ? mallocx(len + 1,
96	    MALLOCX_TCACHE_NONE) : rallocx(parser->buf, parser->len + len + 1,
97	    MALLOCX_TCACHE_NONE);
98	if (buf == NULL) {
99		return true;
100	}
101	memcpy(&buf[parser->len], str, len + 1);
102	parser->buf = buf;
103	parser->len += len;
104	return false;
105}
106
107static bool
108parser_tokenize(parser_t *parser) {
109	enum {
110		STATE_START,
111		STATE_EOI,
112		STATE_N, STATE_NU, STATE_NUL, STATE_NULL,
113		STATE_F, STATE_FA, STATE_FAL, STATE_FALS, STATE_FALSE,
114		STATE_T, STATE_TR, STATE_TRU, STATE_TRUE,
115		STATE_LBRACKET,
116		STATE_RBRACKET,
117		STATE_LBRACE,
118		STATE_RBRACE,
119		STATE_COLON,
120		STATE_COMMA,
121		STATE_CHARS,
122		STATE_CHAR_ESCAPE,
123		STATE_CHAR_U, STATE_CHAR_UD, STATE_CHAR_UDD, STATE_CHAR_UDDD,
124		STATE_STRING,
125		STATE_MINUS,
126		STATE_LEADING_ZERO,
127		STATE_DIGITS,
128		STATE_DECIMAL,
129		STATE_FRAC_DIGITS,
130		STATE_EXP,
131		STATE_EXP_SIGN,
132		STATE_EXP_DIGITS,
133		STATE_ACCEPT
134	} state = STATE_START;
135	size_t token_pos JEMALLOC_CC_SILENCE_INIT(0);
136	size_t token_line JEMALLOC_CC_SILENCE_INIT(1);
137	size_t token_col JEMALLOC_CC_SILENCE_INIT(0);
138
139	assert_zu_le(parser->pos, parser->len,
140	    "Position is past end of buffer");
141
142	while (state != STATE_ACCEPT) {
143		char c = parser->buf[parser->pos];
144
145		switch (state) {
146		case STATE_START:
147			token_pos = parser->pos;
148			token_line = parser->line;
149			token_col = parser->col;
150			switch (c) {
151			case ' ': case '\b': case '\n': case '\r': case '\t':
152				break;
153			case '\0':
154				state = STATE_EOI;
155				break;
156			case 'n':
157				state = STATE_N;
158				break;
159			case 'f':
160				state = STATE_F;
161				break;
162			case 't':
163				state = STATE_T;
164				break;
165			case '[':
166				state = STATE_LBRACKET;
167				break;
168			case ']':
169				state = STATE_RBRACKET;
170				break;
171			case '{':
172				state = STATE_LBRACE;
173				break;
174			case '}':
175				state = STATE_RBRACE;
176				break;
177			case ':':
178				state = STATE_COLON;
179				break;
180			case ',':
181				state = STATE_COMMA;
182				break;
183			case '"':
184				state = STATE_CHARS;
185				break;
186			case '-':
187				state = STATE_MINUS;
188				break;
189			case '0':
190				state = STATE_LEADING_ZERO;
191				break;
192			case '1': case '2': case '3': case '4':
193			case '5': case '6': case '7': case '8': case '9':
194				state = STATE_DIGITS;
195				break;
196			default:
197				token_init(&parser->token, parser,
198				    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
199				    - token_pos, token_line, token_col);
200				return true;
201			}
202			break;
203		case STATE_EOI:
204			token_init(&parser->token, parser,
205			    TOKEN_TYPE_EOI, token_pos, parser->pos -
206			    token_pos, token_line, token_col);
207			state = STATE_ACCEPT;
208			break;
209		case STATE_N:
210			switch (c) {
211			case 'u':
212				state = STATE_NU;
213				break;
214			default:
215				token_init(&parser->token, parser,
216				    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
217				    - token_pos, token_line, token_col);
218				return true;
219			}
220			break;
221		case STATE_NU:
222			switch (c) {
223			case 'l':
224				state = STATE_NUL;
225				break;
226			default:
227				token_init(&parser->token, parser,
228				    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
229				    - token_pos, token_line, token_col);
230				return true;
231			}
232			break;
233		case STATE_NUL:
234			switch (c) {
235			case 'l':
236				state = STATE_NULL;
237				break;
238			default:
239				token_init(&parser->token, parser,
240				    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
241				    - token_pos, token_line, token_col);
242				return true;
243			}
244			break;
245		case STATE_NULL:
246			switch (c) {
247			case ' ': case '\b': case '\n': case '\r': case '\t':
248			case '\0':
249			case '[': case ']': case '{': case '}': case ':':
250			case ',':
251				break;
252			default:
253				token_init(&parser->token, parser,
254				    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
255				    - token_pos, token_line, token_col);
256				return true;
257			}
258			token_init(&parser->token, parser, TOKEN_TYPE_NULL,
259			    token_pos, parser->pos - token_pos, token_line,
260			    token_col);
261			state = STATE_ACCEPT;
262			break;
263		case STATE_F:
264			switch (c) {
265			case 'a':
266				state = STATE_FA;
267				break;
268			default:
269				token_init(&parser->token, parser,
270				    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
271				    - token_pos, token_line, token_col);
272				return true;
273			}
274			break;
275		case STATE_FA:
276			switch (c) {
277			case 'l':
278				state = STATE_FAL;
279				break;
280			default:
281				token_init(&parser->token, parser,
282				    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
283				    - token_pos, token_line, token_col);
284				return true;
285			}
286			break;
287		case STATE_FAL:
288			switch (c) {
289			case 's':
290				state = STATE_FALS;
291				break;
292			default:
293				token_init(&parser->token, parser,
294				    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
295				    - token_pos, token_line, token_col);
296				return true;
297			}
298			break;
299		case STATE_FALS:
300			switch (c) {
301			case 'e':
302				state = STATE_FALSE;
303				break;
304			default:
305				token_init(&parser->token, parser,
306				    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
307				    - token_pos, token_line, token_col);
308				return true;
309			}
310			break;
311		case STATE_FALSE:
312			switch (c) {
313			case ' ': case '\b': case '\n': case '\r': case '\t':
314			case '\0':
315			case '[': case ']': case '{': case '}': case ':':
316			case ',':
317				break;
318			default:
319				token_init(&parser->token, parser,
320				    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
321				    - token_pos, token_line, token_col);
322				return true;
323			}
324			token_init(&parser->token, parser,
325			    TOKEN_TYPE_FALSE, token_pos, parser->pos -
326			    token_pos, token_line, token_col);
327			state = STATE_ACCEPT;
328			break;
329		case STATE_T:
330			switch (c) {
331			case 'r':
332				state = STATE_TR;
333				break;
334			default:
335				token_init(&parser->token, parser,
336				    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
337				    - token_pos, token_line, token_col);
338				return true;
339			}
340			break;
341		case STATE_TR:
342			switch (c) {
343			case 'u':
344				state = STATE_TRU;
345				break;
346			default:
347				token_init(&parser->token, parser,
348				    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
349				    - token_pos, token_line, token_col);
350				return true;
351			}
352			break;
353		case STATE_TRU:
354			switch (c) {
355			case 'e':
356				state = STATE_TRUE;
357				break;
358			default:
359				token_init(&parser->token, parser,
360				    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
361				    - token_pos, token_line, token_col);
362				return true;
363			}
364			break;
365		case STATE_TRUE:
366			switch (c) {
367			case ' ': case '\b': case '\n': case '\r': case '\t':
368			case '\0':
369			case '[': case ']': case '{': case '}': case ':':
370			case ',':
371				break;
372			default:
373				token_init(&parser->token, parser,
374				    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
375				    - token_pos, token_line, token_col);
376				return true;
377			}
378			token_init(&parser->token, parser, TOKEN_TYPE_TRUE,
379			    token_pos, parser->pos - token_pos, token_line,
380			    token_col);
381			state = STATE_ACCEPT;
382			break;
383		case STATE_LBRACKET:
384			token_init(&parser->token, parser, TOKEN_TYPE_LBRACKET,
385			    token_pos, parser->pos - token_pos, token_line,
386			    token_col);
387			state = STATE_ACCEPT;
388			break;
389		case STATE_RBRACKET:
390			token_init(&parser->token, parser, TOKEN_TYPE_RBRACKET,
391			    token_pos, parser->pos - token_pos, token_line,
392			    token_col);
393			state = STATE_ACCEPT;
394			break;
395		case STATE_LBRACE:
396			token_init(&parser->token, parser, TOKEN_TYPE_LBRACE,
397			    token_pos, parser->pos - token_pos, token_line,
398			    token_col);
399			state = STATE_ACCEPT;
400			break;
401		case STATE_RBRACE:
402			token_init(&parser->token, parser, TOKEN_TYPE_RBRACE,
403			    token_pos, parser->pos - token_pos, token_line,
404			    token_col);
405			state = STATE_ACCEPT;
406			break;
407		case STATE_COLON:
408			token_init(&parser->token, parser, TOKEN_TYPE_COLON,
409			    token_pos, parser->pos - token_pos, token_line,
410			    token_col);
411			state = STATE_ACCEPT;
412			break;
413		case STATE_COMMA:
414			token_init(&parser->token, parser, TOKEN_TYPE_COMMA,
415			    token_pos, parser->pos - token_pos, token_line,
416			    token_col);
417			state = STATE_ACCEPT;
418			break;
419		case STATE_CHARS:
420			switch (c) {
421			case '\\':
422				state = STATE_CHAR_ESCAPE;
423				break;
424			case '"':
425				state = STATE_STRING;
426				break;
427			case 0x00: case 0x01: case 0x02: case 0x03: case 0x04:
428			case 0x05: case 0x06: case 0x07: case 0x08: case 0x09:
429			case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e:
430			case 0x0f: case 0x10: case 0x11: case 0x12: case 0x13:
431			case 0x14: case 0x15: case 0x16: case 0x17: case 0x18:
432			case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d:
433			case 0x1e: case 0x1f:
434				token_init(&parser->token, parser,
435				    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
436				    - token_pos, token_line, token_col);
437				return true;
438			default:
439				break;
440			}
441			break;
442		case STATE_CHAR_ESCAPE:
443			switch (c) {
444			case '"': case '\\': case '/': case 'b': case 'n':
445			case 'r': case 't':
446				state = STATE_CHARS;
447				break;
448			case 'u':
449				state = STATE_CHAR_U;
450				break;
451			default:
452				token_init(&parser->token, parser,
453				    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
454				    - token_pos, token_line, token_col);
455				return true;
456			}
457			break;
458		case STATE_CHAR_U:
459			switch (c) {
460			case '0': case '1': case '2': case '3': case '4':
461			case '5': case '6': case '7': case '8': case '9':
462			case 'a': case 'b': case 'c': case 'd': case 'e':
463			case 'f':
464			case 'A': case 'B': case 'C': case 'D': case 'E':
465			case 'F':
466				state = STATE_CHAR_UD;
467				break;
468			default:
469				token_init(&parser->token, parser,
470				    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
471				    - token_pos, token_line, token_col);
472				return true;
473			}
474			break;
475		case STATE_CHAR_UD:
476			switch (c) {
477			case '0': case '1': case '2': case '3': case '4':
478			case '5': case '6': case '7': case '8': case '9':
479			case 'a': case 'b': case 'c': case 'd': case 'e':
480			case 'f':
481			case 'A': case 'B': case 'C': case 'D': case 'E':
482			case 'F':
483				state = STATE_CHAR_UDD;
484				break;
485			default:
486				token_init(&parser->token, parser,
487				    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
488				    - token_pos, token_line, token_col);
489				return true;
490			}
491			break;
492		case STATE_CHAR_UDD:
493			switch (c) {
494			case '0': case '1': case '2': case '3': case '4':
495			case '5': case '6': case '7': case '8': case '9':
496			case 'a': case 'b': case 'c': case 'd': case 'e':
497			case 'f':
498			case 'A': case 'B': case 'C': case 'D': case 'E':
499			case 'F':
500				state = STATE_CHAR_UDDD;
501				break;
502			default:
503				token_init(&parser->token, parser,
504				    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
505				    - token_pos, token_line, token_col);
506				return true;
507			}
508			break;
509		case STATE_CHAR_UDDD:
510			switch (c) {
511			case '0': case '1': case '2': case '3': case '4':
512			case '5': case '6': case '7': case '8': case '9':
513			case 'a': case 'b': case 'c': case 'd': case 'e':
514			case 'f':
515			case 'A': case 'B': case 'C': case 'D': case 'E':
516			case 'F':
517				state = STATE_CHARS;
518				break;
519			default:
520				token_init(&parser->token, parser,
521				    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
522				    - token_pos, token_line, token_col);
523				return true;
524			}
525			break;
526		case STATE_STRING:
527			token_init(&parser->token, parser, TOKEN_TYPE_STRING,
528			    token_pos, parser->pos - token_pos, token_line,
529			    token_col);
530			state = STATE_ACCEPT;
531			break;
532		case STATE_MINUS:
533			switch (c) {
534			case '0':
535				state = STATE_LEADING_ZERO;
536				break;
537			case '1': case '2': case '3': case '4':
538			case '5': case '6': case '7': case '8': case '9':
539				state = STATE_DIGITS;
540				break;
541			default:
542				token_init(&parser->token, parser,
543				    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
544				    - token_pos, token_line, token_col);
545				return true;
546			}
547			break;
548		case STATE_LEADING_ZERO:
549			switch (c) {
550			case '.':
551				state = STATE_DECIMAL;
552				break;
553			default:
554				token_init(&parser->token, parser,
555				    TOKEN_TYPE_NUMBER, token_pos, parser->pos -
556				    token_pos, token_line, token_col);
557				state = STATE_ACCEPT;
558				break;
559			}
560			break;
561		case STATE_DIGITS:
562			switch (c) {
563			case '0': case '1': case '2': case '3': case '4':
564			case '5': case '6': case '7': case '8': case '9':
565				break;
566			case '.':
567				state = STATE_DECIMAL;
568				break;
569			default:
570				token_init(&parser->token, parser,
571				    TOKEN_TYPE_NUMBER, token_pos, parser->pos -
572				    token_pos, token_line, token_col);
573				state = STATE_ACCEPT;
574				break;
575			}
576			break;
577		case STATE_DECIMAL:
578			switch (c) {
579			case '0': case '1': case '2': case '3': case '4':
580			case '5': case '6': case '7': case '8': case '9':
581				state = STATE_FRAC_DIGITS;
582				break;
583			default:
584				token_init(&parser->token, parser,
585				    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
586				    - token_pos, token_line, token_col);
587				return true;
588			}
589			break;
590		case STATE_FRAC_DIGITS:
591			switch (c) {
592			case '0': case '1': case '2': case '3': case '4':
593			case '5': case '6': case '7': case '8': case '9':
594				break;
595			case 'e': case 'E':
596				state = STATE_EXP;
597				break;
598			default:
599				token_init(&parser->token, parser,
600				    TOKEN_TYPE_NUMBER, token_pos, parser->pos -
601				    token_pos, token_line, token_col);
602				state = STATE_ACCEPT;
603				break;
604			}
605			break;
606		case STATE_EXP:
607			switch (c) {
608			case '-': case '+':
609				state = STATE_EXP_SIGN;
610				break;
611			case '0': case '1': case '2': case '3': case '4':
612			case '5': case '6': case '7': case '8': case '9':
613				state = STATE_EXP_DIGITS;
614				break;
615			default:
616				token_init(&parser->token, parser,
617				    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
618				    - token_pos, token_line, token_col);
619				return true;
620			}
621			break;
622		case STATE_EXP_SIGN:
623			switch (c) {
624			case '0': case '1': case '2': case '3': case '4':
625			case '5': case '6': case '7': case '8': case '9':
626				state = STATE_EXP_DIGITS;
627				break;
628			default:
629				token_init(&parser->token, parser,
630				    TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
631				    - token_pos, token_line, token_col);
632				return true;
633			}
634			break;
635		case STATE_EXP_DIGITS:
636			switch (c) {
637			case '0': case '1': case '2': case '3': case '4':
638			case '5': case '6': case '7': case '8': case '9':
639				break;
640			default:
641				token_init(&parser->token, parser,
642				    TOKEN_TYPE_NUMBER, token_pos, parser->pos -
643				    token_pos, token_line, token_col);
644				state = STATE_ACCEPT;
645				break;
646			}
647			break;
648		default:
649			not_reached();
650		}
651
652		if (state != STATE_ACCEPT) {
653			if (c == '\n') {
654				parser->line++;
655				parser->col = 0;
656			} else {
657				parser->col++;
658			}
659			parser->pos++;
660		}
661	}
662	return false;
663}
664
665static bool	parser_parse_array(parser_t *parser);
666static bool	parser_parse_object(parser_t *parser);
667
668static bool
669parser_parse_value(parser_t *parser) {
670	switch (parser->token.token_type) {
671	case TOKEN_TYPE_NULL:
672	case TOKEN_TYPE_FALSE:
673	case TOKEN_TYPE_TRUE:
674	case TOKEN_TYPE_STRING:
675	case TOKEN_TYPE_NUMBER:
676		return false;
677	case TOKEN_TYPE_LBRACE:
678		return parser_parse_object(parser);
679	case TOKEN_TYPE_LBRACKET:
680		return parser_parse_array(parser);
681	default:
682		return true;
683	}
684	not_reached();
685}
686
687static bool
688parser_parse_pair(parser_t *parser) {
689	assert_d_eq(parser->token.token_type, TOKEN_TYPE_STRING,
690	    "Pair should start with string");
691	if (parser_tokenize(parser)) {
692		return true;
693	}
694	switch (parser->token.token_type) {
695	case TOKEN_TYPE_COLON:
696		if (parser_tokenize(parser)) {
697			return true;
698		}
699		return parser_parse_value(parser);
700	default:
701		return true;
702	}
703}
704
705static bool
706parser_parse_values(parser_t *parser) {
707	if (parser_parse_value(parser)) {
708		return true;
709	}
710
711	while (true) {
712		if (parser_tokenize(parser)) {
713			return true;
714		}
715		switch (parser->token.token_type) {
716		case TOKEN_TYPE_COMMA:
717			if (parser_tokenize(parser)) {
718				return true;
719			}
720			if (parser_parse_value(parser)) {
721				return true;
722			}
723			break;
724		case TOKEN_TYPE_RBRACKET:
725			return false;
726		default:
727			return true;
728		}
729	}
730}
731
732static bool
733parser_parse_array(parser_t *parser) {
734	assert_d_eq(parser->token.token_type, TOKEN_TYPE_LBRACKET,
735	    "Array should start with [");
736	if (parser_tokenize(parser)) {
737		return true;
738	}
739	switch (parser->token.token_type) {
740	case TOKEN_TYPE_RBRACKET:
741		return false;
742	default:
743		return parser_parse_values(parser);
744	}
745	not_reached();
746}
747
748static bool
749parser_parse_pairs(parser_t *parser) {
750	assert_d_eq(parser->token.token_type, TOKEN_TYPE_STRING,
751	    "Object should start with string");
752	if (parser_parse_pair(parser)) {
753		return true;
754	}
755
756	while (true) {
757		if (parser_tokenize(parser)) {
758			return true;
759		}
760		switch (parser->token.token_type) {
761		case TOKEN_TYPE_COMMA:
762			if (parser_tokenize(parser)) {
763				return true;
764			}
765			switch (parser->token.token_type) {
766			case TOKEN_TYPE_STRING:
767				if (parser_parse_pair(parser)) {
768					return true;
769				}
770				break;
771			default:
772				return true;
773			}
774			break;
775		case TOKEN_TYPE_RBRACE:
776			return false;
777		default:
778			return true;
779		}
780	}
781}
782
783static bool
784parser_parse_object(parser_t *parser) {
785	assert_d_eq(parser->token.token_type, TOKEN_TYPE_LBRACE,
786	    "Object should start with {");
787	if (parser_tokenize(parser)) {
788		return true;
789	}
790	switch (parser->token.token_type) {
791	case TOKEN_TYPE_STRING:
792		return parser_parse_pairs(parser);
793	case TOKEN_TYPE_RBRACE:
794		return false;
795	default:
796		return true;
797	}
798	not_reached();
799}
800
801static bool
802parser_parse(parser_t *parser) {
803	if (parser_tokenize(parser)) {
804		goto label_error;
805	}
806	if (parser_parse_value(parser)) {
807		goto label_error;
808	}
809
810	if (parser_tokenize(parser)) {
811		goto label_error;
812	}
813	switch (parser->token.token_type) {
814	case TOKEN_TYPE_EOI:
815		return false;
816	default:
817		goto label_error;
818	}
819	not_reached();
820
821label_error:
822	token_error(&parser->token);
823	return true;
824}
825
826TEST_BEGIN(test_json_parser) {
827	size_t i;
828	const char *invalid_inputs[] = {
829		/* Tokenizer error case tests. */
830		"{ \"string\": X }",
831		"{ \"string\": nXll }",
832		"{ \"string\": nuXl }",
833		"{ \"string\": nulX }",
834		"{ \"string\": nullX }",
835		"{ \"string\": fXlse }",
836		"{ \"string\": faXse }",
837		"{ \"string\": falXe }",
838		"{ \"string\": falsX }",
839		"{ \"string\": falseX }",
840		"{ \"string\": tXue }",
841		"{ \"string\": trXe }",
842		"{ \"string\": truX }",
843		"{ \"string\": trueX }",
844		"{ \"string\": \"\n\" }",
845		"{ \"string\": \"\\z\" }",
846		"{ \"string\": \"\\uX000\" }",
847		"{ \"string\": \"\\u0X00\" }",
848		"{ \"string\": \"\\u00X0\" }",
849		"{ \"string\": \"\\u000X\" }",
850		"{ \"string\": -X }",
851		"{ \"string\": 0.X }",
852		"{ \"string\": 0.0eX }",
853		"{ \"string\": 0.0e+X }",
854
855		/* Parser error test cases. */
856		"{\"string\": }",
857		"{\"string\" }",
858		"{\"string\": [ 0 }",
859		"{\"string\": {\"a\":0, 1 } }",
860		"{\"string\": {\"a\":0: } }",
861		"{",
862		"{}{",
863	};
864	const char *valid_inputs[] = {
865		/* Token tests. */
866		"null",
867		"false",
868		"true",
869		"{}",
870		"{\"a\": 0}",
871		"[]",
872		"[0, 1]",
873		"0",
874		"1",
875		"10",
876		"-10",
877		"10.23",
878		"10.23e4",
879		"10.23e-4",
880		"10.23e+4",
881		"10.23E4",
882		"10.23E-4",
883		"10.23E+4",
884		"-10.23",
885		"-10.23e4",
886		"-10.23e-4",
887		"-10.23e+4",
888		"-10.23E4",
889		"-10.23E-4",
890		"-10.23E+4",
891		"\"value\"",
892		"\" \\\" \\/ \\b \\n \\r \\t \\u0abc \\u1DEF \"",
893
894		/* Parser test with various nesting. */
895		"{\"a\":null, \"b\":[1,[{\"c\":2},3]], \"d\":{\"e\":true}}",
896	};
897
898	for (i = 0; i < sizeof(invalid_inputs)/sizeof(const char *); i++) {
899		const char *input = invalid_inputs[i];
900		parser_t parser;
901		parser_init(&parser, false);
902		assert_false(parser_append(&parser, input),
903		    "Unexpected input appending failure");
904		assert_true(parser_parse(&parser),
905		    "Unexpected parse success for input: %s", input);
906		parser_fini(&parser);
907	}
908
909	for (i = 0; i < sizeof(valid_inputs)/sizeof(const char *); i++) {
910		const char *input = valid_inputs[i];
911		parser_t parser;
912		parser_init(&parser, true);
913		assert_false(parser_append(&parser, input),
914		    "Unexpected input appending failure");
915		assert_false(parser_parse(&parser),
916		    "Unexpected parse error for input: %s", input);
917		parser_fini(&parser);
918	}
919}
920TEST_END
921
922void
923write_cb(void *opaque, const char *str) {
924	parser_t *parser = (parser_t *)opaque;
925	if (parser_append(parser, str)) {
926		test_fail("Unexpected input appending failure");
927	}
928}
929
930TEST_BEGIN(test_stats_print_json) {
931	const char *opts[] = {
932		"J",
933		"Jg",
934		"Jm",
935		"Jd",
936		"Jmd",
937		"Jgd",
938		"Jgm",
939		"Jgmd",
940		"Ja",
941		"Jb",
942		"Jl",
943		"Jx",
944		"Jbl",
945		"Jal",
946		"Jab",
947		"Jabl",
948		"Jax",
949		"Jbx",
950		"Jlx",
951		"Jablx",
952		"Jgmdablx",
953	};
954	unsigned arena_ind, i;
955
956	for (i = 0; i < 3; i++) {
957		unsigned j;
958
959		switch (i) {
960		case 0:
961			break;
962		case 1: {
963			size_t sz = sizeof(arena_ind);
964			assert_d_eq(mallctl("arenas.create", (void *)&arena_ind,
965			    &sz, NULL, 0), 0, "Unexpected mallctl failure");
966			break;
967		} case 2: {
968			size_t mib[3];
969			size_t miblen = sizeof(mib)/sizeof(size_t);
970			assert_d_eq(mallctlnametomib("arena.0.destroy",
971			    mib, &miblen), 0,
972			    "Unexpected mallctlnametomib failure");
973			mib[1] = arena_ind;
974			assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL,
975			    0), 0, "Unexpected mallctlbymib failure");
976			break;
977		} default:
978			not_reached();
979		}
980
981		for (j = 0; j < sizeof(opts)/sizeof(const char *); j++) {
982			parser_t parser;
983
984			parser_init(&parser, true);
985			malloc_stats_print(write_cb, (void *)&parser, opts[j]);
986			assert_false(parser_parse(&parser),
987			    "Unexpected parse error, opts=\"%s\"", opts[j]);
988			parser_fini(&parser);
989		}
990	}
991}
992TEST_END
993
994int
995main(void) {
996	return test(
997	    test_json_parser,
998	    test_stats_print_json);
999}
1000