iodine 0.7.59 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. checksums.yaml +4 -4
  2. data/.clang-format +15 -0
  3. data/.rspec +1 -0
  4. data/.yardopts +1 -3
  5. data/CHANGELOG.md +132 -12
  6. data/Gemfile +5 -7
  7. data/LICENSE.txt +1 -1
  8. data/README.md +147 -671
  9. data/Rakefile +9 -16
  10. data/examples/chatroom.nru +9 -0
  11. data/examples/client +74 -0
  12. data/examples/config-hello.nru +9 -0
  13. data/examples/config-hello.ru +11 -0
  14. data/examples/config-hijack.ru +13 -0
  15. data/examples/config-shootout.ru +43 -0
  16. data/examples/config.nru +120 -0
  17. data/examples/config.rb +37 -0
  18. data/examples/config.ru +42 -53
  19. data/examples/fake +24 -0
  20. data/examples/hello_neorack +137 -0
  21. data/examples/routes.nru +29 -0
  22. data/examples/routes.ru +42 -0
  23. data/exe/iodine +218 -258
  24. data/ext/iodine/extconf.rb +27 -100
  25. data/ext/iodine/fio-stl.h +128336 -0
  26. data/ext/iodine/fio-stl.md +26396 -0
  27. data/ext/iodine/iodine.c +213 -1393
  28. data/ext/iodine/iodine.h +250 -55
  29. data/ext/iodine/iodine_arg_helper.h +329 -0
  30. data/ext/iodine/iodine_caller.h +234 -25
  31. data/ext/iodine/iodine_cli.h +434 -0
  32. data/ext/iodine/iodine_compression.h +312 -0
  33. data/ext/iodine/iodine_connection.h +3484 -34
  34. data/ext/iodine/iodine_core.h +363 -0
  35. data/ext/iodine/iodine_crypto.h +1441 -0
  36. data/ext/iodine/iodine_defer.h +225 -4
  37. data/ext/iodine/iodine_json.h +738 -4
  38. data/ext/iodine/iodine_listener.h +263 -0
  39. data/ext/iodine/iodine_log.h +297 -0
  40. data/ext/iodine/iodine_minimap.h +543 -0
  41. data/ext/iodine/iodine_mustache.h +461 -4
  42. data/ext/iodine/iodine_pubsub_eng.h +729 -0
  43. data/ext/iodine/iodine_pubsub_history.h +547 -0
  44. data/ext/iodine/iodine_pubsub_msg.h +228 -0
  45. data/ext/iodine/iodine_redis.h +322 -0
  46. data/ext/iodine/iodine_store.h +418 -16
  47. data/ext/iodine/iodine_threads.h +279 -0
  48. data/ext/iodine/iodine_tls.h +515 -6
  49. data/ext/iodine/iodine_utils.h +923 -0
  50. data/iodine.gemspec +37 -42
  51. data/lib/iodine/benchmark.rb +1690 -0
  52. data/lib/iodine/documentation.rb +3085 -0
  53. data/lib/iodine/version.rb +17 -1
  54. data/lib/iodine.rb +54 -255
  55. data/logo.svg +36 -0
  56. data/mustache.md +55 -0
  57. data/sig/iodine.rbs +4 -0
  58. metadata +58 -184
  59. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
  60. data/.github/workflows/ruby.yml +0 -42
  61. data/.gitignore +0 -20
  62. data/LIMITS.md +0 -41
  63. data/SPEC-PubSub-Draft.md +0 -159
  64. data/SPEC-WebSocket-Draft.md +0 -239
  65. data/bin/console +0 -22
  66. data/bin/info.md +0 -353
  67. data/bin/mustache_bench.rb +0 -100
  68. data/bin/poc/Gemfile.lock +0 -23
  69. data/bin/poc/README.md +0 -37
  70. data/bin/poc/config.ru +0 -66
  71. data/bin/poc/gemfile +0 -1
  72. data/bin/poc/www/index.html +0 -57
  73. data/examples/async_task.ru +0 -92
  74. data/examples/bates/README.md +0 -3
  75. data/examples/bates/config.ru +0 -342
  76. data/examples/bates/david+bold.pdf +0 -0
  77. data/examples/bates/public/drop-pdf.png +0 -0
  78. data/examples/bates/public/index.html +0 -600
  79. data/examples/echo.ru +0 -59
  80. data/examples/etag.ru +0 -16
  81. data/examples/hello.ru +0 -29
  82. data/examples/pubsub_engine.ru +0 -81
  83. data/examples/rack3.ru +0 -12
  84. data/examples/redis.ru +0 -70
  85. data/examples/shootout.ru +0 -73
  86. data/examples/sub-protocols.ru +0 -90
  87. data/examples/tcp_client.rb +0 -66
  88. data/examples/x-sendfile.ru +0 -14
  89. data/ext/iodine/fio.c +0 -11995
  90. data/ext/iodine/fio.h +0 -6371
  91. data/ext/iodine/fio_cli.c +0 -431
  92. data/ext/iodine/fio_cli.h +0 -189
  93. data/ext/iodine/fio_json_parser.h +0 -688
  94. data/ext/iodine/fio_siphash.c +0 -157
  95. data/ext/iodine/fio_siphash.h +0 -37
  96. data/ext/iodine/fio_tls.h +0 -129
  97. data/ext/iodine/fio_tls_missing.c +0 -649
  98. data/ext/iodine/fio_tls_openssl.c +0 -1106
  99. data/ext/iodine/fio_tmpfile.h +0 -50
  100. data/ext/iodine/fiobj.h +0 -44
  101. data/ext/iodine/fiobj4fio.h +0 -21
  102. data/ext/iodine/fiobj_ary.c +0 -333
  103. data/ext/iodine/fiobj_ary.h +0 -139
  104. data/ext/iodine/fiobj_data.c +0 -1187
  105. data/ext/iodine/fiobj_data.h +0 -167
  106. data/ext/iodine/fiobj_hash.c +0 -409
  107. data/ext/iodine/fiobj_hash.h +0 -176
  108. data/ext/iodine/fiobj_json.c +0 -622
  109. data/ext/iodine/fiobj_json.h +0 -68
  110. data/ext/iodine/fiobj_mem.h +0 -71
  111. data/ext/iodine/fiobj_mustache.c +0 -317
  112. data/ext/iodine/fiobj_mustache.h +0 -62
  113. data/ext/iodine/fiobj_numbers.c +0 -344
  114. data/ext/iodine/fiobj_numbers.h +0 -127
  115. data/ext/iodine/fiobj_str.c +0 -433
  116. data/ext/iodine/fiobj_str.h +0 -172
  117. data/ext/iodine/fiobject.c +0 -620
  118. data/ext/iodine/fiobject.h +0 -655
  119. data/ext/iodine/hpack.h +0 -1923
  120. data/ext/iodine/http.c +0 -2743
  121. data/ext/iodine/http.h +0 -1002
  122. data/ext/iodine/http1.c +0 -910
  123. data/ext/iodine/http1.h +0 -29
  124. data/ext/iodine/http1_parser.h +0 -878
  125. data/ext/iodine/http_internal.c +0 -1279
  126. data/ext/iodine/http_internal.h +0 -237
  127. data/ext/iodine/http_mime_parser.h +0 -350
  128. data/ext/iodine/iodine_caller.c +0 -230
  129. data/ext/iodine/iodine_connection.c +0 -941
  130. data/ext/iodine/iodine_defer.c +0 -420
  131. data/ext/iodine/iodine_fiobj2rb.h +0 -120
  132. data/ext/iodine/iodine_helpers.c +0 -282
  133. data/ext/iodine/iodine_helpers.h +0 -12
  134. data/ext/iodine/iodine_http.c +0 -1181
  135. data/ext/iodine/iodine_http.h +0 -23
  136. data/ext/iodine/iodine_json.c +0 -302
  137. data/ext/iodine/iodine_mustache.c +0 -567
  138. data/ext/iodine/iodine_pubsub.c +0 -580
  139. data/ext/iodine/iodine_pubsub.h +0 -26
  140. data/ext/iodine/iodine_rack_io.c +0 -273
  141. data/ext/iodine/iodine_rack_io.h +0 -20
  142. data/ext/iodine/iodine_store.c +0 -146
  143. data/ext/iodine/iodine_tcp.c +0 -346
  144. data/ext/iodine/iodine_tcp.h +0 -13
  145. data/ext/iodine/iodine_tls.c +0 -261
  146. data/ext/iodine/mustache_parser.h +0 -1546
  147. data/ext/iodine/redis_engine.c +0 -957
  148. data/ext/iodine/redis_engine.h +0 -79
  149. data/ext/iodine/resp_parser.h +0 -317
  150. data/ext/iodine/websocket_parser.h +0 -506
  151. data/ext/iodine/websockets.c +0 -743
  152. data/ext/iodine/websockets.h +0 -185
  153. data/lib/iodine/connection.rb +0 -61
  154. data/lib/iodine/json.rb +0 -42
  155. data/lib/iodine/mustache.rb +0 -113
  156. data/lib/iodine/pubsub.rb +0 -55
  157. data/lib/iodine/rack_utils.rb +0 -43
  158. data/lib/iodine/tls.rb +0 -16
  159. data/lib/rack/handler/iodine.rb +0 -33
  160. data/logo.png +0 -0
@@ -1,622 +0,0 @@
1
- /*
2
- Copyright: Boaz Segev, 2017-2019
3
- License: MIT
4
- */
5
- #include <fiobj_json.h>
6
- #define FIO_ARY_NAME fio_json_stack
7
- #define FIO_ARY_TYPE FIOBJ
8
- #include <fio.h>
9
-
10
- #include <fio_json_parser.h>
11
-
12
- #include <assert.h>
13
- #include <ctype.h>
14
- #include <math.h>
15
- #include <stdlib.h>
16
- #include <string.h>
17
-
18
- /* *****************************************************************************
19
- JSON API
20
- ***************************************************************************** */
21
-
22
- /**
23
- * Parses JSON, setting `pobj` to point to the new Object.
24
- *
25
- * Returns the number of bytes consumed. On Error, 0 is returned and no data is
26
- * consumed.
27
- */
28
- size_t fiobj_json2obj(FIOBJ *pobj, const void *data, size_t len);
29
- /* Formats an object into a JSON string. Remember to `fiobj_free`. */
30
- FIOBJ fiobj_obj2json(FIOBJ, uint8_t);
31
-
32
- /* *****************************************************************************
33
- FIOBJ Parser
34
- ***************************************************************************** */
35
-
36
- typedef struct {
37
- json_parser_s p;
38
- FIOBJ key;
39
- FIOBJ top;
40
- FIOBJ target;
41
- fio_json_stack_s stack;
42
- uint8_t is_hash;
43
- } fiobj_json_parser_s;
44
-
45
- /* *****************************************************************************
46
- FIOBJ Callacks
47
- ***************************************************************************** */
48
-
49
- static inline void fiobj_json_add2parser(fiobj_json_parser_s *p, FIOBJ o) {
50
- if (p->top) {
51
- if (p->is_hash) {
52
- if (p->key) {
53
- fiobj_hash_set(p->top, p->key, o);
54
- fiobj_free(p->key);
55
- p->key = FIOBJ_INVALID;
56
- } else {
57
- p->key = o;
58
- }
59
- } else {
60
- fiobj_ary_push(p->top, o);
61
- }
62
- } else {
63
- p->top = o;
64
- }
65
- }
66
-
67
- /** a NULL object was detected */
68
- static void fio_json_on_null(json_parser_s *p) {
69
- fiobj_json_add2parser((fiobj_json_parser_s *)p, fiobj_null());
70
- }
71
- /** a TRUE object was detected */
72
- static void fio_json_on_true(json_parser_s *p) {
73
- fiobj_json_add2parser((fiobj_json_parser_s *)p, fiobj_true());
74
- }
75
- /** a FALSE object was detected */
76
- static void fio_json_on_false(json_parser_s *p) {
77
- fiobj_json_add2parser((fiobj_json_parser_s *)p, fiobj_false());
78
- }
79
- /** a Numberl was detected (long long). */
80
- static void fio_json_on_number(json_parser_s *p, long long i) {
81
- fiobj_json_add2parser((fiobj_json_parser_s *)p, fiobj_num_new(i));
82
- }
83
- /** a Float was detected (double). */
84
- static void fio_json_on_float(json_parser_s *p, double f) {
85
- fiobj_json_add2parser((fiobj_json_parser_s *)p, fiobj_float_new(f));
86
- }
87
- /** a String was detected (int / float). update `pos` to point at ending */
88
- static void fio_json_on_string(json_parser_s *p, void *start, size_t length) {
89
- FIOBJ str = fiobj_str_buf(length);
90
- fiobj_str_resize(
91
- str, fio_json_unescape_str(fiobj_obj2cstr(str).data, start, length));
92
- fiobj_json_add2parser((fiobj_json_parser_s *)p, str);
93
- }
94
- /** a dictionary object was detected */
95
- static int fio_json_on_start_object(json_parser_s *p) {
96
- fiobj_json_parser_s *pr = (fiobj_json_parser_s *)p;
97
- if (pr->target) {
98
- /* push NULL, don't free the objects */
99
- fio_json_stack_push(&pr->stack, pr->top);
100
- pr->top = pr->target;
101
- pr->target = FIOBJ_INVALID;
102
- } else {
103
- FIOBJ hash = fiobj_hash_new();
104
- fiobj_json_add2parser(pr, hash);
105
- fio_json_stack_push(&pr->stack, pr->top);
106
- pr->top = hash;
107
- }
108
- pr->is_hash = 1;
109
- return 0;
110
- }
111
- /** a dictionary object closure detected */
112
- static void fio_json_on_end_object(json_parser_s *p) {
113
- fiobj_json_parser_s *pr = (fiobj_json_parser_s *)p;
114
- if (pr->key) {
115
- FIO_LOG_WARNING("(JSON parsing) malformed JSON, "
116
- "ignoring dangling Hash key.");
117
- fiobj_free(pr->key);
118
- pr->key = FIOBJ_INVALID;
119
- }
120
- pr->top = FIOBJ_INVALID;
121
- fio_json_stack_pop(&pr->stack, &pr->top);
122
- pr->is_hash = FIOBJ_TYPE_IS(pr->top, FIOBJ_T_HASH);
123
- }
124
- /** an array object was detected */
125
- static int fio_json_on_start_array(json_parser_s *p) {
126
- fiobj_json_parser_s *pr = (fiobj_json_parser_s *)p;
127
- if (pr->target)
128
- return -1;
129
- FIOBJ ary = fiobj_ary_new();
130
- fiobj_json_add2parser(pr, ary);
131
- fio_json_stack_push(&pr->stack, pr->top);
132
- pr->top = ary;
133
- pr->is_hash = 0;
134
- return 0;
135
- }
136
- /** an array closure was detected */
137
- static void fio_json_on_end_array(json_parser_s *p) {
138
- fiobj_json_parser_s *pr = (fiobj_json_parser_s *)p;
139
- pr->top = FIOBJ_INVALID;
140
- fio_json_stack_pop(&pr->stack, &pr->top);
141
- pr->is_hash = FIOBJ_TYPE_IS(pr->top, FIOBJ_T_HASH);
142
- }
143
- /** the JSON parsing is complete */
144
- static void fio_json_on_json(json_parser_s *p) {
145
- // fiobj_json_parser_s *pr = (fiobj_json_parser_s *)p;
146
- // FIO_ARY_FOR(&pr->stack, pos) { fiobj_free((FIOBJ)pos.obj); }
147
- // fio_json_stack_free(&pr->stack);
148
- (void)p; /* nothing special... right? */
149
- }
150
- /** the JSON parsing is complete */
151
- static void fio_json_on_error(json_parser_s *p) {
152
- fiobj_json_parser_s *pr = (fiobj_json_parser_s *)p;
153
- #if DEBUG
154
- FIO_LOG_DEBUG("JSON on error called.");
155
- #endif
156
- fiobj_free((FIOBJ)fio_json_stack_get(&pr->stack, 0));
157
- fiobj_free(pr->key);
158
- fio_json_stack_free(&pr->stack);
159
- *pr = (fiobj_json_parser_s){.top = FIOBJ_INVALID};
160
- }
161
-
162
- /* *****************************************************************************
163
- JSON formatting
164
- ***************************************************************************** */
165
-
166
- /** Writes a JSON friendly version of the src String */
167
- static void write_safe_str(FIOBJ dest, const FIOBJ str) {
168
- fio_str_info_s s = fiobj_obj2cstr(str);
169
- fio_str_info_s t = fiobj_obj2cstr(dest);
170
- t.data[t.len] = '"';
171
- t.len++;
172
- fiobj_str_resize(dest, t.len);
173
- t = fiobj_obj2cstr(dest);
174
- const uint8_t *restrict src = (const uint8_t *)s.data;
175
- size_t len = s.len;
176
- uint64_t end = t.len;
177
- /* make sure we have some room */
178
- size_t added = 0;
179
- size_t capa = fiobj_str_capa(dest);
180
- if (capa <= end + s.len + 64) {
181
- if (0) {
182
- capa = (((capa >> 12) + 1) << 12) - 1;
183
- capa = fiobj_str_capa_assert(dest, capa);
184
- } else {
185
- capa = fiobj_str_capa_assert(dest, (end + s.len + 64));
186
- }
187
- fio_str_info_s tmp = fiobj_obj2cstr(dest);
188
- t = tmp;
189
- }
190
- while (len) {
191
- char *restrict writer = (char *)t.data;
192
- while (len && src[0] > 32 && src[0] != '"' && src[0] != '\\') {
193
- len--;
194
- writer[end++] = *(src++);
195
- }
196
- if (!len)
197
- break;
198
- switch (src[0]) {
199
- case '\b':
200
- writer[end++] = '\\';
201
- writer[end++] = 'b';
202
- added++;
203
- break; /* from switch */
204
- case '\f':
205
- writer[end++] = '\\';
206
- writer[end++] = 'f';
207
- added++;
208
- break; /* from switch */
209
- case '\n':
210
- writer[end++] = '\\';
211
- writer[end++] = 'n';
212
- added++;
213
- break; /* from switch */
214
- case '\r':
215
- writer[end++] = '\\';
216
- writer[end++] = 'r';
217
- added++;
218
- break; /* from switch */
219
- case '\t':
220
- writer[end++] = '\\';
221
- writer[end++] = 't';
222
- added++;
223
- break; /* from switch */
224
- case '"':
225
- case '\\':
226
- case '/':
227
- writer[end++] = '\\';
228
- writer[end++] = src[0];
229
- added++;
230
- break; /* from switch */
231
- default:
232
- if (src[0] <= 31) {
233
- /* MUST escape all control values less than 32 */
234
- writer[end++] = '\\';
235
- writer[end++] = 'u';
236
- writer[end++] = '0';
237
- writer[end++] = '0';
238
- writer[end++] = hex_chars[src[0] >> 4];
239
- writer[end++] = hex_chars[src[0] & 15];
240
- added += 4;
241
- } else
242
- writer[end++] = src[0];
243
- break; /* from switch */
244
- }
245
- src++;
246
- len--;
247
- if (added >= 48 && capa <= end + len + 64) {
248
- writer[end] = 0;
249
- fiobj_str_resize(dest, end);
250
- fiobj_str_capa_assert(dest, (end + len + 64));
251
- t = fiobj_obj2cstr(dest);
252
- writer = (char *)t.data;
253
- capa = t.capa;
254
- added = 0;
255
- }
256
- }
257
- t.data[end++] = '"';
258
- fiobj_str_resize(dest, end);
259
- }
260
-
261
- typedef struct {
262
- FIOBJ dest;
263
- FIOBJ parent;
264
- fio_json_stack_s *stack;
265
- uintptr_t count;
266
- uint8_t pretty;
267
- } obj2json_data_s;
268
-
269
- static int fiobj_obj2json_task(FIOBJ o, void *data_) {
270
- obj2json_data_s *data = data_;
271
- uint8_t add_seperator = 1;
272
- if (fiobj_hash_key_in_loop()) {
273
- write_safe_str(data->dest, fiobj_hash_key_in_loop());
274
- fiobj_str_write(data->dest, ":", 1);
275
- }
276
- switch (FIOBJ_TYPE(o)) {
277
- case FIOBJ_T_NUMBER:
278
- case FIOBJ_T_NULL:
279
- case FIOBJ_T_TRUE:
280
- case FIOBJ_T_FALSE:
281
- case FIOBJ_T_FLOAT:
282
- fiobj_str_join(data->dest, o);
283
- --data->count;
284
- break;
285
-
286
- case FIOBJ_T_DATA:
287
- case FIOBJ_T_UNKNOWN:
288
- case FIOBJ_T_STRING:
289
- write_safe_str(data->dest, o);
290
- --data->count;
291
- break;
292
-
293
- case FIOBJ_T_ARRAY:
294
- --data->count;
295
- fio_json_stack_push(data->stack, data->parent);
296
- fio_json_stack_push(data->stack, (FIOBJ)data->count);
297
- data->parent = o;
298
- data->count = fiobj_ary_count(o);
299
- fiobj_str_write(data->dest, "[", 1);
300
- add_seperator = 0;
301
- break;
302
-
303
- case FIOBJ_T_HASH:
304
- --data->count;
305
- fio_json_stack_push(data->stack, data->parent);
306
- fio_json_stack_push(data->stack, (FIOBJ)data->count);
307
- data->parent = o;
308
- data->count = fiobj_hash_count(o);
309
- fiobj_str_write(data->dest, "{", 1);
310
- add_seperator = 0;
311
- break;
312
- }
313
- if (data->pretty) {
314
- fiobj_str_capa_assert(data->dest,
315
- fiobj_obj2cstr(data->dest).len +
316
- (fio_json_stack_count(data->stack) * 5));
317
- while (!data->count && data->parent) {
318
- if (FIOBJ_TYPE_IS(data->parent, FIOBJ_T_HASH)) {
319
- fiobj_str_write(data->dest, "}", 1);
320
- } else {
321
- fiobj_str_write(data->dest, "]", 1);
322
- }
323
- add_seperator = 1;
324
- data->count = 0;
325
- fio_json_stack_pop(data->stack, &data->count);
326
- data->parent = FIOBJ_INVALID;
327
- fio_json_stack_pop(data->stack, &data->parent);
328
- }
329
-
330
- if (add_seperator && data->parent) {
331
- fiobj_str_write(data->dest, ",\n", 2);
332
- uintptr_t indent = fio_json_stack_count(data->stack) - 1;
333
- fiobj_str_capa_assert(data->dest,
334
- fiobj_obj2cstr(data->dest).len + (indent * 2));
335
- fio_str_info_s buf = fiobj_obj2cstr(data->dest);
336
- while (indent--) {
337
- buf.data[buf.len++] = ' ';
338
- buf.data[buf.len++] = ' ';
339
- }
340
- fiobj_str_resize(data->dest, buf.len);
341
- }
342
- } else {
343
- fiobj_str_capa_assert(data->dest,
344
- fiobj_obj2cstr(data->dest).len +
345
- (fio_json_stack_count(data->stack) << 1));
346
- while (!data->count && data->parent) {
347
- if (FIOBJ_TYPE_IS(data->parent, FIOBJ_T_HASH)) {
348
- fiobj_str_write(data->dest, "}", 1);
349
- } else {
350
- fiobj_str_write(data->dest, "]", 1);
351
- }
352
- add_seperator = 1;
353
- data->count = 0;
354
- data->parent = FIOBJ_INVALID;
355
- fio_json_stack_pop(data->stack, &data->count);
356
- fio_json_stack_pop(data->stack, &data->parent);
357
- }
358
-
359
- if (add_seperator && data->parent) {
360
- fiobj_str_write(data->dest, ",", 1);
361
- }
362
- }
363
-
364
- return 0;
365
- }
366
-
367
- /* *****************************************************************************
368
- FIOBJ API
369
- ***************************************************************************** */
370
-
371
- /**
372
- * Parses JSON, setting `pobj` to point to the new Object.
373
- *
374
- * Returns the number of bytes consumed. On Error, 0 is returned and no data is
375
- * consumed.
376
- */
377
- size_t fiobj_json2obj(FIOBJ *pobj, const void *data, size_t len) {
378
- fiobj_json_parser_s p = {.top = FIOBJ_INVALID};
379
- size_t consumed = fio_json_parse(&p.p, data, len);
380
- if (!consumed || p.p.depth) {
381
- fiobj_free(fio_json_stack_get(&p.stack, 0));
382
- p.top = FIOBJ_INVALID;
383
- }
384
- fio_json_stack_free(&p.stack);
385
- fiobj_free(p.key);
386
- *pobj = p.top;
387
- return consumed;
388
- }
389
-
390
- /**
391
- * Updates a Hash using JSON data.
392
- *
393
- * Parsing errors and non-dictionar object JSON data are silently ignored,
394
- * attempting to update the Hash as much as possible before any errors
395
- * encountered.
396
- *
397
- * Conflicting Hash data is overwritten (prefering the new over the old).
398
- *
399
- * Returns the number of bytes consumed. On Error, 0 is returned and no data is
400
- * consumed.
401
- */
402
- size_t fiobj_hash_update_json(FIOBJ hash, const void *data, size_t len) {
403
- if (!hash)
404
- return 0;
405
- fiobj_json_parser_s p = {.top = FIOBJ_INVALID, .target = hash};
406
- size_t consumed = fio_json_parse(&p.p, data, len);
407
- fio_json_stack_free(&p.stack);
408
- fiobj_free(p.key);
409
- if (p.top != hash)
410
- fiobj_free(p.top);
411
- return consumed;
412
- }
413
-
414
- /**
415
- * Formats an object into a JSON string, appending the JSON string to an
416
- * existing String. Remember to `fiobj_free`.
417
- */
418
- FIOBJ fiobj_obj2json2(FIOBJ dest, FIOBJ o, uint8_t pretty) {
419
- assert(dest && FIOBJ_TYPE_IS(dest, FIOBJ_T_STRING));
420
- if (!o) {
421
- fiobj_str_write(dest, "null", 4);
422
- return 0;
423
- }
424
- fio_json_stack_s stack = FIO_ARY_INIT;
425
- obj2json_data_s data = {
426
- .dest = dest,
427
- .stack = &stack,
428
- .pretty = pretty,
429
- .count = 1,
430
- };
431
- if (!o || !FIOBJ_IS_ALLOCATED(o) || !FIOBJECT2VTBL(o)->each) {
432
- fiobj_obj2json_task(o, &data);
433
- return dest;
434
- }
435
- fiobj_each2(o, fiobj_obj2json_task, &data);
436
- fio_json_stack_free(&stack);
437
- return dest;
438
- }
439
-
440
- /* Formats an object into a JSON string. Remember to `fiobj_free`. */
441
- FIOBJ fiobj_obj2json(FIOBJ obj, uint8_t pretty) {
442
- return fiobj_obj2json2(fiobj_str_buf(128), obj, pretty);
443
- }
444
-
445
- /* *****************************************************************************
446
- Test
447
- ***************************************************************************** */
448
-
449
- #if DEBUG
450
- void fiobj_test_json(void) {
451
- fprintf(stderr, "=== Testing JSON parser (simple test)\n");
452
- #define TEST_ASSERT(cond, ...) \
453
- if (!(cond)) { \
454
- fprintf(stderr, "* " __VA_ARGS__); \
455
- fprintf(stderr, "\n !!! Testing failed !!!\n"); \
456
- exit(-1); \
457
- }
458
- char json_str[] = "{\"array\":[1,2,3,\"boom\"],\"my\":{\"secret\":42},"
459
- "\"true\":true,\"false\":false,\"null\":null,\"float\":-2."
460
- "2,\"string\":\"I \\\"wrote\\\" this.\"}";
461
- char json_str_update[] = "{\"array\":[1,2,3]}";
462
- char json_str2[] =
463
- "[\n \"JSON Test Pattern pass1\",\n {\"object with 1 "
464
- "member\":[\"array with 1 element\"]},\n {},\n [],\n -42,\n "
465
- "true,\n false,\n null,\n {\n \"integer\": 1234567890,\n "
466
- " \"real\": -9876.543210,\n \"e\": 0.123456789e-12,\n "
467
- " \"E\": 1.234567890E+34,\n \"\": 23456789012E66,\n "
468
- "\"zero\": 0,\n \"one\": 1,\n \"space\": \" \",\n "
469
- "\"quote\": \"\\\"\",\n \"backslash\": \"\\\\\",\n "
470
- "\"controls\": \"\\b\\f\\n\\r\\t\",\n \"slash\": \"/ & \\/\",\n "
471
- " \"alpha\": \"abcdefghijklmnopqrstuvwyz\",\n \"ALPHA\": "
472
- "\"ABCDEFGHIJKLMNOPQRSTUVWYZ\",\n \"digit\": \"0123456789\",\n "
473
- " \"0123456789\": \"digit\",\n \"special\": "
474
- "\"`1~!@#$%^&*()_+-={':[,]}|;.</>?\",\n \"hex\": "
475
- "\"\\u0123\\u4567\\u89AB\\uCDEF\\uabcd\\uef4A\",\n \"true\": "
476
- "true,\n \"false\": false,\n \"null\": null,\n "
477
- "\"array\":[ ],\n \"object\":{ },\n \"address\": \"50 "
478
- "St. James Street\",\n \"url\": \"http://www.JSON.org/\",\n "
479
- " \"comment\": \"// /* <!-- --\",\n \"# -- --> */\": \" \",\n "
480
- " \" s p a c e d \" :[1,2 , 3\n\n,\n\n4 , 5 , 6 "
481
- " ,7 ],\"compact\":[1,2,3,4,5,6,7],\n \"jsontext\": "
482
- "\"{\\\"object with 1 member\\\":[\\\"array with 1 element\\\"]}\",\n "
483
- " \"quotes\": \"&#34; \\u0022 %22 0x22 034 &#x22;\",\n "
484
- "\"\\/"
485
- "\\\\\\\"\\uCAFE\\uBABE\\uAB98\\uFCDE\\ubcda\\uef4A\\b\\f\\n\\r\\t`1~!@#$"
486
- "%^&*()_+-=[]{}|;:',./<>?\"\n: \"A key can be any string\"\n },\n "
487
- "0.5 "
488
- ",98.6\n,\n99.44\n,\n\n1066,\n1e1,\n0.1e1,\n1e-1,\n1e00,2e+00,2e-00\n,"
489
- "\"rosebud\"]";
490
-
491
- FIOBJ o = 0;
492
- TEST_ASSERT(fiobj_json2obj(&o, "1", 2) == 1,
493
- "JSON number parsing failed to run!\n");
494
- TEST_ASSERT(o, "JSON (single) object missing!\n");
495
- TEST_ASSERT(FIOBJ_TYPE_IS(o, FIOBJ_T_NUMBER),
496
- "JSON (single) not a number!\n");
497
- TEST_ASSERT(fiobj_obj2num(o) == 1, "JSON (single) not == 1!\n");
498
- fiobj_free(o);
499
-
500
- TEST_ASSERT(fiobj_json2obj(&o, "2.0", 5) == 3,
501
- "JSON float parsing failed to run!\n");
502
- TEST_ASSERT(o, "JSON (float) object missing!\n");
503
- TEST_ASSERT(FIOBJ_TYPE_IS(o, FIOBJ_T_FLOAT), "JSON (float) not a float!\n");
504
- TEST_ASSERT(fiobj_obj2float(o) == 2, "JSON (float) not == 2!\n");
505
- fiobj_free(o);
506
-
507
- TEST_ASSERT(fiobj_json2obj(&o, json_str, sizeof(json_str)) ==
508
- (sizeof(json_str) - 1),
509
- "JSON parsing failed to run!\n");
510
- TEST_ASSERT(o, "JSON object missing!\n");
511
- TEST_ASSERT(FIOBJ_TYPE_IS(o, FIOBJ_T_HASH),
512
- "JSON root not a dictionary (not a hash)!\n");
513
- FIOBJ tmp = fiobj_hash_get2(o, fiobj_hash_string("array", 5));
514
- TEST_ASSERT(FIOBJ_TYPE_IS(tmp, FIOBJ_T_ARRAY),
515
- "JSON 'array' not an Array!\n");
516
- TEST_ASSERT(fiobj_obj2num(fiobj_ary_index(tmp, 0)) == 1,
517
- "JSON 'array' index 0 error!\n");
518
- TEST_ASSERT(fiobj_obj2num(fiobj_ary_index(tmp, 1)) == 2,
519
- "JSON 'array' index 1 error!\n");
520
- TEST_ASSERT(fiobj_obj2num(fiobj_ary_index(tmp, 2)) == 3,
521
- "JSON 'array' index 2 error!\n");
522
- TEST_ASSERT(FIOBJ_TYPE_IS(fiobj_ary_index(tmp, 3), FIOBJ_T_STRING),
523
- "JSON 'array' index 3 type error!\n");
524
- TEST_ASSERT(!memcmp("boom", fiobj_obj2cstr(fiobj_ary_index(tmp, 3)).data, 4),
525
- "JSON 'array' index 3 error!\n");
526
- tmp = fiobj_hash_get2(o, fiobj_hash_string("my", 2));
527
- TEST_ASSERT(FIOBJ_TYPE_IS(tmp, FIOBJ_T_HASH),
528
- "JSON 'my:secret' not a Hash!\n");
529
- TEST_ASSERT(
530
- FIOBJ_TYPE_IS(fiobj_hash_get2(tmp, fiobj_hash_string("secret", 6)),
531
- FIOBJ_T_NUMBER),
532
- "JSON 'my:secret' doesn't hold a number!\n");
533
- TEST_ASSERT(
534
- fiobj_obj2num(fiobj_hash_get2(tmp, fiobj_hash_string("secret", 6))) == 42,
535
- "JSON 'my:secret' not 42!\n");
536
- TEST_ASSERT(fiobj_hash_get2(o, fiobj_hash_string("true", 4)) == fiobj_true(),
537
- "JSON 'true' not true!\n");
538
- TEST_ASSERT(fiobj_hash_get2(o, fiobj_hash_string("false", 5)) ==
539
- fiobj_false(),
540
- "JSON 'false' not false!\n");
541
- TEST_ASSERT(fiobj_hash_get2(o, fiobj_hash_string("null", 4)) == fiobj_null(),
542
- "JSON 'null' not null!\n");
543
- tmp = fiobj_hash_get2(o, fiobj_hash_string("float", 5));
544
- TEST_ASSERT(FIOBJ_TYPE_IS(tmp, FIOBJ_T_FLOAT), "JSON 'float' not a float!\n");
545
- tmp = fiobj_hash_get2(o, fiobj_hash_string("string", 6));
546
- TEST_ASSERT(FIOBJ_TYPE_IS(tmp, FIOBJ_T_STRING),
547
- "JSON 'string' not a string!\n");
548
- TEST_ASSERT(!strcmp(fiobj_obj2cstr(tmp).data, "I \"wrote\" this."),
549
- "JSON 'string' incorrect!\n");
550
- fprintf(stderr, "* passed.\n");
551
- fprintf(stderr, "=== Testing JSON formatting (simple test)\n");
552
- tmp = fiobj_obj2json(o, 0);
553
- fprintf(stderr, "* data (%p):\n%.*s\n", (void *)fiobj_obj2cstr(tmp).data,
554
- (int)fiobj_obj2cstr(tmp).len, fiobj_obj2cstr(tmp).data);
555
- if (!strcmp(fiobj_obj2cstr(tmp).data, json_str))
556
- fprintf(stderr, "* Stringify == Original.\n");
557
- TEST_ASSERT(
558
- fiobj_hash_update_json(o, json_str_update, strlen(json_str_update)),
559
- "JSON update failed to parse data.");
560
- fiobj_free(tmp);
561
-
562
- tmp = fiobj_hash_get2(o, fiobj_hash_string("array", 5));
563
- TEST_ASSERT(FIOBJ_TYPE_IS(tmp, FIOBJ_T_ARRAY),
564
- "JSON updated 'array' not an Array!\n");
565
- TEST_ASSERT(fiobj_ary_count(tmp) == 3, "JSON updated 'array' not updated?");
566
- tmp = fiobj_hash_get2(o, fiobj_hash_string("float", 5));
567
- TEST_ASSERT(FIOBJ_TYPE_IS(tmp, FIOBJ_T_FLOAT),
568
- "JSON updated (old) 'float' missing!\n");
569
- fiobj_free(o);
570
- fprintf(stderr, "* passed.\n");
571
-
572
- fprintf(stderr, "=== Testing JSON parsing (UTF-8 and special cases)\n");
573
- fiobj_json2obj(&o, "[\"\\uD834\\uDD1E\"]", 16);
574
- TEST_ASSERT(o, "JSON G clef String failed to parse!\n");
575
- TEST_ASSERT(FIOBJ_TYPE_IS(o, FIOBJ_T_ARRAY),
576
- "JSON G clef container has an incorrect type! (%s)\n",
577
- fiobj_type_name(o));
578
- tmp = o;
579
- o = fiobj_ary_pop(o);
580
- fiobj_free(tmp);
581
- TEST_ASSERT(FIOBJ_TYPE_IS(o, FIOBJ_T_STRING),
582
- "JSON G clef String incorrect type! %p => %s\n", (void *)o,
583
- fiobj_type_name(o));
584
- TEST_ASSERT((!strcmp(fiobj_obj2cstr(o).data, "\xF0\x9D\x84\x9E")),
585
- "JSON G clef String incorrect %s !\n", fiobj_obj2cstr(o).data);
586
- fiobj_free(o);
587
-
588
- fiobj_json2obj(&o, "\"\\uD834\\uDD1E\"", 14);
589
- TEST_ASSERT(FIOBJ_TYPE_IS(o, FIOBJ_T_STRING),
590
- "JSON direct G clef String incorrect type! %p => %s\n", (void *)o,
591
- fiobj_type_name(o));
592
- TEST_ASSERT((!strcmp(fiobj_obj2cstr(o).data, "\xF0\x9D\x84\x9E")),
593
- "JSON direct G clef String incorrect %s !\n",
594
- fiobj_obj2cstr(o).data);
595
- fiobj_free(o);
596
- fiobj_json2obj(&o, "\"Hello\\u0000World\"", 19);
597
- TEST_ASSERT(FIOBJ_TYPE_IS(o, FIOBJ_T_STRING),
598
- "JSON NUL containing String incorrect type! %p => %s\n",
599
- (void *)o, fiobj_type_name(o));
600
- TEST_ASSERT(
601
- (!memcmp(fiobj_obj2cstr(o).data, "Hello\0World", fiobj_obj2cstr(o).len)),
602
- "JSON NUL containing String incorrect! (%u): %s . %s\n",
603
- (int)fiobj_obj2cstr(o).len, fiobj_obj2cstr(o).data,
604
- fiobj_obj2cstr(o).data + 3);
605
- fiobj_free(o);
606
- size_t consumed = fiobj_json2obj(&o, json_str2, sizeof(json_str2));
607
- TEST_ASSERT(
608
- consumed == (sizeof(json_str2) - 1),
609
- "JSON messy string failed to parse (consumed %lu instead of %lu\n",
610
- (unsigned long)consumed, (unsigned long)(sizeof(json_str2) - 1));
611
- TEST_ASSERT(FIOBJ_TYPE_IS(o, FIOBJ_T_ARRAY),
612
- "JSON messy string object error\n");
613
- tmp = fiobj_obj2json(o, 1);
614
- TEST_ASSERT(FIOBJ_TYPE_IS(tmp, FIOBJ_T_STRING),
615
- "JSON messy string isn't a string\n");
616
- fprintf(stderr, "Messy JSON:\n%s\n", fiobj_obj2cstr(tmp).data);
617
- fiobj_free(o);
618
- fiobj_free(tmp);
619
- fprintf(stderr, "* passed.\n");
620
- }
621
-
622
- #endif
@@ -1,68 +0,0 @@
1
- #ifndef H_FIOBJ_JSON_H
2
- #define H_FIOBJ_JSON_H
3
-
4
- /*
5
- Copyright: Boaz Segev, 2017-2019
6
- License: MIT
7
- */
8
-
9
- #include <fiobj_ary.h>
10
- #include <fiobj_hash.h>
11
- #include <fiobj_numbers.h>
12
- #include <fiobj_str.h>
13
- #include <fiobject.h>
14
-
15
- #ifdef __cplusplus
16
- extern "C" {
17
- #endif
18
-
19
- /* *****************************************************************************
20
- JSON API
21
- ***************************************************************************** */
22
-
23
- /** Limit JSON nesting, 32 is the limit to accomodate a 32 bit type. */
24
- #if !defined(JSON_MAX_DEPTH) || JSON_MAX_DEPTH > 32
25
- #undef JSON_MAX_DEPTH
26
- #define JSON_MAX_DEPTH 32
27
- #endif
28
-
29
- /**
30
- * Parses JSON, setting `pobj` to point to the new Object.
31
- *
32
- * Returns the number of bytes consumed. On Error, 0 is returned and no data is
33
- * consumed.
34
- */
35
- size_t fiobj_json2obj(FIOBJ *pobj, const void *data, size_t len);
36
- /**
37
- * Stringify an object into a JSON string. Remember to `fiobj_free`.
38
- *
39
- * Note that only the foloowing basic fiobj types are supported: Primitives
40
- * (True / False / NULL), Numbers (Number / Float), Strings, Hashes and Arrays.
41
- *
42
- * Some objects (such as the POSIX specific IO type) are unsupported and may be
43
- * formatted incorrectly.
44
- */
45
- FIOBJ fiobj_obj2json(FIOBJ, uint8_t pretty);
46
-
47
- /**
48
- * Formats an object into a JSON string, appending the JSON string to an
49
- * existing String. Remember to `fiobj_free` as usual.
50
- *
51
- * Note that only the following basic fiobj types are supported: Primitives
52
- * (True / False / NULL), Numbers (Number / Float), Strings, Hashes and
53
- * Arrays.
54
- *
55
- * Some objects (such as the POSIX specific IO type) are unsupported and may be
56
- * formatted incorrectly.
57
- */
58
- FIOBJ fiobj_obj2json2(FIOBJ dest, FIOBJ object, uint8_t pretty);
59
-
60
- #if DEBUG
61
- void fiobj_test_json(void);
62
- #endif
63
-
64
- #ifdef __cplusplus
65
- } /* extern "C" */
66
- #endif
67
-
68
- #endif