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