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