iodine 0.4.8 → 0.4.10

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of iodine might be problematic. Click here for more details.

Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +26 -0
  4. data/README.md +18 -12
  5. data/SPEC-Websocket-Draft.md +9 -5
  6. data/bin/ws-echo +3 -0
  7. data/examples/config.ru +0 -1
  8. data/examples/echo.ru +3 -1
  9. data/examples/redis.ru +0 -1
  10. data/ext/iodine/base64.c +97 -105
  11. data/ext/iodine/defer.c +16 -1
  12. data/ext/iodine/defer.h +10 -0
  13. data/ext/iodine/evio.c +35 -13
  14. data/ext/iodine/extconf.rb +1 -1
  15. data/ext/iodine/facil.c +12 -1
  16. data/ext/iodine/facil.h +3 -1
  17. data/ext/iodine/fio2resp.c +71 -0
  18. data/ext/iodine/fio2resp.h +50 -0
  19. data/ext/iodine/fio_cli_helper.c +404 -0
  20. data/ext/iodine/fio_cli_helper.h +152 -0
  21. data/ext/iodine/fiobj.h +631 -0
  22. data/ext/iodine/fiobj_alloc.c +81 -0
  23. data/ext/iodine/fiobj_ary.c +290 -0
  24. data/ext/iodine/fiobj_generic.c +260 -0
  25. data/ext/iodine/fiobj_hash.c +447 -0
  26. data/ext/iodine/fiobj_io.c +58 -0
  27. data/ext/iodine/fiobj_json.c +779 -0
  28. data/ext/iodine/fiobj_misc.c +213 -0
  29. data/ext/iodine/fiobj_numbers.c +113 -0
  30. data/ext/iodine/fiobj_primitives.c +98 -0
  31. data/ext/iodine/fiobj_str.c +261 -0
  32. data/ext/iodine/fiobj_sym.c +213 -0
  33. data/ext/iodine/fiobj_tests.c +474 -0
  34. data/ext/iodine/fiobj_types.h +290 -0
  35. data/ext/iodine/http1.c +54 -36
  36. data/ext/iodine/http1_parser.c +143 -35
  37. data/ext/iodine/http1_parser.h +6 -3
  38. data/ext/iodine/http1_response.c +0 -1
  39. data/ext/iodine/http_response.c +1 -1
  40. data/ext/iodine/iodine.c +20 -4
  41. data/ext/iodine/iodine_protocol.c +5 -4
  42. data/ext/iodine/iodine_pubsub.c +1 -1
  43. data/ext/iodine/random.c +5 -5
  44. data/ext/iodine/sha1.c +5 -8
  45. data/ext/iodine/sha2.c +8 -11
  46. data/ext/iodine/sha2.h +3 -3
  47. data/ext/iodine/sock.c +29 -31
  48. data/ext/iodine/websocket_parser.h +428 -0
  49. data/ext/iodine/websockets.c +112 -377
  50. data/ext/iodine/xor-crypt.c +16 -12
  51. data/lib/iodine/version.rb +1 -1
  52. metadata +21 -3
  53. data/ext/iodine/empty.h +0 -26
@@ -0,0 +1,779 @@
1
+ /*
2
+ Copyright: Boaz segev, 2017
3
+ License: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+ #include "fiobj_types.h"
8
+ // #include "fio2resp.h"
9
+ #include <ctype.h>
10
+
11
+ /* *****************************************************************************
12
+ JSON API
13
+ ***************************************************************************** */
14
+ #define JSON_MAX_DEPTH 24
15
+ /**
16
+ * Parses JSON, setting `pobj` to point to the new Object.
17
+ *
18
+ * Returns the number of bytes consumed. On Error, 0 is returned and no data is
19
+ * consumed.
20
+ */
21
+ size_t fiobj_json2obj(fiobj_s **pobj, const void *data, size_t len);
22
+ /* Formats an object into a JSON string. Remember to `fiobj_free`. */
23
+ fiobj_s *fiobj_obj2json(fiobj_s *, uint8_t);
24
+
25
+ /* *****************************************************************************
26
+ JSON UTF-8 safe string formatting
27
+ ***************************************************************************** */
28
+
29
+ static const char hex_chars[] = {'0', '1', '2', '3', '4', '5', '6', '7',
30
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
31
+
32
+ static const uint8_t is_hex[] = {
33
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
34
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
35
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0,
36
+ 0, 0, 0, 0, 0, 11, 12, 13, 14, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0,
37
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 12, 13,
38
+ 14, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
39
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
40
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
41
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
42
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
43
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
44
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
45
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
46
+
47
+ // /* invalid byte length is ignored */
48
+ // static inline int utf8_clen(const uint8_t *str) {
49
+ // if (str[0] <= 127)
50
+ // return 1;
51
+ // if ((str[0] & 224) == 192 && (str[1] & 192) == 128)
52
+ // return 2;
53
+ // if ((str[0] & 240) == 224 && (str[1] & 192) == 128 && (str[2] & 192) ==
54
+ // 128)
55
+ // return 3;
56
+ // if ((str[0] & 248) == 240 && (str[1] & 192) == 128 && (str[2] & 192) == 128
57
+ // &&
58
+ // (str[3] & 192) == 128)
59
+ // return 4;
60
+ // return 0; /* invalid UTF-8 */
61
+ // }
62
+
63
+ /* converts a uint16_t to UTF-8 and returns the number of bytes written */
64
+ static inline int utf8_from_u32(uint8_t *dest, uint32_t u) {
65
+ if (u <= 127) {
66
+ *dest = u;
67
+ return 1;
68
+ } else if (u <= 2047) {
69
+ *(dest++) = 192 | (u >> 6);
70
+ *(dest++) = 128 | (u & 63);
71
+ return 2;
72
+ } else if (u <= 65535) {
73
+ *(dest++) = 224 | (u >> 12);
74
+ *(dest++) = 128 | ((u >> 6) & 63);
75
+ *(dest++) = 128 | (u & 63);
76
+ return 3;
77
+ }
78
+ *(dest++) = 240 | ((u >> 18) & 7);
79
+ *(dest++) = 128 | ((u >> 12) & 63);
80
+ *(dest++) = 128 | ((u >> 6) & 63);
81
+ *(dest++) = 128 | (u & 63);
82
+ return 4;
83
+ }
84
+
85
+ /** Writes a JSON friendly version of the src String, requires the */
86
+ static void write_safe_str(fiobj_s *dest, fiobj_s *str) {
87
+ fio_cstr_s s = fiobj_obj2cstr(str);
88
+ const uint8_t *src = (const uint8_t *)s.data;
89
+ size_t len = s.len;
90
+ uint64_t end = obj2str(dest)->len;
91
+ /* make sure we have some room */
92
+ size_t added = 0;
93
+ if (obj2str(dest)->capa <= end + s.len + 64)
94
+ fiobj_str_capa_assert(dest, (((obj2str(dest)->capa >> 12) + 1) << 12) - 1);
95
+ while (len) {
96
+ while (len &&
97
+ (src[0] > 32 && src[0] != '"' && src[0] != '\\' && src[0] != '/')) {
98
+ len--;
99
+ obj2str(dest)->str[end++] = *(src++);
100
+ }
101
+ if (!len)
102
+ break;
103
+ switch (src[0]) {
104
+ case '\b':
105
+ obj2str(dest)->str[end++] = '\\';
106
+ obj2str(dest)->str[end++] = 'b';
107
+ added++;
108
+ break; /* from switch */
109
+ case '\f':
110
+ obj2str(dest)->str[end++] = '\\';
111
+ obj2str(dest)->str[end++] = 'f';
112
+ added++;
113
+ break; /* from switch */
114
+ case '\n':
115
+ obj2str(dest)->str[end++] = '\\';
116
+ obj2str(dest)->str[end++] = 'n';
117
+ added++;
118
+ break; /* from switch */
119
+ case '\r':
120
+ obj2str(dest)->str[end++] = '\\';
121
+ obj2str(dest)->str[end++] = 'r';
122
+ added++;
123
+ break; /* from switch */
124
+ case '\t':
125
+ obj2str(dest)->str[end++] = '\\';
126
+ obj2str(dest)->str[end++] = 't';
127
+ added++;
128
+ break; /* from switch */
129
+ case '"':
130
+ case '\\':
131
+ case '/':
132
+ obj2str(dest)->str[end++] = '\\';
133
+ obj2str(dest)->str[end++] = src[0];
134
+ added++;
135
+ break; /* from switch */
136
+ default:
137
+ if (src[0] <= 31) {
138
+ /* MUST escape all control values less than 32 */
139
+ obj2str(dest)->str[end++] = '\\';
140
+ obj2str(dest)->str[end++] = 'u';
141
+ obj2str(dest)->str[end++] = '0';
142
+ obj2str(dest)->str[end++] = '0';
143
+ obj2str(dest)->str[end++] = hex_chars[src[0] >> 4];
144
+ obj2str(dest)->str[end++] = hex_chars[src[0] & 15];
145
+ added += 4;
146
+ } else
147
+ obj2str(dest)->str[end++] = src[0];
148
+ break; /* from switch */
149
+ }
150
+ src++;
151
+ len--;
152
+ if (added >= 48 && obj2str(dest)->capa <= end + len + 64) {
153
+ fiobj_str_capa_assert(dest,
154
+ (((obj2str(dest)->capa >> 12) + 1) << 12) - 1);
155
+ added = 0;
156
+ }
157
+ }
158
+ obj2str(dest)->len = end;
159
+ obj2str(dest)->str[end] = 0;
160
+ }
161
+
162
+ /* *****************************************************************************
163
+ JSON formatting
164
+ ***************************************************************************** */
165
+
166
+ /* this is used to persist data in `fiobj_each2` */
167
+ struct fiobj_str_new_json_data_s {
168
+ fiobj_s *parent; /* stores item types */
169
+ fiobj_s *waiting; /* stores item counts and types */
170
+ fiobj_s *buffer; /* we'll write the JSON here */
171
+ fiobj_s *count; /* used to persist item counts for arrays / hashes */
172
+ uint8_t pretty; /* make it beautiful */
173
+ };
174
+
175
+ static int fiobj_str_new_json_task(fiobj_s *obj, void *d_) {
176
+ struct fiobj_str_new_json_data_s *data = d_;
177
+ if (data->count && fiobj_obj2num(data->count))
178
+ fiobj_num_set(data->count, fiobj_obj2num(data->count) - 1);
179
+ /* headroom */
180
+ fiobj_str_capa_assert(
181
+ data->buffer,
182
+ ((((obj2str(data->buffer)->len + 63) >> 12) + 1) << 12) - 1);
183
+ pretty_re_rooted:
184
+ /* pretty? */
185
+ if (data->pretty) {
186
+ fiobj_str_write(data->buffer, "\n", 1);
187
+ for (size_t i = 0; i < fiobj_ary_count(data->parent); i++) {
188
+ fiobj_str_write(data->buffer, " ", 2);
189
+ }
190
+ }
191
+ re_rooted:
192
+ if (!obj) {
193
+ fiobj_str_write(data->buffer, "null", 4);
194
+ goto review_nesting;
195
+ }
196
+ switch (obj->type) {
197
+ case FIOBJ_T_HASH:
198
+ fiobj_str_write(data->buffer, "{", 1);
199
+ fiobj_ary_push(data->parent, obj);
200
+ fiobj_ary_push(data->waiting, data->count);
201
+ data->count = fiobj_num_new(fiobj_hash_count(obj));
202
+ break;
203
+ case FIOBJ_T_ARRAY:
204
+ fiobj_str_write(data->buffer, "[", 1);
205
+ /* push current state to stacks and update state */
206
+ fiobj_ary_push(data->parent, obj);
207
+ fiobj_ary_push(data->waiting, data->count);
208
+ data->count = fiobj_num_new(fiobj_ary_count(obj));
209
+ break;
210
+ case FIOBJ_T_SYMBOL:
211
+ case FIOBJ_T_STRING: {
212
+ fiobj_str_capa_assert(
213
+ data->buffer,
214
+ ((((obj2str(data->buffer)->len + 63 + obj2str(obj)->len) >> 12) + 1)
215
+ << 12) -
216
+ 1);
217
+ fiobj_str_write(data->buffer, "\"", 1);
218
+ write_safe_str(data->buffer, obj);
219
+ fiobj_str_write(data->buffer, "\"", 1);
220
+ break;
221
+ }
222
+ case FIOBJ_T_COUPLET: {
223
+ fiobj_str_capa_assert(data->buffer,
224
+ ((((obj2str(data->buffer)->len + 31 +
225
+ obj2sym(fiobj_couplet2key(obj))->len) >>
226
+ 12) +
227
+ 1)
228
+ << 12) -
229
+ 1);
230
+ fiobj_str_write(data->buffer, "\"", 1);
231
+ write_safe_str(data->buffer, fiobj_couplet2key(obj));
232
+ fiobj_str_write(data->buffer, "\":", 2);
233
+ obj = fiobj_couplet2obj(obj);
234
+ if (data->pretty &&
235
+ (obj->type == FIOBJ_T_ARRAY || obj->type == FIOBJ_T_HASH))
236
+ goto pretty_re_rooted;
237
+ goto re_rooted;
238
+ break;
239
+ }
240
+ case FIOBJ_T_NUMBER:
241
+ obj2str(data->buffer)->len +=
242
+ fio_ltoa(obj2str(data->buffer)->str + obj2str(data->buffer)->len,
243
+ obj2num(obj)->i, 10);
244
+ break;
245
+ case FIOBJ_T_FLOAT:
246
+ if (isnan(obj2float(obj)->f))
247
+ fiobj_str_write(data->buffer, "\"NaN\"", 5);
248
+ else if (isinf(obj2float(obj)->f)) {
249
+ if (obj2float(obj)->f > 0)
250
+ fiobj_str_write(data->buffer, "\"Infinity\"", 10);
251
+ else
252
+ fiobj_str_write(data->buffer, "\"-Infinity\"", 11);
253
+ } else {
254
+ char *start = obj2str(data->buffer)->str + obj2str(data->buffer)->len;
255
+ fiobj_str_write2(data->buffer, "%g", obj2float(obj)->f);
256
+ uint8_t need_zero = 1;
257
+ while (*start) {
258
+ if (*start == ',') // locale issues?
259
+ *start = '.';
260
+ if (*start == '.' || *start == 'e') {
261
+ need_zero = 0;
262
+ break;
263
+ }
264
+ start++;
265
+ }
266
+ if (need_zero)
267
+ fiobj_str_write(data->buffer, ".0", 2);
268
+ }
269
+ break;
270
+ // case FIOBJ_T_FLOAT:
271
+ // obj2str(data->buffer)->len +=
272
+ // fio_ftoa(obj2str(data->buffer)->str + obj2str(data->buffer)->len,
273
+ // obj2float(obj)->f, 10);
274
+ // break;
275
+ case FIOBJ_T_TRUE:
276
+ fiobj_str_write(data->buffer, "true", 4);
277
+ break;
278
+ case FIOBJ_T_FALSE:
279
+ fiobj_str_write(data->buffer, "false", 5);
280
+ break;
281
+ case FIOBJ_T_IO:
282
+ case FIOBJ_T_NULL:
283
+ fiobj_str_write(data->buffer, "null", 4);
284
+ break;
285
+ }
286
+
287
+ review_nesting:
288
+ /* print clousure to String */
289
+ while (!fiobj_obj2num(data->count)) {
290
+ fiobj_s *tmp = fiobj_ary_pop(data->parent);
291
+ if (!tmp)
292
+ break;
293
+ fiobj_free(data->count);
294
+ data->count = fiobj_ary_pop(data->waiting);
295
+ if (data->pretty) {
296
+ fiobj_str_write(data->buffer, "\n", 1);
297
+ for (size_t i = 0; i < fiobj_ary_count(data->parent); i++) {
298
+ fiobj_str_write(data->buffer, " ", 2);
299
+ }
300
+ }
301
+ if (tmp->type == FIOBJ_T_ARRAY)
302
+ fiobj_str_write(data->buffer, "]", 1);
303
+ else
304
+ fiobj_str_write(data->buffer, "}", 1);
305
+ }
306
+ /* print object divisions to String */
307
+ if (data->count && fiobj_obj2num(data->count) &&
308
+ (!obj || (obj->type != FIOBJ_T_ARRAY && obj->type != FIOBJ_T_HASH)))
309
+ fiobj_str_write(data->buffer, ",", 1);
310
+ return 0;
311
+ }
312
+
313
+ /* Formats an object into a JSON string. Remember to `fiobj_free`. */
314
+ fiobj_s *fiobj_obj2json(fiobj_s *obj, uint8_t pretty) {
315
+ /* Using a whole page size could optimize future allocations (no copy) */
316
+ struct fiobj_str_new_json_data_s data = {
317
+ .parent = fiobj_ary_new(),
318
+ .waiting = fiobj_ary_new(),
319
+ .buffer = fiobj_str_buf(0),
320
+ .count = NULL,
321
+ .pretty = pretty,
322
+ };
323
+ fiobj_each2(obj, fiobj_str_new_json_task, &data);
324
+
325
+ while (fiobj_ary_pop(data.parent))
326
+ ; /* we didn't duplicate the objects, so we must remove them from array */
327
+ fiobj_free(data.parent);
328
+ fiobj_free(data.waiting);
329
+ fiobj_str_minimize(data.buffer);
330
+ return data.buffer;
331
+ }
332
+
333
+ /* *****************************************************************************
334
+ JSON parsing
335
+ *****************************************************************************
336
+ */
337
+
338
+ /*
339
+ Marks as object seperators any of the following:
340
+
341
+ * White Space: [0x09, 0x0A, 0x0D, 0x20]
342
+ * Comma ("," / 0x2C)
343
+ * Colon (":" / 0x3A)
344
+ The rest belong to objects,
345
+ */
346
+ static const uint8_t JSON_SEPERATOR[] = {
347
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
348
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
349
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
350
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
351
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
352
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
353
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
354
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
355
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
356
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
357
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
358
+ };
359
+
360
+ inline static void move_to_end(const uint8_t **pos, const uint8_t *limit) {
361
+ while (*pos < limit && JSON_SEPERATOR[**pos] == 0)
362
+ (*pos)++;
363
+ }
364
+ inline static void move_to_start(const uint8_t **pos, const uint8_t *limit) {
365
+ while (*pos < limit && JSON_SEPERATOR[**pos])
366
+ (*pos)++;
367
+ }
368
+ inline static uint8_t move_to_eol(const uint8_t **pos, const uint8_t *limit) {
369
+ /* single char lookup using library is best when target is far... */
370
+ if (*pos >= limit)
371
+ return 0;
372
+ if (**pos == '\n')
373
+ return 1;
374
+ void *tmp = memchr(*pos, '\n', limit - (*pos));
375
+ if (tmp) {
376
+ *pos = tmp;
377
+ return 1;
378
+ }
379
+ *pos = limit;
380
+ return 0;
381
+ }
382
+
383
+ inline static int move_to_quote(const uint8_t **pos, const uint8_t *limit) {
384
+ if (**pos == '\\')
385
+ return 1;
386
+ if (**pos == '\"')
387
+ return 0;
388
+ uint64_t wanted1 = 0x0101010101010101ULL * '"';
389
+ uint64_t wanted2 = 0x0101010101010101ULL * '\\';
390
+ uint64_t *lpos = (uint64_t *)*pos;
391
+ uint64_t *llimit = ((uint64_t *)limit) - 1;
392
+
393
+ for (; lpos < llimit; ++lpos) {
394
+ const uint64_t eq1 = ~((*lpos) ^ wanted1);
395
+ const uint64_t t0 = (eq1 & 0x7f7f7f7f7f7f7f7fllu) + 0x0101010101010101llu;
396
+ const uint64_t t1 = (eq1 & 0x8080808080808080llu);
397
+ const uint64_t eq2 = ~((*lpos) ^ wanted2);
398
+ const uint64_t t2 = (eq2 & 0x7f7f7f7f7f7f7f7fllu) + 0x0101010101010101llu;
399
+ const uint64_t t3 = (eq2 & 0x8080808080808080llu);
400
+ if ((t0 & t1) || (t2 & t3))
401
+ break;
402
+ }
403
+ *pos = (uint8_t *)lpos;
404
+
405
+ while (*pos < limit) {
406
+ if (**pos == '\"') {
407
+ return 0;
408
+ }
409
+ if (**pos == '\\') {
410
+ return 1;
411
+ }
412
+ (*pos)++;
413
+ }
414
+ return 0;
415
+ }
416
+
417
+ /* *****************************************************************************
418
+ JSON UTF-8 safe string deconstruction
419
+ *****************************************************************************
420
+ */
421
+
422
+ /* Converts a JSON friendly String to a binary String.
423
+ *
424
+ * Also deals with the non-standard oct ("\77") and hex ("\xFF") notations
425
+ */
426
+ static void safestr2local(fiobj_s *str) {
427
+ if (str->type != FIOBJ_T_STRING && str->type != FIOBJ_T_SYMBOL) {
428
+ fprintf(stderr,
429
+ "CRITICAL ERROR: unexpected function call `safestr2local`\n");
430
+ exit(-1);
431
+ }
432
+ fio_cstr_s s = fiobj_obj2cstr(str);
433
+ uint8_t had_changed = 0;
434
+ uint8_t *end = (uint8_t *)s.bytes + s.len;
435
+ uint8_t *reader = (uint8_t *)s.bytes;
436
+ uint8_t *writer = (uint8_t *)s.bytes;
437
+ while (reader < end) {
438
+ while (reader < end && reader[0] != '\\') {
439
+ *(writer++) = *(reader++);
440
+ }
441
+ if (reader[0] != '\\')
442
+ break;
443
+
444
+ had_changed = 1;
445
+ switch (reader[1]) {
446
+ case 'b':
447
+ *(writer++) = '\b';
448
+ reader += 2;
449
+ break; /* from switch */
450
+ case 'f':
451
+ *(writer++) = '\f';
452
+ reader += 2;
453
+ break; /* from switch */
454
+ case 'n':
455
+ *(writer++) = '\n';
456
+ reader += 2;
457
+ break; /* from switch */
458
+ case 'r':
459
+ *(writer++) = '\r';
460
+ reader += 2;
461
+ break; /* from switch */
462
+ case 't':
463
+ *(writer++) = '\t';
464
+ reader += 2;
465
+ break; /* from switch */
466
+ case 'u': { /* test for octal notation */
467
+ if (is_hex[reader[2]] && is_hex[reader[3]] && is_hex[reader[4]] &&
468
+ is_hex[reader[5]]) {
469
+ uint32_t t =
470
+ ((((is_hex[reader[2]] - 1) << 4) | (is_hex[reader[3]] - 1)) << 8) |
471
+ (((is_hex[reader[4]] - 1) << 4) | (is_hex[reader[5]] - 1));
472
+ if (reader[6] == '\\' && reader[7] == 'u' && is_hex[reader[8]] &&
473
+ is_hex[reader[9]] && is_hex[reader[10]] && is_hex[reader[11]]) {
474
+ /* Serrogate Pair */
475
+ t = (t & 0x03FF) << 10;
476
+ t |= ((((((is_hex[reader[8]] - 1) << 4) | (is_hex[reader[9]] - 1))
477
+ << 8) |
478
+ (((is_hex[reader[10]] - 1) << 4) | (is_hex[reader[11]] - 1))) &
479
+ 0x03FF);
480
+ t += 0x10000;
481
+ /* Wikipedia way: */
482
+ // t = 0x10000 + ((t - 0xD800) * 0x400) +
483
+ // ((((((is_hex[reader[8]] - 1) << 4) | (is_hex[reader[9]] - 1))
484
+ // << 8) |
485
+ // (((is_hex[reader[10]] - 1) << 4) | (is_hex[reader[11]] -
486
+ // 1)))
487
+ // -
488
+ // 0xDC00);
489
+ reader += 6;
490
+ }
491
+ writer += utf8_from_u32(writer, t);
492
+ reader += 6;
493
+ break; /* from switch */
494
+ } else
495
+ goto invalid_escape;
496
+ }
497
+ case 'x': { /* test for hex notation */
498
+ if (is_hex[reader[2]] && is_hex[reader[3]]) {
499
+ *(writer++) = ((is_hex[reader[2]] - 1) << 4) | (is_hex[reader[3]] - 1);
500
+ reader += 4;
501
+ break; /* from switch */
502
+ } else
503
+ goto invalid_escape;
504
+ }
505
+ case '0':
506
+ case '1':
507
+ case '2':
508
+ case '3':
509
+ case '4':
510
+ case '5':
511
+ case '6':
512
+ case '7': { /* test for octal notation */
513
+ if (reader[1] >= '0' && reader[1] <= '7' && reader[2] >= '0' &&
514
+ reader[2] <= '7') {
515
+ *(writer++) = ((reader[1] - '0') << 3) | (reader[2] - '0');
516
+ reader += 3;
517
+ break; /* from switch */
518
+ } else
519
+ goto invalid_escape;
520
+ }
521
+ case '"':
522
+ case '\\':
523
+ case '/':
524
+ /* fallthrough */
525
+ default:
526
+ invalid_escape:
527
+ *(writer++) = reader[1];
528
+ reader += 2;
529
+ }
530
+ }
531
+ if (str->type == FIOBJ_T_STRING) {
532
+ obj2str(str)->len = (uintptr_t)writer - (uintptr_t)obj2str(str)->str;
533
+ obj2str(str)->str[obj2str(str)->len] = 0;
534
+ } else {
535
+ obj2sym(str)->len = (uintptr_t)writer - (uintptr_t)obj2sym(str)->str;
536
+ obj2sym(str)->str[obj2sym(str)->len] = 0;
537
+ if (had_changed)
538
+ obj2sym(str)->hash = fiobj_sym_hash(obj2sym(str)->str, obj2sym(str)->len);
539
+ }
540
+ }
541
+
542
+ /* *****************************************************************************
543
+ JSON => Obj
544
+ *****************************************************************************
545
+ */
546
+
547
+ /**
548
+ * Parses JSON, setting `pobj` to point to the new Object.
549
+ *
550
+ * Returns the number of bytes consumed. On Error, 0 is returned and no data
551
+ * is consumed.
552
+ */
553
+ size_t fiobj_json2obj(fiobj_s **pobj, const void *data, size_t len) {
554
+ if (!data) {
555
+ *pobj = NULL;
556
+ return 0;
557
+ }
558
+ fiobj_s *nesting = fiobj_ary_new2(JSON_MAX_DEPTH + 2);
559
+ const uint8_t *start;
560
+ fiobj_s *obj;
561
+ const uint8_t *end = (uint8_t *)data;
562
+ const uint8_t *stop = end + len;
563
+ uint8_t depth = 0;
564
+ while (1) {
565
+ /* skip any white space / seperators */
566
+ move_to_start(&end, stop);
567
+ /* set objcet data end point to the starting endpoint */
568
+ obj = NULL;
569
+ start = end;
570
+ if (end >= stop) {
571
+ goto finish;
572
+ }
573
+
574
+ /* test object type. tests are ordered by precedence, if one fails, the
575
+ * other is performed. */
576
+ if (end[0] == '{') {
577
+ /* start an object (hash) */
578
+ fiobj_ary_push(nesting, fiobj_hash_new());
579
+ end++;
580
+ depth++;
581
+ if (depth >= JSON_MAX_DEPTH) {
582
+ goto error;
583
+ }
584
+ continue;
585
+ }
586
+ if (end[0] == '[') {
587
+ /* start an array */
588
+ fiobj_ary_push(nesting, fiobj_ary_new2(4));
589
+ end++;
590
+ depth++;
591
+ if (depth >= JSON_MAX_DEPTH) {
592
+ goto error;
593
+ }
594
+ continue;
595
+ }
596
+ if (end[0] == '}') {
597
+ /* end an object (hash) */
598
+ end++;
599
+ depth--;
600
+ obj = fiobj_ary_pop(nesting);
601
+ if (obj->type != FIOBJ_T_HASH) {
602
+ goto error;
603
+ }
604
+ goto has_obj;
605
+ }
606
+ if (end[0] == ']') {
607
+ /* end an array */
608
+ end++;
609
+ depth--;
610
+ obj = fiobj_ary_pop(nesting);
611
+ if (obj->type != FIOBJ_T_ARRAY) {
612
+ goto error;
613
+ }
614
+ goto has_obj;
615
+ }
616
+ if (end + 3 < stop && end[0] == 't' && end[1] == 'r' && end[2] == 'u' &&
617
+ end[3] == 'e') {
618
+ /* true */
619
+ end += 4;
620
+ obj = fiobj_true();
621
+ goto has_obj;
622
+ }
623
+ if (end + 4 < stop && end[0] == 'f' && end[1] == 'a' && end[2] == 'l' &&
624
+ end[3] == 's' && end[4] == 'e') {
625
+ /* false */
626
+ end += 5;
627
+ obj = fiobj_false();
628
+ goto has_obj;
629
+ }
630
+ if (end + 3 < stop && end[0] == 'n' && end[1] == 'u' && end[2] == 'l' &&
631
+ end[3] == 'l') {
632
+ /* null */
633
+ end += 4;
634
+ obj = fiobj_null();
635
+ goto has_obj;
636
+ }
637
+ if (end[0] == '/') {
638
+ /* could be a Javascript comment */
639
+ if (end[1] == '/') {
640
+ end += 2;
641
+ move_to_eol(&end, stop);
642
+ continue;
643
+ }
644
+ if (end[1] == '*') {
645
+ end += 2;
646
+ while (end < stop && !(end[0] == '*' && end[1] == '/'))
647
+ end++;
648
+ if (end < stop && end[0] == '*')
649
+ end += 2;
650
+ continue;
651
+ }
652
+ }
653
+ if (end[0] == '#') {
654
+ /* could be a Ruby style comment */
655
+ move_to_eol(&end, stop);
656
+ continue;
657
+ }
658
+ if (start[0] == '"') {
659
+ /* object is a string (require qoutes) */
660
+ start++;
661
+ end++;
662
+ uint8_t dirty = 0;
663
+ while (move_to_quote(&end, stop)) {
664
+ end += 2;
665
+ dirty = 1;
666
+ }
667
+ if (end >= stop) {
668
+ goto error;
669
+ }
670
+ if (fiobj_ary_entry(nesting, -1) &&
671
+ fiobj_ary_entry(nesting, -1)->type == FIOBJ_T_HASH) {
672
+ obj = fiobj_sym_new((char *)start, end - start);
673
+ } else {
674
+ obj = fiobj_str_new((char *)start, end - start);
675
+ }
676
+ if (dirty)
677
+ safestr2local(obj);
678
+ end++;
679
+
680
+ goto has_obj;
681
+ }
682
+ if (end[0] == '-' || (end[0] >= '0' && end[0] <= '9')) {
683
+ /* test for a number OR float */
684
+ int64_t num = fio_atol((char **)&end);
685
+ if (end == start || *end == '.' || *end == 'e' || *end == 'E') {
686
+ end = start;
687
+ double fnum = fio_atof((char **)&end);
688
+ if (end == start)
689
+ goto error;
690
+ obj = fiobj_float_new(fnum);
691
+ goto has_obj;
692
+ }
693
+ obj = fiobj_num_new(num);
694
+ goto has_obj;
695
+ // uint8_t decimal = 0;
696
+ // while (end < stop && JSON_SEPERATOR[*end] == 0 && *end != ']' &&
697
+ // *end != '}') {
698
+ // if (*end == '.' || *end == 'e' || *end == 'E')
699
+ // decimal = 1;
700
+ // end++;
701
+ // }
702
+ // /* test against forbidden leading zeros... but allow hex and binary
703
+ // */ if (end - start > 1 && start[0] == '0' &&
704
+ // !(start[1] == '.' || start[1] == 'x' || start[1] == 'b')) {
705
+ // goto error;
706
+ // }
707
+ // /* it's a number */
708
+ // if (decimal) {
709
+ // obj = fiobj_float_new(fio_atof((char *)start));
710
+ // } else {
711
+ // obj = fiobj_num_new(fio_atol((char *)start));
712
+ // }
713
+ // goto has_obj;
714
+ }
715
+ if (end[0] == 'N' && end[1] == 'a' && end[2] == 'N') {
716
+ obj = fiobj_float_new(nan(""));
717
+ goto has_obj;
718
+ }
719
+ if (end[0] == '-' && end[1] == 'I' && end[2] == 'n' && end[3] == 'f') {
720
+ obj = fiobj_float_new(nan(""));
721
+ obj2float(obj)->f = copysign(obj2float(obj)->f, (double)-1);
722
+ goto has_obj;
723
+ }
724
+ if (end[0] == 'I' && end[1] == 'n' && end[2] == 'f') {
725
+ move_to_end(&end, stop);
726
+ obj = fiobj_float_new(INFINITY);
727
+ goto has_obj;
728
+ }
729
+ if (end[0] == '-' && end[1] == 'I' && end[2] == 'n' && end[3] == 'f') {
730
+ move_to_end(&end, stop);
731
+ obj = fiobj_float_new(INFINITY);
732
+ obj2float(obj)->f = copysign(obj2float(obj)->f, (double)-1);
733
+ goto has_obj;
734
+ }
735
+ goto error;
736
+
737
+ has_obj:
738
+
739
+ if (fiobj_ary_count(nesting) == 0)
740
+ goto finish_with_obj;
741
+ if (fiobj_ary_entry(nesting, -1)->type == FIOBJ_T_ARRAY) {
742
+ fiobj_ary_push(fiobj_ary_entry(nesting, -1), obj);
743
+ continue;
744
+ }
745
+ if (fiobj_ary_entry(nesting, -1)->type == FIOBJ_T_HASH) {
746
+ fio_cstr_s s = fiobj_obj2cstr(obj);
747
+ fiobj_ary_push(nesting, fiobj_sym_new(s.buffer, s.len));
748
+ fiobj_free(obj);
749
+ continue;
750
+ }
751
+ fiobj_s *sym = fiobj_ary_pop(nesting);
752
+ if (fiobj_ary_entry(nesting, -1)->type != FIOBJ_T_HASH)
753
+ goto error;
754
+ fiobj_hash_set(fiobj_ary_entry(nesting, -1), sym, obj);
755
+ fiobj_free(sym);
756
+ continue;
757
+ }
758
+
759
+ finish:
760
+ if (!obj)
761
+ obj = fiobj_ary_pop(nesting);
762
+ finish_with_obj:
763
+ if (obj && fiobj_ary_count(nesting) == 0) {
764
+ *pobj = obj;
765
+ fiobj_free(nesting);
766
+ return end - (uint8_t *)data;
767
+ }
768
+ error:
769
+ if (obj)
770
+ fiobj_free(obj);
771
+ while ((obj = fiobj_ary_pop(nesting)))
772
+ fiobj_free(obj);
773
+ fiobj_free(nesting);
774
+ *pobj = NULL;
775
+ // fprintf(stderr, "ERROR starting at %.*s, ending at %.*s, with %s\n", 3,
776
+ // start,
777
+ // 3, end, start);
778
+ return 0;
779
+ }