jsmn.c 7.8 KB

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