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