jsmn.c 9.7 KB

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