xref: /freebsd/lib/libpmc/pmu-events/jsmn.c (revision 1d386b48a555f61cb7325543adbbb5c3f3407a66)
1 /*
2  * Copyright (c) 2010 Serge A. Zaitsev
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20  * THE SOFTWARE.
21  *
22  * Slightly modified by AK to not assume 0 terminated input.
23  *
24  */
25 
26 #include <stdlib.h>
27 #include "jsmn.h"
28 #define JSMN_STRICT
29 
30 /*
31  * Allocates a fresh unused token from the token pool.
32  */
33 static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
34 				   jsmntok_t *tokens, size_t num_tokens)
35 {
36 	jsmntok_t *tok;
37 
38 	if ((unsigned)parser->toknext >= num_tokens)
39 		return NULL;
40 	tok = &tokens[parser->toknext++];
41 	tok->start = tok->end = -1;
42 	tok->size = 0;
43 	return tok;
44 }
45 
46 /*
47  * Fills token type and boundaries.
48  */
49 static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
50 			    int start, int end)
51 {
52 	token->type = type;
53 	token->start = start;
54 	token->end = end;
55 	token->size = 0;
56 }
57 
58 /*
59  * Fills next available token with JSON primitive.
60  */
61 static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js,
62 				      size_t len,
63 				      jsmntok_t *tokens, size_t num_tokens)
64 {
65 	jsmntok_t *token;
66 	int start;
67 
68 	start = parser->pos;
69 
70 	for (; parser->pos < len; parser->pos++) {
71 		switch (js[parser->pos]) {
72 #ifndef JSMN_STRICT
73 		/*
74 		 * In strict mode primitive must be followed by ","
75 		 * or "}" or "]"
76 		 */
77 		case ':':
78 #endif
79 		case '\t':
80 		case '\r':
81 		case '\n':
82 		case ' ':
83 		case ',':
84 		case ']':
85 		case '}':
86 			goto found;
87 		default:
88 			break;
89 		}
90 		if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
91 			parser->pos = start;
92 			return JSMN_ERROR_INVAL;
93 		}
94 	}
95 #ifdef JSMN_STRICT
96 	/*
97 	 * In strict mode primitive must be followed by a
98 	 * comma/object/array.
99 	 */
100 	parser->pos = start;
101 	return JSMN_ERROR_PART;
102 #endif
103 
104 found:
105 	token = jsmn_alloc_token(parser, tokens, num_tokens);
106 	if (token == NULL) {
107 		parser->pos = start;
108 		return JSMN_ERROR_NOMEM;
109 	}
110 	jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
111 	parser->pos--; /* parent sees closing brackets */
112 	return JSMN_SUCCESS;
113 }
114 
115 /*
116  * Fills next token with JSON string.
117  */
118 static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js,
119 				   size_t len,
120 				   jsmntok_t *tokens, size_t num_tokens)
121 {
122 	jsmntok_t *token;
123 	int start = parser->pos;
124 
125 	/* Skip starting quote */
126 	parser->pos++;
127 
128 	for (; parser->pos < len; parser->pos++) {
129 		char c = js[parser->pos];
130 
131 		/* Quote: end of string */
132 		if (c == '\"') {
133 			token = jsmn_alloc_token(parser, tokens, num_tokens);
134 			if (token == NULL) {
135 				parser->pos = start;
136 				return JSMN_ERROR_NOMEM;
137 			}
138 			jsmn_fill_token(token, JSMN_STRING, start+1,
139 					parser->pos);
140 			return JSMN_SUCCESS;
141 		}
142 
143 		/* Backslash: Quoted symbol expected */
144 		if (c == '\\') {
145 			parser->pos++;
146 			switch (js[parser->pos]) {
147 				/* Allowed escaped symbols */
148 			case '\"':
149 			case '/':
150 			case '\\':
151 			case 'b':
152 			case 'f':
153 			case 'r':
154 			case 'n':
155 			case 't':
156 				break;
157 				/* Allows escaped symbol \uXXXX */
158 			case 'u':
159 				/* TODO */
160 				break;
161 				/* Unexpected symbol */
162 			default:
163 				parser->pos = start;
164 				return JSMN_ERROR_INVAL;
165 			}
166 		}
167 	}
168 	parser->pos = start;
169 	return JSMN_ERROR_PART;
170 }
171 
172 /*
173  * Parse JSON string and fill tokens.
174  */
175 jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
176 		     jsmntok_t *tokens, unsigned int num_tokens)
177 {
178 	jsmnerr_t r;
179 	int i;
180 	jsmntok_t *token;
181 #ifdef JSMN_STRICT
182 	/*
183 	 * Keeps track of whether a new object/list/primitive is expected. New items are only
184 	 * allowed after an opening brace, comma or colon. A closing brace after a comma is not
185 	 * valid JSON.
186 	 */
187 	int expecting_item = 1;
188 #endif
189 
190 	for (; parser->pos < len; parser->pos++) {
191 		char c;
192 		jsmntype_t type;
193 
194 		c = js[parser->pos];
195 		switch (c) {
196 		case '{':
197 		case '[':
198 #ifdef JSMN_STRICT
199 			if (!expecting_item)
200 				return JSMN_ERROR_INVAL;
201 #endif
202 			token = jsmn_alloc_token(parser, tokens, num_tokens);
203 			if (token == NULL)
204 				return JSMN_ERROR_NOMEM;
205 			if (parser->toksuper != -1)
206 				tokens[parser->toksuper].size++;
207 			token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
208 			token->start = parser->pos;
209 			parser->toksuper = parser->toknext - 1;
210 			break;
211 		case '}':
212 		case ']':
213 #ifdef JSMN_STRICT
214 			if (expecting_item)
215 				return JSMN_ERROR_INVAL;
216 #endif
217 			type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
218 			for (i = parser->toknext - 1; i >= 0; i--) {
219 				token = &tokens[i];
220 				if (token->start != -1 && token->end == -1) {
221 					if (token->type != type)
222 						return JSMN_ERROR_INVAL;
223 					parser->toksuper = -1;
224 					token->end = parser->pos + 1;
225 					break;
226 				}
227 			}
228 			/* Error if unmatched closing bracket */
229 			if (i == -1)
230 				return JSMN_ERROR_INVAL;
231 			for (; i >= 0; i--) {
232 				token = &tokens[i];
233 				if (token->start != -1 && token->end == -1) {
234 					parser->toksuper = i;
235 					break;
236 				}
237 			}
238 			break;
239 		case '\"':
240 #ifdef JSMN_STRICT
241 			if (!expecting_item)
242 				return JSMN_ERROR_INVAL;
243 			expecting_item = 0;
244 #endif
245 			r = jsmn_parse_string(parser, js, len, tokens,
246 					      num_tokens);
247 			if (r < 0)
248 				return r;
249 			if (parser->toksuper != -1)
250 				tokens[parser->toksuper].size++;
251 			break;
252 		case '\t':
253 		case '\r':
254 		case '\n':
255 		case ' ':
256 			break;
257 #ifdef JSMN_STRICT
258 		case ':':
259 		case ',':
260 			if (expecting_item)
261 				return JSMN_ERROR_INVAL;
262 			expecting_item = 1;
263 			break;
264 			/*
265 			 * In strict mode primitives are:
266 			 * numbers and booleans.
267 			 */
268 		case '-':
269 		case '0':
270 		case '1':
271 		case '2':
272 		case '3':
273 		case '4':
274 		case '5':
275 		case '6':
276 		case '7':
277 		case '8':
278 		case '9':
279 		case 't':
280 		case 'f':
281 		case 'n':
282 #else
283 		case ':':
284 		case ',':
285 			break;
286 			/*
287 			 * In non-strict mode every unquoted value
288 			 * is a primitive.
289 			 */
290 			/*FALL THROUGH */
291 		default:
292 #endif
293 
294 #ifdef JSMN_STRICT
295 			if (!expecting_item)
296 				return JSMN_ERROR_INVAL;
297 			expecting_item = 0;
298 #endif
299 			r = jsmn_parse_primitive(parser, js, len, tokens,
300 						 num_tokens);
301 			if (r < 0)
302 				return r;
303 			if (parser->toksuper != -1)
304 				tokens[parser->toksuper].size++;
305 			break;
306 
307 #ifdef JSMN_STRICT
308 			/* Unexpected char in strict mode */
309 		default:
310 			return JSMN_ERROR_INVAL;
311 #endif
312 		}
313 	}
314 
315 	for (i = parser->toknext - 1; i >= 0; i--) {
316 		/* Unmatched opened object or array */
317 		if (tokens[i].start != -1 && tokens[i].end == -1)
318 			return JSMN_ERROR_PART;
319 	}
320 
321 #ifdef JSMN_STRICT
322 	return expecting_item ? JSMN_ERROR_INVAL : JSMN_SUCCESS;
323 #else
324 	return JSMN_SUCCESS;
325 #endif
326 }
327 
328 /*
329  * Creates a new parser based over a given  buffer with an array of tokens
330  * available.
331  */
332 void jsmn_init(jsmn_parser *parser)
333 {
334 	parser->pos = 0;
335 	parser->toknext = 0;
336 	parser->toksuper = -1;
337 }
338 
339 const char *jsmn_strerror(jsmnerr_t err)
340 {
341 	switch (err) {
342 	case JSMN_ERROR_NOMEM:
343 		return "No enough tokens";
344 	case JSMN_ERROR_INVAL:
345 		return "Invalid character inside JSON string";
346 	case JSMN_ERROR_PART:
347 		return "The string is not a full JSON packet, more bytes expected";
348 	case JSMN_SUCCESS:
349 		return "Success";
350 	default:
351 		return "Unknown json error";
352 	}
353 }
354