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