jsmn.c 8.3 KB

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