xref: /freebsd-src/lib/libpmc/pmu-events/jsmn.c (revision b3e7694832e81d7a904a10f525f8797b753bf0d3)
1959826caSMatt Macy /*
2959826caSMatt Macy  * Copyright (c) 2010 Serge A. Zaitsev
3959826caSMatt Macy  *
4959826caSMatt Macy  * Permission is hereby granted, free of charge, to any person obtaining a copy
5959826caSMatt Macy  * of this software and associated documentation files (the "Software"), to deal
6959826caSMatt Macy  * in the Software without restriction, including without limitation the rights
7959826caSMatt Macy  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8959826caSMatt Macy  * copies of the Software, and to permit persons to whom the Software is
9959826caSMatt Macy  * furnished to do so, subject to the following conditions:
10959826caSMatt Macy  *
11959826caSMatt Macy  * The above copyright notice and this permission notice shall be included in
12959826caSMatt Macy  * all copies or substantial portions of the Software.
13959826caSMatt Macy  *
14959826caSMatt Macy  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15959826caSMatt Macy  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16959826caSMatt Macy  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17959826caSMatt Macy  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18959826caSMatt Macy  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19959826caSMatt Macy  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20959826caSMatt Macy  * THE SOFTWARE.
21959826caSMatt Macy  *
22959826caSMatt Macy  * Slightly modified by AK to not assume 0 terminated input.
23959826caSMatt Macy  *
24959826caSMatt Macy  */
25959826caSMatt Macy 
26959826caSMatt Macy #include <stdlib.h>
27959826caSMatt Macy #include "jsmn.h"
28*62ff619dSAlexander Motin #define JSMN_STRICT
29959826caSMatt Macy 
30959826caSMatt Macy /*
31959826caSMatt Macy  * Allocates a fresh unused token from the token pool.
32959826caSMatt Macy  */
jsmn_alloc_token(jsmn_parser * parser,jsmntok_t * tokens,size_t num_tokens)33959826caSMatt Macy static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
34959826caSMatt Macy 				   jsmntok_t *tokens, size_t num_tokens)
35959826caSMatt Macy {
36959826caSMatt Macy 	jsmntok_t *tok;
37959826caSMatt Macy 
38959826caSMatt Macy 	if ((unsigned)parser->toknext >= num_tokens)
39959826caSMatt Macy 		return NULL;
40959826caSMatt Macy 	tok = &tokens[parser->toknext++];
41959826caSMatt Macy 	tok->start = tok->end = -1;
42959826caSMatt Macy 	tok->size = 0;
43959826caSMatt Macy 	return tok;
44959826caSMatt Macy }
45959826caSMatt Macy 
46959826caSMatt Macy /*
47959826caSMatt Macy  * Fills token type and boundaries.
48959826caSMatt Macy  */
jsmn_fill_token(jsmntok_t * token,jsmntype_t type,int start,int end)49959826caSMatt Macy static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
50959826caSMatt Macy 			    int start, int end)
51959826caSMatt Macy {
52959826caSMatt Macy 	token->type = type;
53959826caSMatt Macy 	token->start = start;
54959826caSMatt Macy 	token->end = end;
55959826caSMatt Macy 	token->size = 0;
56959826caSMatt Macy }
57959826caSMatt Macy 
58959826caSMatt Macy /*
59959826caSMatt Macy  * Fills next available token with JSON primitive.
60959826caSMatt Macy  */
jsmn_parse_primitive(jsmn_parser * parser,const char * js,size_t len,jsmntok_t * tokens,size_t num_tokens)61959826caSMatt Macy static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js,
62959826caSMatt Macy 				      size_t len,
63959826caSMatt Macy 				      jsmntok_t *tokens, size_t num_tokens)
64959826caSMatt Macy {
65959826caSMatt Macy 	jsmntok_t *token;
66959826caSMatt Macy 	int start;
67959826caSMatt Macy 
68959826caSMatt Macy 	start = parser->pos;
69959826caSMatt Macy 
70959826caSMatt Macy 	for (; parser->pos < len; parser->pos++) {
71959826caSMatt Macy 		switch (js[parser->pos]) {
72959826caSMatt Macy #ifndef JSMN_STRICT
73959826caSMatt Macy 		/*
74959826caSMatt Macy 		 * In strict mode primitive must be followed by ","
75959826caSMatt Macy 		 * or "}" or "]"
76959826caSMatt Macy 		 */
77959826caSMatt Macy 		case ':':
78959826caSMatt Macy #endif
79959826caSMatt Macy 		case '\t':
80959826caSMatt Macy 		case '\r':
81959826caSMatt Macy 		case '\n':
82959826caSMatt Macy 		case ' ':
83959826caSMatt Macy 		case ',':
84959826caSMatt Macy 		case ']':
85959826caSMatt Macy 		case '}':
86959826caSMatt Macy 			goto found;
87959826caSMatt Macy 		default:
88959826caSMatt Macy 			break;
89959826caSMatt Macy 		}
90959826caSMatt Macy 		if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
91959826caSMatt Macy 			parser->pos = start;
92959826caSMatt Macy 			return JSMN_ERROR_INVAL;
93959826caSMatt Macy 		}
94959826caSMatt Macy 	}
95959826caSMatt Macy #ifdef JSMN_STRICT
96959826caSMatt Macy 	/*
97959826caSMatt Macy 	 * In strict mode primitive must be followed by a
98959826caSMatt Macy 	 * comma/object/array.
99959826caSMatt Macy 	 */
100959826caSMatt Macy 	parser->pos = start;
101959826caSMatt Macy 	return JSMN_ERROR_PART;
102959826caSMatt Macy #endif
103959826caSMatt Macy 
104959826caSMatt Macy found:
105959826caSMatt Macy 	token = jsmn_alloc_token(parser, tokens, num_tokens);
106959826caSMatt Macy 	if (token == NULL) {
107959826caSMatt Macy 		parser->pos = start;
108959826caSMatt Macy 		return JSMN_ERROR_NOMEM;
109959826caSMatt Macy 	}
110959826caSMatt Macy 	jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
111959826caSMatt Macy 	parser->pos--; /* parent sees closing brackets */
112959826caSMatt Macy 	return JSMN_SUCCESS;
113959826caSMatt Macy }
114959826caSMatt Macy 
115959826caSMatt Macy /*
116959826caSMatt Macy  * Fills next token with JSON string.
117959826caSMatt Macy  */
jsmn_parse_string(jsmn_parser * parser,const char * js,size_t len,jsmntok_t * tokens,size_t num_tokens)118959826caSMatt Macy static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js,
119959826caSMatt Macy 				   size_t len,
120959826caSMatt Macy 				   jsmntok_t *tokens, size_t num_tokens)
121959826caSMatt Macy {
122959826caSMatt Macy 	jsmntok_t *token;
123959826caSMatt Macy 	int start = parser->pos;
124959826caSMatt Macy 
125959826caSMatt Macy 	/* Skip starting quote */
126959826caSMatt Macy 	parser->pos++;
127959826caSMatt Macy 
128959826caSMatt Macy 	for (; parser->pos < len; parser->pos++) {
129959826caSMatt Macy 		char c = js[parser->pos];
130959826caSMatt Macy 
131959826caSMatt Macy 		/* Quote: end of string */
132959826caSMatt Macy 		if (c == '\"') {
133959826caSMatt Macy 			token = jsmn_alloc_token(parser, tokens, num_tokens);
134959826caSMatt Macy 			if (token == NULL) {
135959826caSMatt Macy 				parser->pos = start;
136959826caSMatt Macy 				return JSMN_ERROR_NOMEM;
137959826caSMatt Macy 			}
138959826caSMatt Macy 			jsmn_fill_token(token, JSMN_STRING, start+1,
139959826caSMatt Macy 					parser->pos);
140959826caSMatt Macy 			return JSMN_SUCCESS;
141959826caSMatt Macy 		}
142959826caSMatt Macy 
143959826caSMatt Macy 		/* Backslash: Quoted symbol expected */
144959826caSMatt Macy 		if (c == '\\') {
145959826caSMatt Macy 			parser->pos++;
146959826caSMatt Macy 			switch (js[parser->pos]) {
147959826caSMatt Macy 				/* Allowed escaped symbols */
148959826caSMatt Macy 			case '\"':
149959826caSMatt Macy 			case '/':
150959826caSMatt Macy 			case '\\':
151959826caSMatt Macy 			case 'b':
152959826caSMatt Macy 			case 'f':
153959826caSMatt Macy 			case 'r':
154959826caSMatt Macy 			case 'n':
155959826caSMatt Macy 			case 't':
156959826caSMatt Macy 				break;
157959826caSMatt Macy 				/* Allows escaped symbol \uXXXX */
158959826caSMatt Macy 			case 'u':
159959826caSMatt Macy 				/* TODO */
160959826caSMatt Macy 				break;
161959826caSMatt Macy 				/* Unexpected symbol */
162959826caSMatt Macy 			default:
163959826caSMatt Macy 				parser->pos = start;
164959826caSMatt Macy 				return JSMN_ERROR_INVAL;
165959826caSMatt Macy 			}
166959826caSMatt Macy 		}
167959826caSMatt Macy 	}
168959826caSMatt Macy 	parser->pos = start;
169959826caSMatt Macy 	return JSMN_ERROR_PART;
170959826caSMatt Macy }
171959826caSMatt Macy 
172959826caSMatt Macy /*
173959826caSMatt Macy  * Parse JSON string and fill tokens.
174959826caSMatt Macy  */
jsmn_parse(jsmn_parser * parser,const char * js,size_t len,jsmntok_t * tokens,unsigned int num_tokens)175959826caSMatt Macy jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
176959826caSMatt Macy 		     jsmntok_t *tokens, unsigned int num_tokens)
177959826caSMatt Macy {
178959826caSMatt Macy 	jsmnerr_t r;
179959826caSMatt Macy 	int i;
180959826caSMatt Macy 	jsmntok_t *token;
181*62ff619dSAlexander Motin #ifdef JSMN_STRICT
182*62ff619dSAlexander Motin 	/*
183*62ff619dSAlexander Motin 	 * Keeps track of whether a new object/list/primitive is expected. New items are only
184*62ff619dSAlexander Motin 	 * allowed after an opening brace, comma or colon. A closing brace after a comma is not
185*62ff619dSAlexander Motin 	 * valid JSON.
186*62ff619dSAlexander Motin 	 */
187*62ff619dSAlexander Motin 	int expecting_item = 1;
188*62ff619dSAlexander Motin #endif
189959826caSMatt Macy 
190959826caSMatt Macy 	for (; parser->pos < len; parser->pos++) {
191959826caSMatt Macy 		char c;
192959826caSMatt Macy 		jsmntype_t type;
193959826caSMatt Macy 
194959826caSMatt Macy 		c = js[parser->pos];
195959826caSMatt Macy 		switch (c) {
196959826caSMatt Macy 		case '{':
197959826caSMatt Macy 		case '[':
198*62ff619dSAlexander Motin #ifdef JSMN_STRICT
199*62ff619dSAlexander Motin 			if (!expecting_item)
200*62ff619dSAlexander Motin 				return JSMN_ERROR_INVAL;
201*62ff619dSAlexander Motin #endif
202959826caSMatt Macy 			token = jsmn_alloc_token(parser, tokens, num_tokens);
203959826caSMatt Macy 			if (token == NULL)
204959826caSMatt Macy 				return JSMN_ERROR_NOMEM;
205959826caSMatt Macy 			if (parser->toksuper != -1)
206959826caSMatt Macy 				tokens[parser->toksuper].size++;
207959826caSMatt Macy 			token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
208959826caSMatt Macy 			token->start = parser->pos;
209959826caSMatt Macy 			parser->toksuper = parser->toknext - 1;
210959826caSMatt Macy 			break;
211959826caSMatt Macy 		case '}':
212959826caSMatt Macy 		case ']':
213*62ff619dSAlexander Motin #ifdef JSMN_STRICT
214*62ff619dSAlexander Motin 			if (expecting_item)
215*62ff619dSAlexander Motin 				return JSMN_ERROR_INVAL;
216*62ff619dSAlexander Motin #endif
217959826caSMatt Macy 			type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
218959826caSMatt Macy 			for (i = parser->toknext - 1; i >= 0; i--) {
219959826caSMatt Macy 				token = &tokens[i];
220959826caSMatt Macy 				if (token->start != -1 && token->end == -1) {
221959826caSMatt Macy 					if (token->type != type)
222959826caSMatt Macy 						return JSMN_ERROR_INVAL;
223959826caSMatt Macy 					parser->toksuper = -1;
224959826caSMatt Macy 					token->end = parser->pos + 1;
225959826caSMatt Macy 					break;
226959826caSMatt Macy 				}
227959826caSMatt Macy 			}
228959826caSMatt Macy 			/* Error if unmatched closing bracket */
229959826caSMatt Macy 			if (i == -1)
230959826caSMatt Macy 				return JSMN_ERROR_INVAL;
231959826caSMatt Macy 			for (; i >= 0; i--) {
232959826caSMatt Macy 				token = &tokens[i];
233959826caSMatt Macy 				if (token->start != -1 && token->end == -1) {
234959826caSMatt Macy 					parser->toksuper = i;
235959826caSMatt Macy 					break;
236959826caSMatt Macy 				}
237959826caSMatt Macy 			}
238959826caSMatt Macy 			break;
239959826caSMatt Macy 		case '\"':
240*62ff619dSAlexander Motin #ifdef JSMN_STRICT
241*62ff619dSAlexander Motin 			if (!expecting_item)
242*62ff619dSAlexander Motin 				return JSMN_ERROR_INVAL;
243*62ff619dSAlexander Motin 			expecting_item = 0;
244*62ff619dSAlexander Motin #endif
245959826caSMatt Macy 			r = jsmn_parse_string(parser, js, len, tokens,
246959826caSMatt Macy 					      num_tokens);
247959826caSMatt Macy 			if (r < 0)
248959826caSMatt Macy 				return r;
249959826caSMatt Macy 			if (parser->toksuper != -1)
250959826caSMatt Macy 				tokens[parser->toksuper].size++;
251959826caSMatt Macy 			break;
252959826caSMatt Macy 		case '\t':
253959826caSMatt Macy 		case '\r':
254959826caSMatt Macy 		case '\n':
255959826caSMatt Macy 		case ' ':
256959826caSMatt Macy 			break;
257959826caSMatt Macy #ifdef JSMN_STRICT
258*62ff619dSAlexander Motin 		case ':':
259*62ff619dSAlexander Motin 		case ',':
260*62ff619dSAlexander Motin 			if (expecting_item)
261*62ff619dSAlexander Motin 				return JSMN_ERROR_INVAL;
262*62ff619dSAlexander Motin 			expecting_item = 1;
263*62ff619dSAlexander Motin 			break;
264959826caSMatt Macy 			/*
265959826caSMatt Macy 			 * In strict mode primitives are:
266959826caSMatt Macy 			 * numbers and booleans.
267959826caSMatt Macy 			 */
268959826caSMatt Macy 		case '-':
269959826caSMatt Macy 		case '0':
270959826caSMatt Macy 		case '1':
271959826caSMatt Macy 		case '2':
272959826caSMatt Macy 		case '3':
273959826caSMatt Macy 		case '4':
274959826caSMatt Macy 		case '5':
275959826caSMatt Macy 		case '6':
276959826caSMatt Macy 		case '7':
277959826caSMatt Macy 		case '8':
278959826caSMatt Macy 		case '9':
279959826caSMatt Macy 		case 't':
280959826caSMatt Macy 		case 'f':
281959826caSMatt Macy 		case 'n':
282959826caSMatt Macy #else
283*62ff619dSAlexander Motin 		case ':':
284*62ff619dSAlexander Motin 		case ',':
285*62ff619dSAlexander Motin 			break;
286959826caSMatt Macy 			/*
287959826caSMatt Macy 			 * In non-strict mode every unquoted value
288959826caSMatt Macy 			 * is a primitive.
289959826caSMatt Macy 			 */
290959826caSMatt Macy 			/*FALL THROUGH */
291959826caSMatt Macy 		default:
292959826caSMatt Macy #endif
293*62ff619dSAlexander Motin 
294*62ff619dSAlexander Motin #ifdef JSMN_STRICT
295*62ff619dSAlexander Motin 			if (!expecting_item)
296*62ff619dSAlexander Motin 				return JSMN_ERROR_INVAL;
297*62ff619dSAlexander Motin 			expecting_item = 0;
298*62ff619dSAlexander Motin #endif
299959826caSMatt Macy 			r = jsmn_parse_primitive(parser, js, len, tokens,
300959826caSMatt Macy 						 num_tokens);
301959826caSMatt Macy 			if (r < 0)
302959826caSMatt Macy 				return r;
303959826caSMatt Macy 			if (parser->toksuper != -1)
304959826caSMatt Macy 				tokens[parser->toksuper].size++;
305959826caSMatt Macy 			break;
306959826caSMatt Macy 
307959826caSMatt Macy #ifdef JSMN_STRICT
308959826caSMatt Macy 			/* Unexpected char in strict mode */
309959826caSMatt Macy 		default:
310959826caSMatt Macy 			return JSMN_ERROR_INVAL;
311959826caSMatt Macy #endif
312959826caSMatt Macy 		}
313959826caSMatt Macy 	}
314959826caSMatt Macy 
315959826caSMatt Macy 	for (i = parser->toknext - 1; i >= 0; i--) {
316959826caSMatt Macy 		/* Unmatched opened object or array */
317959826caSMatt Macy 		if (tokens[i].start != -1 && tokens[i].end == -1)
318959826caSMatt Macy 			return JSMN_ERROR_PART;
319959826caSMatt Macy 	}
320959826caSMatt Macy 
321*62ff619dSAlexander Motin #ifdef JSMN_STRICT
322*62ff619dSAlexander Motin 	return expecting_item ? JSMN_ERROR_INVAL : JSMN_SUCCESS;
323*62ff619dSAlexander Motin #else
324959826caSMatt Macy 	return JSMN_SUCCESS;
325*62ff619dSAlexander Motin #endif
326959826caSMatt Macy }
327959826caSMatt Macy 
328959826caSMatt Macy /*
329959826caSMatt Macy  * Creates a new parser based over a given  buffer with an array of tokens
330959826caSMatt Macy  * available.
331959826caSMatt Macy  */
jsmn_init(jsmn_parser * parser)332959826caSMatt Macy void jsmn_init(jsmn_parser *parser)
333959826caSMatt Macy {
334959826caSMatt Macy 	parser->pos = 0;
335959826caSMatt Macy 	parser->toknext = 0;
336959826caSMatt Macy 	parser->toksuper = -1;
337959826caSMatt Macy }
338959826caSMatt Macy 
jsmn_strerror(jsmnerr_t err)339959826caSMatt Macy const char *jsmn_strerror(jsmnerr_t err)
340959826caSMatt Macy {
341959826caSMatt Macy 	switch (err) {
342959826caSMatt Macy 	case JSMN_ERROR_NOMEM:
343959826caSMatt Macy 		return "No enough tokens";
344959826caSMatt Macy 	case JSMN_ERROR_INVAL:
345959826caSMatt Macy 		return "Invalid character inside JSON string";
346959826caSMatt Macy 	case JSMN_ERROR_PART:
347959826caSMatt Macy 		return "The string is not a full JSON packet, more bytes expected";
348959826caSMatt Macy 	case JSMN_SUCCESS:
349959826caSMatt Macy 		return "Success";
350959826caSMatt Macy 	default:
351959826caSMatt Macy 		return "Unknown json error";
352959826caSMatt Macy 	}
353959826caSMatt Macy }
354