https://github.com/zserge/jsmn

Committer:
dvddnr
Date:
Wed Jan 24 11:03:28 2018 +0000
Revision:
0:538ac55fc6fb
first release

Who changed what in which revision?

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