xs_json.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. /* copyright (c) 2022 grunfink - MIT license */
  2. #ifndef _XS_JSON_H
  3. #define _XS_JSON_H
  4. d_char *xs_json_dumps_pp(char *data, int indent);
  5. #define xs_json_dumps(data) xs_json_dumps_pp(data, 0)
  6. d_char *xs_json_loads(const char *json);
  7. #ifdef XS_IMPLEMENTATION
  8. /** IMPLEMENTATION **/
  9. /** JSON dumps **/
  10. d_char *_xs_json_dumps_str(d_char *s, char *data)
  11. /* dumps a string in JSON format */
  12. {
  13. unsigned char c;
  14. s = xs_str_cat(s, "\"");
  15. while ((c = *data)) {
  16. if (c == '\n')
  17. s = xs_str_cat(s, "\\n");
  18. else
  19. if (c == '\r')
  20. s = xs_str_cat(s, "\\r");
  21. else
  22. if (c == '\t')
  23. s = xs_str_cat(s, "\\t");
  24. else
  25. if (c == '\\')
  26. s = xs_str_cat(s, "\\\\");
  27. else
  28. if (c == '"')
  29. s = xs_str_cat(s, "\\\"");
  30. else
  31. if (c < 32) {
  32. char tmp[10];
  33. sprintf(tmp, "\\u%04x", (unsigned int) c);
  34. s = xs_str_cat(s, tmp);
  35. }
  36. else
  37. s = xs_append_m(s, data, 1);
  38. data++;
  39. }
  40. s = xs_str_cat(s, "\"");
  41. return s;
  42. }
  43. d_char *_xs_json_indent(d_char *s, int level, int indent)
  44. /* adds indentation */
  45. {
  46. if (indent) {
  47. int n;
  48. s = xs_str_cat(s, "\n");
  49. for (n = 0; n < level * indent; n++)
  50. s = xs_str_cat(s, " ");
  51. }
  52. return s;
  53. }
  54. d_char *_xs_json_dumps(d_char *s, char *data, int level, int indent)
  55. /* dumps partial data as JSON */
  56. {
  57. char *k, *v;
  58. int c = 0;
  59. switch (xs_type(data)) {
  60. case XSTYPE_NULL:
  61. s = xs_str_cat(s, "null");
  62. break;
  63. case XSTYPE_TRUE:
  64. s = xs_str_cat(s, "true");
  65. break;
  66. case XSTYPE_FALSE:
  67. s = xs_str_cat(s, "false");
  68. break;
  69. case XSTYPE_NUMBER:
  70. {
  71. char tmp[32];
  72. snprintf(tmp, sizeof(tmp), "%.15f", xs_number_get(data));
  73. /* strip useless zeros */
  74. if (strchr(tmp, '.') != NULL) {
  75. char *ptr;
  76. for (ptr = tmp + strlen(tmp) - 1; *ptr == '0'; ptr--);
  77. if (*ptr != '.')
  78. ptr++;
  79. *ptr = '\0';
  80. }
  81. s = xs_str_cat(s, tmp);
  82. }
  83. break;
  84. case XSTYPE_LIST:
  85. s = xs_str_cat(s, "[");
  86. while (xs_list_iter(&data, &v)) {
  87. if (c != 0)
  88. s = xs_str_cat(s, ",");
  89. s = _xs_json_indent(s, level + 1, indent);
  90. s = _xs_json_dumps(s, v, level + 1, indent);
  91. c++;
  92. }
  93. s = _xs_json_indent(s, level, indent);
  94. s = xs_str_cat(s, "]");
  95. break;
  96. case XSTYPE_DICT:
  97. s = xs_str_cat(s, "{");
  98. while (xs_dict_iter(&data, &k, &v)) {
  99. if (c != 0)
  100. s = xs_str_cat(s, ",");
  101. s = _xs_json_indent(s, level + 1, indent);
  102. s = _xs_json_dumps_str(s, k);
  103. s = xs_str_cat(s, ":");
  104. if (indent)
  105. s = xs_str_cat(s, " ");
  106. s = _xs_json_dumps(s, v, level + 1, indent);
  107. c++;
  108. }
  109. s = _xs_json_indent(s, level, indent);
  110. s = xs_str_cat(s, "}");
  111. break;
  112. case XSTYPE_STRING:
  113. s = _xs_json_dumps_str(s, data);
  114. break;
  115. default:
  116. break;
  117. }
  118. return s;
  119. }
  120. d_char *xs_json_dumps_pp(char *data, int indent)
  121. /* dumps a piece of data as JSON */
  122. {
  123. xstype t = xs_type(data);
  124. d_char *s = NULL;
  125. if (t == XSTYPE_LIST || t == XSTYPE_DICT) {
  126. s = xs_str_new(NULL);
  127. s = _xs_json_dumps(s, data, 0, indent);
  128. }
  129. return s;
  130. }
  131. /** JSON loads **/
  132. /* this code comes mostly from the Minimum Profit Text Editor (MPDM) */
  133. typedef enum {
  134. JS_ERROR = -1,
  135. JS_INCOMPLETE,
  136. JS_OCURLY,
  137. JS_OBRACK,
  138. JS_CCURLY,
  139. JS_CBRACK,
  140. JS_COMMA,
  141. JS_COLON,
  142. JS_VALUE,
  143. JS_STRING,
  144. JS_INTEGER,
  145. JS_REAL,
  146. JS_TRUE,
  147. JS_FALSE,
  148. JS_NULL,
  149. JS_ARRAY,
  150. JS_OBJECT
  151. } js_type;
  152. d_char *_xs_json_loads_lexer(const char **json, js_type *t)
  153. {
  154. char c;
  155. const char *s = *json;
  156. d_char *v = NULL;
  157. /* skip blanks */
  158. while (*s == L' ' || *s == L'\t' || *s == L'\n' || *s == L'\r')
  159. s++;
  160. c = *s++;
  161. if (c == '{')
  162. *t = JS_OCURLY;
  163. else
  164. if (c == '}')
  165. *t = JS_CCURLY;
  166. else
  167. if (c == '[')
  168. *t = JS_OBRACK;
  169. else
  170. if (c == ']')
  171. *t = JS_CBRACK;
  172. else
  173. if (c == ',')
  174. *t = JS_COMMA;
  175. else
  176. if (c == ':')
  177. *t = JS_COLON;
  178. else
  179. if (c == '"') {
  180. *t = JS_STRING;
  181. v = xs_str_new(NULL);
  182. while ((c = *s) != '"' && c != '\0') {
  183. char tmp[5];
  184. int cp, i;
  185. if (c == '\\') {
  186. s++;
  187. c = *s;
  188. switch (c) {
  189. case 'n': c = '\n'; break;
  190. case 'r': c = '\r'; break;
  191. case 't': c = '\t'; break;
  192. case 'u': /* Unicode codepoint as an hex char */
  193. s++;
  194. memcpy(tmp, s, 4);
  195. s += 3;
  196. tmp[4] = '\0';
  197. sscanf(tmp, "%04x", &i);
  198. if (i >= 0xd800 && i <= 0xdfff) {
  199. /* it's a surrogate pair */
  200. cp = (i & 0x3ff) << 10;
  201. /* skip to the next value */
  202. s += 3;
  203. memcpy(tmp, s, 4);
  204. s += 3;
  205. sscanf(tmp, "%04x", &i);
  206. cp |= (i & 0x3ff);
  207. cp += 0x10000;
  208. }
  209. else
  210. cp = i;
  211. v = xs_utf8_enc(v, cp);
  212. c = '\0';
  213. break;
  214. }
  215. }
  216. if (c)
  217. v = xs_append_m(v, &c, 1);
  218. s++;
  219. }
  220. if (c != '\0')
  221. s++;
  222. }
  223. else
  224. if (c == '-' || (c >= '0' && c <= '9') || c == '.') {
  225. xs *vn = NULL;
  226. *t = JS_INTEGER;
  227. vn = xs_str_new(NULL);
  228. vn = xs_append_m(vn, &c, 1);
  229. while (((c = *s) >= '0' && c <= '9') || c == '.') {
  230. if (c == '.')
  231. *t = JS_REAL;
  232. vn = xs_append_m(vn, &c, 1);
  233. s++;
  234. }
  235. /* convert to XSTYPE_NUMBER */
  236. v = xs_number_new(atof(vn));
  237. }
  238. else
  239. if (c == 't' && strncmp(s, "rue", 3) == 0) {
  240. s += 3;
  241. *t = JS_TRUE;
  242. v = xs_val_new(XSTYPE_TRUE);
  243. }
  244. else
  245. if (c == 'f' && strncmp(s, "alse", 4) == 0) {
  246. s += 4;
  247. *t = JS_FALSE;
  248. v = xs_val_new(XSTYPE_FALSE);
  249. }
  250. else
  251. if (c == 'n' && strncmp(s, "ull", 3) == 0) {
  252. s += 3;
  253. *t = JS_NULL;
  254. v = xs_val_new(XSTYPE_NULL);
  255. }
  256. else
  257. *t = JS_ERROR;
  258. *json = s;
  259. return v;
  260. }
  261. d_char *_xs_json_loads_array(const char **json, js_type *t);
  262. d_char *_xs_json_loads_object(const char **json, js_type *t);
  263. d_char *_xs_json_loads_value(const char **json, js_type *t, d_char *v)
  264. /* parses a JSON value */
  265. {
  266. if (*t == JS_OBRACK)
  267. v = _xs_json_loads_array(json, t);
  268. else
  269. if (*t == JS_OCURLY)
  270. v = _xs_json_loads_object(json, t);
  271. if (*t >= JS_VALUE)
  272. *t = JS_VALUE;
  273. else
  274. *t = JS_ERROR;
  275. return v;
  276. }
  277. d_char *_xs_json_loads_array(const char **json, js_type *t)
  278. /* parses a JSON array */
  279. {
  280. const char *s = *json;
  281. xs *v;
  282. d_char *l;
  283. js_type tt;
  284. l = xs_list_new();
  285. *t = JS_INCOMPLETE;
  286. v = _xs_json_loads_lexer(&s, &tt);
  287. if (tt == JS_CBRACK)
  288. *t = JS_ARRAY;
  289. else {
  290. v = _xs_json_loads_value(&s, &tt, v);
  291. if (tt == JS_VALUE) {
  292. l = xs_list_append(l, v);
  293. while (*t == JS_INCOMPLETE) {
  294. free(_xs_json_loads_lexer(&s, &tt));
  295. if (tt == JS_CBRACK)
  296. *t = JS_ARRAY;
  297. else
  298. if (tt == JS_COMMA) {
  299. xs *v2;
  300. v2 = _xs_json_loads_lexer(&s, &tt);
  301. v2 = _xs_json_loads_value(&s, &tt, v2);
  302. if (tt == JS_VALUE)
  303. l = xs_list_append(l, v2);
  304. else
  305. *t = JS_ERROR;
  306. }
  307. else
  308. *t = JS_ERROR;
  309. }
  310. }
  311. else
  312. *t = JS_ERROR;
  313. }
  314. if (*t == JS_ERROR) {
  315. free(l);
  316. l = NULL;
  317. }
  318. *json = s;
  319. return l;
  320. }
  321. d_char *_xs_json_loads_object(const char **json, js_type *t)
  322. /* parses a JSON object */
  323. {
  324. const char *s = *json;
  325. xs *k1;
  326. d_char *d;
  327. js_type tt;
  328. d = xs_dict_new();
  329. *t = JS_INCOMPLETE;
  330. k1 = _xs_json_loads_lexer(&s, &tt);
  331. if (tt == JS_CCURLY)
  332. *t = JS_OBJECT;
  333. else
  334. if (tt == JS_STRING) {
  335. free(_xs_json_loads_lexer(&s, &tt));
  336. if (tt == JS_COLON) {
  337. xs *v1;
  338. v1 = _xs_json_loads_lexer(&s, &tt);
  339. v1 = _xs_json_loads_value(&s, &tt, v1);
  340. if (tt == JS_VALUE) {
  341. d = xs_dict_append(d, k1, v1);
  342. while (*t == JS_INCOMPLETE) {
  343. free(_xs_json_loads_lexer(&s, &tt));
  344. if (tt == JS_CCURLY)
  345. *t = JS_OBJECT;
  346. else
  347. if (tt == JS_COMMA) {
  348. xs *k = _xs_json_loads_lexer(&s, &tt);
  349. if (tt == JS_STRING) {
  350. free(_xs_json_loads_lexer(&s, &tt));
  351. if (tt == JS_COLON) {
  352. xs *v;
  353. v = _xs_json_loads_lexer(&s, &tt);
  354. v = _xs_json_loads_value(&s, &tt, v);
  355. if (tt == JS_VALUE)
  356. d = xs_dict_append(d, k, v);
  357. else
  358. *t = JS_ERROR;
  359. }
  360. else
  361. *t = JS_ERROR;
  362. }
  363. else
  364. *t = JS_ERROR;
  365. }
  366. else
  367. *t = JS_ERROR;
  368. }
  369. }
  370. else
  371. *t = JS_ERROR;
  372. }
  373. else
  374. *t = JS_ERROR;
  375. }
  376. else
  377. *t = JS_ERROR;
  378. if (*t == JS_ERROR) {
  379. free(d);
  380. d = NULL;
  381. }
  382. *json = s;
  383. return d;
  384. }
  385. d_char *xs_json_loads(const char *json)
  386. /* loads a string in JSON format and converts to a multiple data */
  387. {
  388. d_char *v = NULL;
  389. js_type t;
  390. _xs_json_loads_lexer(&json, &t);
  391. if (t == JS_OBRACK)
  392. v = _xs_json_loads_array(&json, &t);
  393. else
  394. if (t == JS_OCURLY)
  395. v = _xs_json_loads_object(&json, &t);
  396. else
  397. t = JS_ERROR;
  398. return v;
  399. }
  400. #endif /* XS_IMPLEMENTATION */
  401. #endif /* _XS_JSON_H */