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