jsmn.c revision 275970
10SN/A#include <stdlib.h> 217408Salanb#include <string.h> 30SN/A 40SN/A#include "jsmn.h" 50SN/A 60SN/A/** 72362SN/A * Allocates a fresh unused token from the token pull. 80SN/A */ 92362SN/Astatic jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, 100SN/A jsmntok_t *tokens, size_t num_tokens) { 110SN/A jsmntok_t *tok; 120SN/A if (parser->toknext >= num_tokens) { 130SN/A return NULL; 140SN/A } 150SN/A tok = &tokens[parser->toknext++]; 160SN/A tok->start = tok->end = -1; 170SN/A tok->size = 0; 180SN/A#ifdef JSMN_PARENT_LINKS 190SN/A tok->parent = -1; 200SN/A#endif 212362SN/A return tok; 222362SN/A} 232362SN/A 240SN/A/** 250SN/A * Fills token type and boundaries. 260SN/A */ 270SN/Astatic void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, 280SN/A int start, int end) { 290SN/A token->type = type; 3017408Salanb token->start = start; 3117408Salanb token->end = end; 320SN/A token->size = 0; 330SN/A} 340SN/A 3517408Salanb/** 360SN/A * Fills next available token with JSON primitive. 370SN/A */ 380SN/Astatic jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js, 390SN/A jsmntok_t *tokens, size_t num_tokens) { 400SN/A jsmntok_t *token; 410SN/A int start; 420SN/A 430SN/A start = parser->pos; 440SN/A 450SN/A for (; js[parser->pos] != '\0'; parser->pos++) { 460SN/A switch (js[parser->pos]) { 4712546Savstepan#ifndef JSMN_STRICT 480SN/A /* In strict mode primitive must be followed by "," or "}" or "]" */ 490SN/A case ':': 500SN/A#endif 510SN/A case '\t' : case '\r' : case '\n' : case ' ' : 520SN/A case ',' : case ']' : case '}' : 530SN/A goto found; 540SN/A } 550SN/A if (js[parser->pos] < 32 || js[parser->pos] >= 127) { 560SN/A parser->pos = start; 5712546Savstepan return JSMN_ERROR_INVAL; 580SN/A } 5912546Savstepan } 600SN/A#ifdef JSMN_STRICT 610SN/A /* In strict mode primitive must be followed by a comma/object/array */ 620SN/A parser->pos = start; 630SN/A return JSMN_ERROR_PART; 640SN/A#endif 650SN/A 660SN/Afound: 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 */ 83static 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 parser->pos++; 112 switch (js[parser->pos]) { 113 /* Allowed escaped symbols */ 114 case '\"': case '/' : case '\\' : case 'b' : 115 case 'f' : case 'r' : case 'n' : case 't' : 116 break; 117 /* Allows escaped symbol \uXXXX */ 118 case 'u': 119 parser->pos++; 120 int i = 0; 121 for(; i < 4 && js[parser->pos] != '\0'; i++) { 122 /* If it isn't a hex character we have an error */ 123 if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ 124 (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ 125 (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ 126 parser->pos = start; 127 return JSMN_ERROR_INVAL; 128 } 129 parser->pos++; 130 } 131 parser->pos--; 132 break; 133 /* Unexpected symbol */ 134 default: 135 parser->pos = start; 136 return JSMN_ERROR_INVAL; 137 } 138 } 139 } 140 parser->pos = start; 141 return JSMN_ERROR_PART; 142} 143 144/** 145 * Parse JSON string and fill tokens. 146 */ 147jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, jsmntok_t *tokens, 148 unsigned int num_tokens) { 149 jsmnerr_t r; 150 int i; 151 jsmntok_t *token; 152 153 for (; js[parser->pos] != '\0'; parser->pos++) { 154 char c; 155 jsmntype_t type; 156 157 c = js[parser->pos]; 158 switch (c) { 159 case '{': case '[': 160 token = jsmn_alloc_token(parser, tokens, num_tokens); 161 if (token == NULL) 162 return JSMN_ERROR_NOMEM; 163 if (parser->toksuper != -1) { 164 tokens[parser->toksuper].size++; 165#ifdef JSMN_PARENT_LINKS 166 token->parent = parser->toksuper; 167#endif 168 } 169 token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); 170 token->start = parser->pos; 171 parser->toksuper = parser->toknext - 1; 172 break; 173 case '}': case ']': 174 type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); 175#ifdef JSMN_PARENT_LINKS 176 if (parser->toknext < 1) { 177 return JSMN_ERROR_INVAL; 178 } 179 token = &tokens[parser->toknext - 1]; 180 for (;;) { 181 if (token->start != -1 && token->end == -1) { 182 if (token->type != type) { 183 return JSMN_ERROR_INVAL; 184 } 185 token->end = parser->pos + 1; 186 parser->toksuper = token->parent; 187 break; 188 } 189 if (token->parent == -1) { 190 break; 191 } 192 token = &tokens[token->parent]; 193 } 194#else 195 for (i = parser->toknext - 1; i >= 0; i--) { 196 token = &tokens[i]; 197 if (token->start != -1 && token->end == -1) { 198 if (token->type != type) { 199 return JSMN_ERROR_INVAL; 200 } 201 parser->toksuper = -1; 202 token->end = parser->pos + 1; 203 break; 204 } 205 } 206 /* Error if unmatched closing bracket */ 207 if (i == -1) return JSMN_ERROR_INVAL; 208 for (; i >= 0; i--) { 209 token = &tokens[i]; 210 if (token->start != -1 && token->end == -1) { 211 parser->toksuper = i; 212 break; 213 } 214 } 215#endif 216 break; 217 case '\"': 218 r = jsmn_parse_string(parser, js, tokens, num_tokens); 219 if (r < 0) return r; 220 if (parser->toksuper != -1) 221 tokens[parser->toksuper].size++; 222 break; 223 case '\t' : case '\r' : case '\n' : case ':' : case ',': case ' ': 224 break; 225#ifdef JSMN_STRICT 226 /* In strict mode primitives are: numbers and booleans */ 227 case '-': case '0': case '1' : case '2': case '3' : case '4': 228 case '5': case '6': case '7' : case '8': case '9': 229 case 't': case 'f': case 'n' : 230#else 231 /* In non-strict mode every unquoted value is a primitive */ 232 default: 233#endif 234 r = jsmn_parse_primitive(parser, js, tokens, num_tokens); 235 if (r < 0) return r; 236 if (parser->toksuper != -1) 237 tokens[parser->toksuper].size++; 238 break; 239 240#ifdef JSMN_STRICT 241 /* Unexpected char in strict mode */ 242 default: 243 return JSMN_ERROR_INVAL; 244#endif 245 246 } 247 } 248 249 for (i = parser->toknext - 1; i >= 0; i--) { 250 /* Unmatched opened object or array */ 251 if (tokens[i].start != -1 && tokens[i].end == -1) { 252 return JSMN_ERROR_PART; 253 } 254 } 255 256 return JSMN_SUCCESS; 257} 258 259/** 260 * Creates a new parser based over a given buffer with an array of tokens 261 * available. 262 */ 263void jsmn_init(jsmn_parser *parser) { 264 parser->pos = 0; 265 parser->toknext = 0; 266 parser->toksuper = -1; 267} 268 269