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