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