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