jsmn.c 8.3 KB

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