xs_json.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  1. /* copyright (c) 2022 - 2026 grunfink et al. / MIT license */
  2. #ifndef _XS_JSON_H
  3. #define _XS_JSON_H
  4. #ifndef MAX_JSON_DEPTH
  5. #define MAX_JSON_DEPTH 32
  6. #endif
  7. #ifdef NO_FMEMOPEN
  8. typedef struct {
  9. union { FILE *f; const char *b; } src;
  10. const char *b_end;
  11. } json_src;
  12. #else
  13. #define json_src FILE
  14. #endif
  15. void xs_json_dump_value(const xs_val *data, int level, int indent, FILE *f);
  16. int xs_json_dump(const xs_val *data, int indent, FILE *f);
  17. xs_str *xs_json_dumps(const xs_val *data, int indent);
  18. xs_val *xs_json_load_full(FILE *f, int maxdepth);
  19. xs_val *xs_json_loads_full(const xs_str *json, int maxdepth);
  20. #define xs_json_load(f) xs_json_load_full(f, MAX_JSON_DEPTH)
  21. #define xs_json_loads(s) xs_json_loads_full(s, MAX_JSON_DEPTH)
  22. xstype xs_json_load_type(json_src *f);
  23. int xs_json_load_array_iter(json_src *f, xs_val **value, xstype *pt, int *c);
  24. int xs_json_load_object_iter(json_src *f, xs_str **key, xs_val **value, xstype *pt, int *c);
  25. int xs_json_load_array(json_src *f, int maxdepth, xs_list **l);
  26. int xs_json_load_object(json_src *f, int maxdepth, xs_dict **d);
  27. #ifdef XS_IMPLEMENTATION
  28. /** IMPLEMENTATION **/
  29. #include "xs_unicode.h"
  30. /** JSON dumps **/
  31. static void _xs_json_dump_str(const char *data, FILE *f)
  32. /* dumps a string in JSON format */
  33. {
  34. unsigned char c;
  35. fputs("\"", f);
  36. while ((c = *data)) {
  37. if (c == '\n')
  38. fputs("\\n", f);
  39. else
  40. if (c == '\r')
  41. fputs("\\r", f);
  42. else
  43. if (c == '\t')
  44. fputs("\\t", f);
  45. else
  46. if (c == '\\')
  47. fputs("\\\\", f);
  48. else
  49. if (c == '"')
  50. fputs("\\\"", f);
  51. else
  52. if (c < 32)
  53. fprintf(f, "\\u%04x", (unsigned int) c);
  54. else
  55. fputc(c, f);
  56. data++;
  57. }
  58. fputs("\"", f);
  59. }
  60. static void _xs_json_indent(int level, int indent, FILE *f)
  61. /* adds indentation */
  62. {
  63. if (indent) {
  64. int n;
  65. fputc('\n', f);
  66. for (n = 0; n < level * indent; n++)
  67. fputc(' ', f);
  68. }
  69. }
  70. void xs_json_dump_value(const xs_val *data, int level, int indent, FILE *f)
  71. /* dumps partial data as JSON */
  72. {
  73. int c = 0;
  74. const xs_val *v;
  75. switch (xs_type(data)) {
  76. case XSTYPE_NULL:
  77. fputs("null", f);
  78. break;
  79. case XSTYPE_TRUE:
  80. fputs("true", f);
  81. break;
  82. case XSTYPE_FALSE:
  83. fputs("false", f);
  84. break;
  85. case XSTYPE_NUMBER:
  86. fputs(xs_number_str(data), f);
  87. break;
  88. case XSTYPE_LIST:
  89. fputc('[', f);
  90. xs_list_foreach(data, v) {
  91. if (c != 0)
  92. fputc(',', f);
  93. _xs_json_indent(level + 1, indent, f);
  94. xs_json_dump_value(v, level + 1, indent, f);
  95. c++;
  96. }
  97. _xs_json_indent(level, indent, f);
  98. fputc(']', f);
  99. break;
  100. case XSTYPE_DICT:
  101. fputc('{', f);
  102. const xs_str *k;
  103. xs_dict_foreach(data, k, v) {
  104. if (c != 0)
  105. fputc(',', f);
  106. _xs_json_indent(level + 1, indent, f);
  107. _xs_json_dump_str(k, f);
  108. fputc(':', f);
  109. if (indent)
  110. fputc(' ', f);
  111. xs_json_dump_value(v, level + 1, indent, f);
  112. c++;
  113. }
  114. _xs_json_indent(level, indent, f);
  115. fputc('}', f);
  116. break;
  117. case XSTYPE_STRING:
  118. _xs_json_dump_str(data, f);
  119. break;
  120. default:
  121. break;
  122. }
  123. }
  124. int xs_json_dump(const xs_val *data, int indent, FILE *f)
  125. /* dumps data into a file as JSON */
  126. {
  127. xstype t = xs_type(data);
  128. if (t == XSTYPE_LIST || t == XSTYPE_DICT) {
  129. xs_json_dump_value(data, 0, indent, f);
  130. return 1;
  131. }
  132. return 0;
  133. }
  134. xs_str *xs_json_dumps(const xs_val *data, int indent)
  135. /* dumps data as a JSON string */
  136. {
  137. xs_str *s = NULL;
  138. size_t sz;
  139. FILE *f;
  140. if ((f = open_memstream(&s, &sz)) != NULL) {
  141. int r = xs_json_dump(data, indent, f);
  142. fclose(f);
  143. if (!r)
  144. s = xs_free(s);
  145. }
  146. return s;
  147. }
  148. /** JSON loads **/
  149. typedef enum {
  150. JS_ERROR = -1,
  151. JS_INCOMPLETE,
  152. JS_OCURLY,
  153. JS_OBRACK,
  154. JS_CCURLY,
  155. JS_CBRACK,
  156. JS_COMMA,
  157. JS_COLON,
  158. JS_VALUE,
  159. JS_STRING,
  160. JS_NUMBER,
  161. JS_TRUE,
  162. JS_FALSE,
  163. JS_NULL,
  164. JS_ARRAY,
  165. JS_OBJECT
  166. } js_type;
  167. #ifdef NO_FMEMOPEN
  168. static int jgetc(json_src *f)
  169. /* calls fgetc on an underlying file, otherwise reads the buffer */
  170. {
  171. if (f->b_end == NULL)
  172. return fgetc(f->src.f);
  173. else
  174. if (f->src.b >= f->b_end)
  175. return EOF;
  176. else
  177. return *(f->src.b)++;
  178. }
  179. static int jungetc(int c, json_src *f)
  180. /* calls ungetc on an underlying file, otherwise decrements the buf ptr */
  181. /* this function will NOT mutate an underlying buffer */
  182. {
  183. if (f->b_end == NULL)
  184. return ungetc(c, f->src.f);
  185. else
  186. if (c == EOF)
  187. return EOF;
  188. else {
  189. f->src.b--;
  190. return c;
  191. }
  192. }
  193. #define jscanf(f, fmt, arg) _jscanf(f, fmt, fmt "%n", arg)
  194. static int _jscanf(json_src *restrict f, const char *restrict fmt, const char *restrict fmt2, void *arg)
  195. /* calls fscanf on an underlying file, otherwise reads the buffer. */
  196. {
  197. if (f->b_end == NULL)
  198. return fscanf(f->src.f, fmt, arg);
  199. else
  200. if (f->src.b >= f->b_end)
  201. return EOF;
  202. else {
  203. int n, b;
  204. n = sscanf(f->src.b, fmt2, arg, &b);
  205. if (n >= 0)
  206. f->src.b += b;
  207. return n;
  208. }
  209. }
  210. #else
  211. #define jgetc(f) fgetc(f)
  212. #define jungetc(c, f) ungetc(c, f)
  213. #define jscanf(f, fmt, ...) fscanf(f, fmt, __VA_ARGS__)
  214. #endif /* NO_FMEMOPEN */
  215. static xs_val *_xs_json_load_lexer(json_src *f, js_type *t)
  216. {
  217. int c;
  218. xs_val *v = NULL;
  219. int offset;
  220. *t = JS_ERROR;
  221. /* skip blanks */
  222. while ((c = jgetc(f)) == ' ' || c == '\t' || c == '\n' || c == '\r');
  223. if (c == '{')
  224. *t = JS_OCURLY;
  225. else
  226. if (c == '}')
  227. *t = JS_CCURLY;
  228. else
  229. if (c == '[')
  230. *t = JS_OBRACK;
  231. else
  232. if (c == ']')
  233. *t = JS_CBRACK;
  234. else
  235. if (c == ',')
  236. *t = JS_COMMA;
  237. else
  238. if (c == ':')
  239. *t = JS_COLON;
  240. else
  241. if (c == '"') {
  242. *t = JS_STRING;
  243. v = xs_str_new(NULL);
  244. offset = 0;
  245. while ((c = jgetc(f)) != '"' && c != EOF && *t != JS_ERROR) {
  246. if (c == '\\') {
  247. unsigned int cp = jgetc(f);
  248. switch (cp) {
  249. case 'n': cp = '\n'; break;
  250. case 'r': cp = '\r'; break;
  251. case 't': cp = '\t'; break;
  252. case '"': cp = '"'; break;
  253. case '\\': cp = '\\'; break;
  254. case '/': cp = '/'; break;
  255. case 'u': /* Unicode codepoint as an hex char */
  256. if (jscanf(f, "%04x", &cp) != 1) {
  257. *t = JS_ERROR;
  258. break;
  259. }
  260. if (xs_is_surrogate(cp)) {
  261. /* \u must follow */
  262. if (jgetc(f) != '\\' || jgetc(f) != 'u') {
  263. *t = JS_ERROR;
  264. break;
  265. }
  266. unsigned int p2;
  267. if (jscanf(f, "%04x", &p2) != 1) {
  268. *t = JS_ERROR;
  269. break;
  270. }
  271. cp = xs_surrogate_dec(cp, p2);
  272. }
  273. /* replace dangerous control codes with their visual representations */
  274. if (cp == 0 || (cp < ' ' && !strchr("\r\n\t", cp)))
  275. cp += 0x2400;
  276. break;
  277. default:
  278. *t = JS_ERROR;
  279. break;
  280. }
  281. v = xs_utf8_insert(v, cp, &offset);
  282. }
  283. else {
  284. if (c >= 0 && c < ' ') {
  285. v = xs_utf8_insert(v, c + 0x2400, &offset);
  286. }
  287. else {
  288. char cc = c;
  289. v = xs_insert_m(v, offset, &cc, 1);
  290. offset++;
  291. }
  292. if (!xs_is_string(v)) {
  293. *t = JS_ERROR;
  294. break;
  295. }
  296. }
  297. }
  298. if (c == EOF)
  299. *t = JS_ERROR;
  300. }
  301. else
  302. if (c == '-' || (c >= '0' && c <= '9') || c == '.') {
  303. double d;
  304. jungetc(c, f);
  305. if (jscanf(f, "%lf", &d) == 1) {
  306. *t = JS_NUMBER;
  307. v = xs_number_new(d);
  308. }
  309. }
  310. else
  311. if (c == 't') {
  312. if (jgetc(f) == 'r' && jgetc(f) == 'u' && jgetc(f) == 'e') {
  313. *t = JS_TRUE;
  314. v = xs_val_new(XSTYPE_TRUE);
  315. }
  316. }
  317. else
  318. if (c == 'f') {
  319. if (jgetc(f) == 'a' && jgetc(f) == 'l' &&
  320. jgetc(f) == 's' && jgetc(f) == 'e') {
  321. *t = JS_FALSE;
  322. v = xs_val_new(XSTYPE_FALSE);
  323. }
  324. }
  325. else
  326. if (c == 'n') {
  327. if (jgetc(f) == 'u' && jgetc(f) == 'l' && jgetc(f) == 'l') {
  328. *t = JS_NULL;
  329. v = xs_val_new(XSTYPE_NULL);
  330. }
  331. }
  332. if (*t == JS_ERROR)
  333. v = xs_free(v);
  334. return v;
  335. }
  336. int xs_json_load_array_iter(json_src *f, xs_val **value, xstype *pt, int *c)
  337. /* loads the next scalar value from the JSON stream */
  338. /* if the value ahead is compound, value is NULL and pt is set */
  339. {
  340. js_type t;
  341. *value = _xs_json_load_lexer(f, &t);
  342. if (t == JS_ERROR)
  343. return -1;
  344. if (t == JS_CBRACK)
  345. return 0;
  346. if (*c > 0) {
  347. if (t == JS_COMMA)
  348. *value = _xs_json_load_lexer(f, &t);
  349. else
  350. return -1;
  351. }
  352. if (*value == NULL) {
  353. /* possible compound type ahead */
  354. if (t == JS_OBRACK)
  355. *pt = XSTYPE_LIST;
  356. else
  357. if (t == JS_OCURLY)
  358. *pt = XSTYPE_DICT;
  359. else
  360. return -1;
  361. }
  362. else
  363. *pt = xs_type(*value);
  364. *c = *c + 1;
  365. return 1;
  366. }
  367. int xs_json_load_array(json_src *f, int maxdepth, xs_list **l)
  368. /* loads a full JSON array (after the initial OBRACK) */
  369. /* l can be NULL for the content to be dropped */
  370. {
  371. xstype t;
  372. int r = 0;
  373. int c = 0;
  374. for (;;) {
  375. xs *v = NULL;
  376. r = xs_json_load_array_iter(f, &v, &t, &c);
  377. if (r == 1) {
  378. /* partial load? */
  379. if (v == NULL && maxdepth != 0) {
  380. if (t == XSTYPE_LIST) {
  381. if (l)
  382. v = xs_list_new();
  383. r = xs_json_load_array(f, maxdepth - 1, &v);
  384. }
  385. else
  386. if (t == XSTYPE_DICT) {
  387. if (l)
  388. v = xs_dict_new();
  389. r = xs_json_load_object(f, maxdepth - 1, &v);
  390. }
  391. }
  392. /* error? */
  393. if (r < 0)
  394. break;
  395. if (l)
  396. *l = xs_list_append(*l, v);
  397. }
  398. else
  399. break;
  400. }
  401. if (r < 0 && l)
  402. *l = xs_free(*l);
  403. return r;
  404. }
  405. int xs_json_load_object_iter(json_src *f, xs_str **key, xs_val **value, xstype *pt, int *c)
  406. /* loads the next key and scalar value from the JSON stream */
  407. /* if the value ahead is compound, value is NULL and pt is set */
  408. {
  409. js_type t;
  410. *key = _xs_json_load_lexer(f, &t);
  411. if (t == JS_ERROR)
  412. return -1;
  413. if (t == JS_CCURLY)
  414. return 0;
  415. if (*c > 0) {
  416. if (t == JS_COMMA)
  417. *key = _xs_json_load_lexer(f, &t);
  418. else
  419. return -1;
  420. }
  421. if (t != JS_STRING)
  422. return -1;
  423. xs_free(_xs_json_load_lexer(f, &t));
  424. if (t != JS_COLON)
  425. return -1;
  426. *value = _xs_json_load_lexer(f, &t);
  427. if (*value == NULL) {
  428. /* possible complex type ahead */
  429. if (t == JS_OBRACK)
  430. *pt = XSTYPE_LIST;
  431. else
  432. if (t == JS_OCURLY)
  433. *pt = XSTYPE_DICT;
  434. else
  435. return -1;
  436. }
  437. else
  438. *pt = xs_type(*value);
  439. *c = *c + 1;
  440. return 1;
  441. }
  442. int xs_json_load_object(json_src *f, int maxdepth, xs_dict **d)
  443. /* loads a full JSON object (after the initial OCURLY) */
  444. /* d can be NULL for the content to be dropped */
  445. {
  446. xstype t;
  447. int r = 0;
  448. int c = 0;
  449. for (;;) {
  450. xs *k = NULL;
  451. xs *v = NULL;
  452. r = xs_json_load_object_iter(f, &k, &v, &t, &c);
  453. if (r == 1) {
  454. /* partial load? */
  455. if (v == NULL && maxdepth != 0) {
  456. if (t == XSTYPE_LIST) {
  457. if (d)
  458. v = xs_list_new();
  459. r = xs_json_load_array(f, maxdepth - 1, &v);
  460. }
  461. else
  462. if (t == XSTYPE_DICT) {
  463. if (d)
  464. v = xs_dict_new();
  465. r = xs_json_load_object(f, maxdepth - 1, &v);
  466. }
  467. }
  468. /* error? */
  469. if (r < 0)
  470. break;
  471. if (d)
  472. *d = xs_dict_append(*d, k, v);
  473. }
  474. else
  475. break;
  476. }
  477. if (r < 0 && d)
  478. *d = xs_free(*d);
  479. return r;
  480. }
  481. xstype xs_json_load_type(json_src *f)
  482. /* identifies the type of a JSON stream */
  483. {
  484. xstype t = XSTYPE_NULL;
  485. js_type jt;
  486. xs_free(_xs_json_load_lexer(f, &jt));
  487. if (jt == JS_OBRACK)
  488. t = XSTYPE_LIST;
  489. else
  490. if (jt == JS_OCURLY)
  491. t = XSTYPE_DICT;
  492. return t;
  493. }
  494. #ifdef NO_FMEMOPEN
  495. static xs_val *_xs_json_load(json_src *f, int maxdepth)
  496. #else
  497. xs_val *xs_json_load_full(FILE *f, int maxdepth)
  498. /* loads a JSON file */
  499. #endif
  500. {
  501. xs_val *v = NULL;
  502. xstype t = xs_json_load_type(f);
  503. if (t == XSTYPE_LIST) {
  504. v = xs_list_new();
  505. xs_json_load_array(f, maxdepth, &v);
  506. }
  507. else
  508. if (t == XSTYPE_DICT) {
  509. v = xs_dict_new();
  510. xs_json_load_object(f, maxdepth, &v);
  511. }
  512. return v;
  513. }
  514. #ifdef NO_FMEMOPEN
  515. xs_val *xs_json_load_full(FILE *fp, int maxdepth)
  516. /* loads a JSON file */
  517. {
  518. json_src f;
  519. f.src.f = fp;
  520. f.b_end = NULL;
  521. return _xs_json_load(&f, maxdepth);
  522. }
  523. #endif
  524. xs_val *xs_json_loads_full(const xs_str *json, int maxdepth)
  525. /* loads a string in JSON format and converts to a multiple data */
  526. {
  527. #ifdef NO_FMEMOPEN
  528. json_src f;
  529. f.src.b = json;
  530. f.b_end = json+strlen(json);
  531. return _xs_json_load(&f, maxdepth);
  532. #else
  533. FILE *f;
  534. xs_val *v = NULL;
  535. if ((f = fmemopen((char *)json, strlen(json), "r")) != NULL) {
  536. v = xs_json_load_full(f, maxdepth);
  537. fclose(f);
  538. }
  539. return v;
  540. #endif
  541. }
  542. #endif /* XS_IMPLEMENTATION */
  543. #endif /* _XS_JSON_H */