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