1#include <stdlib.h> 2 3#include "jsmn.h" 4 5/** 6 * Allocates a fresh unused token from the token pull. 7 */ 8static 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 */ 26static 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 */ 37static jsmnerr_t 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 65found: 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 */ 86static jsmnerr_t 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 == '\\') { 117 parser->pos++; 118 switch (js[parser->pos]) { 119 /* Allowed escaped symbols */ 120 case '\"': case '/' : case '\\' : case 'b' : 121 case 'f' : case 'r' : case 'n' : case 't' : 122 break; 123 /* Allows escaped symbol \uXXXX */ 124 case 'u': 125 parser->pos++; 126 int i = 0; 127 for(; i < 4 && 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 */ 153jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len, 154 jsmntok_t *tokens, unsigned int num_tokens) { 155 jsmnerr_t r; 156 int i; 157 jsmntok_t *token; 158 int count = 0; 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 ':' : case ',': case ' ': 238 break; 239#ifdef JSMN_STRICT 240 /* In strict mode primitives are: numbers and booleans */ 241 case '-': case '0': case '1' : case '2': case '3' : case '4': 242 case '5': case '6': case '7' : case '8': case '9': 243 case 't': case 'f': case 'n' : 244#else 245 /* In non-strict mode every unquoted value is a primitive */ 246 default: 247#endif 248 r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); 249 if (r < 0) return r; 250 count++; 251 if (parser->toksuper != -1 && tokens != NULL) 252 tokens[parser->toksuper].size++; 253 break; 254 255#ifdef JSMN_STRICT 256 /* Unexpected char in strict mode */ 257 default: 258 return JSMN_ERROR_INVAL; 259#endif 260 } 261 } 262 263 for (i = parser->toknext - 1; i >= 0; i--) { 264 /* Unmatched opened object or array */ 265 if (tokens[i].start != -1 && tokens[i].end == -1) { 266 return JSMN_ERROR_PART; 267 } 268 } 269 270 return count; 271} 272 273/** 274 * Creates a new parser based over a given buffer with an array of tokens 275 * available. 276 */ 277void jsmn_init(jsmn_parser *parser) { 278 parser->pos = 0; 279 parser->toknext = 0; 280 parser->toksuper = -1; 281} 282