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