jsmn.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. /*
  2. * MIT License
  3. *
  4. * Copyright (c) 2010 Serge Zaitsev
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  22. * SOFTWARE.
  23. */
  24. #ifndef JSMN_H
  25. #define JSMN_H
  26. #include <stddef.h>
  27. #ifdef __cplusplus
  28. extern "C" {
  29. #endif
  30. #ifdef JSMN_STATIC
  31. #define JSMN_API static
  32. #else
  33. #define JSMN_API extern
  34. #endif
  35. /**
  36. * JSON type identifier. Basic types are:
  37. * o Object
  38. * o Array
  39. * o String
  40. * o Other primitive: number, boolean (true/false) or null
  41. */
  42. typedef enum {
  43. JSMN_UNDEFINED = 0,
  44. JSMN_OBJECT = 1,
  45. JSMN_ARRAY = 2,
  46. JSMN_STRING = 3,
  47. JSMN_PRIMITIVE = 4
  48. } jsmntype_t;
  49. enum jsmnerr {
  50. /* Not enough tokens were provided */
  51. JSMN_ERROR_NOMEM = -1,
  52. /* Invalid character inside JSON string */
  53. JSMN_ERROR_INVAL = -2,
  54. /* The string is not a full JSON packet, more bytes expected */
  55. JSMN_ERROR_PART = -3
  56. };
  57. /**
  58. * JSON token description.
  59. * type type (object, array, string etc.)
  60. * start start position in JSON data string
  61. * end end position in JSON data string
  62. */
  63. typedef struct jsmntok {
  64. jsmntype_t type;
  65. int start;
  66. int end;
  67. int size;
  68. #ifdef JSMN_PARENT_LINKS
  69. int parent;
  70. #endif
  71. } jsmntok_t;
  72. /**
  73. * JSON parser. Contains an array of token blocks available. Also stores
  74. * the string being parsed now and current position in that string.
  75. */
  76. typedef struct jsmn_parser {
  77. unsigned int pos; /* offset in the JSON string */
  78. unsigned int toknext; /* next token to allocate */
  79. int toksuper; /* superior token node, e.g. parent object or array */
  80. } jsmn_parser;
  81. /**
  82. * Create JSON parser over an array of tokens
  83. */
  84. JSMN_API void jsmn_init(jsmn_parser *parser);
  85. /**
  86. * Run JSON parser. It parses a JSON data string into and array of tokens, each
  87. * describing
  88. * a single JSON object.
  89. */
  90. JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
  91. jsmntok_t *tokens, const unsigned int num_tokens);
  92. #ifndef JSMN_HEADER
  93. /**
  94. * Allocates a fresh unused token from the token pool.
  95. */
  96. static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens,
  97. const size_t num_tokens) {
  98. jsmntok_t *tok;
  99. if (parser->toknext >= num_tokens) {
  100. return NULL;
  101. }
  102. tok = &tokens[parser->toknext++];
  103. tok->start = tok->end = -1;
  104. tok->size = 0;
  105. #ifdef JSMN_PARENT_LINKS
  106. tok->parent = -1;
  107. #endif
  108. return tok;
  109. }
  110. /**
  111. * Fills token type and boundaries.
  112. */
  113. static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type,
  114. const int start, const int end) {
  115. token->type = type;
  116. token->start = start;
  117. token->end = end;
  118. token->size = 0;
  119. }
  120. /**
  121. * Fills next available token with JSON primitive.
  122. */
  123. static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
  124. const size_t len, jsmntok_t *tokens,
  125. const size_t num_tokens) {
  126. jsmntok_t *token;
  127. int start;
  128. start = parser->pos;
  129. for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
  130. switch (js[parser->pos]) {
  131. #ifndef JSMN_STRICT
  132. /* In strict mode primitive must be followed by "," or "}" or "]" */
  133. case ':':
  134. #endif
  135. case '\t':
  136. case '\r':
  137. case '\n':
  138. case ' ':
  139. case ',':
  140. case ']':
  141. case '}':
  142. goto found;
  143. default:
  144. /* to quiet a warning from gcc*/
  145. break;
  146. }
  147. if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
  148. parser->pos = start;
  149. return JSMN_ERROR_INVAL;
  150. }
  151. }
  152. #ifdef JSMN_STRICT
  153. /* In strict mode primitive must be followed by a comma/object/array */
  154. parser->pos = start;
  155. return JSMN_ERROR_PART;
  156. #endif
  157. found:
  158. if (tokens == NULL) {
  159. parser->pos--;
  160. return 0;
  161. }
  162. token = jsmn_alloc_token(parser, tokens, num_tokens);
  163. if (token == NULL) {
  164. parser->pos = start;
  165. return JSMN_ERROR_NOMEM;
  166. }
  167. jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
  168. #ifdef JSMN_PARENT_LINKS
  169. token->parent = parser->toksuper;
  170. #endif
  171. parser->pos--;
  172. return 0;
  173. }
  174. /**
  175. * Fills next token with JSON string.
  176. */
  177. static int jsmn_parse_string(jsmn_parser *parser, const char *js,
  178. const size_t len, jsmntok_t *tokens,
  179. const size_t num_tokens) {
  180. jsmntok_t *token;
  181. int start = parser->pos;
  182. parser->pos++;
  183. /* Skip starting quote */
  184. for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
  185. char c = js[parser->pos];
  186. /* Quote: end of string */
  187. if (c == '\"') {
  188. if (tokens == NULL) {
  189. return 0;
  190. }
  191. token = jsmn_alloc_token(parser, tokens, num_tokens);
  192. if (token == NULL) {
  193. parser->pos = start;
  194. return JSMN_ERROR_NOMEM;
  195. }
  196. jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos);
  197. #ifdef JSMN_PARENT_LINKS
  198. token->parent = parser->toksuper;
  199. #endif
  200. return 0;
  201. }
  202. /* Backslash: Quoted symbol expected */
  203. if (c == '\\' && parser->pos + 1 < len) {
  204. int i;
  205. parser->pos++;
  206. switch (js[parser->pos]) {
  207. /* Allowed escaped symbols */
  208. case '\"':
  209. case '/':
  210. case '\\':
  211. case 'b':
  212. case 'f':
  213. case 'r':
  214. case 'n':
  215. case 't':
  216. break;
  217. /* Allows escaped symbol \uXXXX */
  218. case 'u':
  219. parser->pos++;
  220. for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0';
  221. i++) {
  222. /* If it isn't a hex character we have an error */
  223. if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
  224. (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
  225. (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
  226. parser->pos = start;
  227. return JSMN_ERROR_INVAL;
  228. }
  229. parser->pos++;
  230. }
  231. parser->pos--;
  232. break;
  233. /* Unexpected symbol */
  234. default:
  235. parser->pos = start;
  236. return JSMN_ERROR_INVAL;
  237. }
  238. }
  239. }
  240. parser->pos = start;
  241. return JSMN_ERROR_PART;
  242. }
  243. /**
  244. * Parse JSON string and fill tokens.
  245. */
  246. JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
  247. jsmntok_t *tokens, const unsigned int num_tokens) {
  248. int r;
  249. int i;
  250. jsmntok_t *token;
  251. int count = parser->toknext;
  252. for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
  253. char c;
  254. jsmntype_t type;
  255. c = js[parser->pos];
  256. switch (c) {
  257. case '{':
  258. case '[':
  259. count++;
  260. if (tokens == NULL) {
  261. break;
  262. }
  263. token = jsmn_alloc_token(parser, tokens, num_tokens);
  264. if (token == NULL) {
  265. return JSMN_ERROR_NOMEM;
  266. }
  267. if (parser->toksuper != -1) {
  268. jsmntok_t *t = &tokens[parser->toksuper];
  269. #ifdef JSMN_STRICT
  270. /* In strict mode an object or array can't become a key */
  271. if (t->type == JSMN_OBJECT) {
  272. return JSMN_ERROR_INVAL;
  273. }
  274. #endif
  275. t->size++;
  276. #ifdef JSMN_PARENT_LINKS
  277. token->parent = parser->toksuper;
  278. #endif
  279. }
  280. token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
  281. token->start = parser->pos;
  282. parser->toksuper = parser->toknext - 1;
  283. break;
  284. case '}':
  285. case ']':
  286. if (tokens == NULL) {
  287. break;
  288. }
  289. type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
  290. #ifdef JSMN_PARENT_LINKS
  291. if (parser->toknext < 1) {
  292. return JSMN_ERROR_INVAL;
  293. }
  294. token = &tokens[parser->toknext - 1];
  295. for (;;) {
  296. if (token->start != -1 && token->end == -1) {
  297. if (token->type != type) {
  298. return JSMN_ERROR_INVAL;
  299. }
  300. token->end = parser->pos + 1;
  301. parser->toksuper = token->parent;
  302. break;
  303. }
  304. if (token->parent == -1) {
  305. if (token->type != type || parser->toksuper == -1) {
  306. return JSMN_ERROR_INVAL;
  307. }
  308. break;
  309. }
  310. token = &tokens[token->parent];
  311. }
  312. #else
  313. for (i = parser->toknext - 1; i >= 0; i--) {
  314. token = &tokens[i];
  315. if (token->start != -1 && token->end == -1) {
  316. if (token->type != type) {
  317. return JSMN_ERROR_INVAL;
  318. }
  319. parser->toksuper = -1;
  320. token->end = parser->pos + 1;
  321. break;
  322. }
  323. }
  324. /* Error if unmatched closing bracket */
  325. if (i == -1) {
  326. return JSMN_ERROR_INVAL;
  327. }
  328. for (; i >= 0; i--) {
  329. token = &tokens[i];
  330. if (token->start != -1 && token->end == -1) {
  331. parser->toksuper = i;
  332. break;
  333. }
  334. }
  335. #endif
  336. break;
  337. case '\"':
  338. r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
  339. if (r < 0) {
  340. return r;
  341. }
  342. count++;
  343. if (parser->toksuper != -1 && tokens != NULL) {
  344. tokens[parser->toksuper].size++;
  345. }
  346. break;
  347. case '\t':
  348. case '\r':
  349. case '\n':
  350. case ' ':
  351. break;
  352. case ':':
  353. parser->toksuper = parser->toknext - 1;
  354. break;
  355. case ',':
  356. if (tokens != NULL && parser->toksuper != -1 &&
  357. tokens[parser->toksuper].type != JSMN_ARRAY &&
  358. tokens[parser->toksuper].type != JSMN_OBJECT) {
  359. #ifdef JSMN_PARENT_LINKS
  360. parser->toksuper = tokens[parser->toksuper].parent;
  361. #else
  362. for (i = parser->toknext - 1; i >= 0; i--) {
  363. if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
  364. if (tokens[i].start != -1 && tokens[i].end == -1) {
  365. parser->toksuper = i;
  366. break;
  367. }
  368. }
  369. }
  370. #endif
  371. }
  372. break;
  373. #ifdef JSMN_STRICT
  374. /* In strict mode primitives are: numbers and booleans */
  375. case '-':
  376. case '0':
  377. case '1':
  378. case '2':
  379. case '3':
  380. case '4':
  381. case '5':
  382. case '6':
  383. case '7':
  384. case '8':
  385. case '9':
  386. case 't':
  387. case 'f':
  388. case 'n':
  389. /* And they must not be keys of the object */
  390. if (tokens != NULL && parser->toksuper != -1) {
  391. const jsmntok_t *t = &tokens[parser->toksuper];
  392. if (t->type == JSMN_OBJECT ||
  393. (t->type == JSMN_STRING && t->size != 0)) {
  394. return JSMN_ERROR_INVAL;
  395. }
  396. }
  397. #else
  398. /* In non-strict mode every unquoted value is a primitive */
  399. default:
  400. #endif
  401. r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
  402. if (r < 0) {
  403. return r;
  404. }
  405. count++;
  406. if (parser->toksuper != -1 && tokens != NULL) {
  407. tokens[parser->toksuper].size++;
  408. }
  409. break;
  410. #ifdef JSMN_STRICT
  411. /* Unexpected char in strict mode */
  412. default:
  413. return JSMN_ERROR_INVAL;
  414. #endif
  415. }
  416. }
  417. if (tokens != NULL) {
  418. for (i = parser->toknext - 1; i >= 0; i--) {
  419. /* Unmatched opened object or array */
  420. if (tokens[i].start != -1 && tokens[i].end == -1) {
  421. return JSMN_ERROR_PART;
  422. }
  423. }
  424. }
  425. return count;
  426. }
  427. /**
  428. * Creates a new parser based over a given buffer with an array of tokens
  429. * available.
  430. */
  431. JSMN_API void jsmn_init(jsmn_parser *parser) {
  432. parser->pos = 0;
  433. parser->toknext = 0;
  434. parser->toksuper = -1;
  435. }
  436. #endif /* JSMN_HEADER */
  437. #ifdef __cplusplus
  438. }
  439. #endif
  440. #endif /* JSMN_H */