rage-iodine 1.7.58

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  3. data/.github/workflows/ruby.yml +42 -0
  4. data/.gitignore +20 -0
  5. data/.rspec +2 -0
  6. data/.yardopts +8 -0
  7. data/CHANGELOG.md +1098 -0
  8. data/Gemfile +11 -0
  9. data/LICENSE.txt +21 -0
  10. data/LIMITS.md +41 -0
  11. data/README.md +782 -0
  12. data/Rakefile +23 -0
  13. data/SPEC-PubSub-Draft.md +159 -0
  14. data/SPEC-WebSocket-Draft.md +239 -0
  15. data/bin/console +22 -0
  16. data/bin/info.md +353 -0
  17. data/bin/mustache_bench.rb +100 -0
  18. data/bin/poc/Gemfile.lock +23 -0
  19. data/bin/poc/README.md +37 -0
  20. data/bin/poc/config.ru +66 -0
  21. data/bin/poc/gemfile +1 -0
  22. data/bin/poc/www/index.html +57 -0
  23. data/examples/async_task.ru +92 -0
  24. data/examples/bates/README.md +3 -0
  25. data/examples/bates/config.ru +342 -0
  26. data/examples/bates/david+bold.pdf +0 -0
  27. data/examples/bates/public/drop-pdf.png +0 -0
  28. data/examples/bates/public/index.html +600 -0
  29. data/examples/config.ru +59 -0
  30. data/examples/echo.ru +59 -0
  31. data/examples/etag.ru +16 -0
  32. data/examples/hello.ru +29 -0
  33. data/examples/pubsub_engine.ru +81 -0
  34. data/examples/rack3.ru +12 -0
  35. data/examples/redis.ru +70 -0
  36. data/examples/shootout.ru +73 -0
  37. data/examples/sub-protocols.ru +90 -0
  38. data/examples/tcp_client.rb +66 -0
  39. data/examples/x-sendfile.ru +14 -0
  40. data/exe/iodine +280 -0
  41. data/ext/iodine/extconf.rb +110 -0
  42. data/ext/iodine/fio.c +12096 -0
  43. data/ext/iodine/fio.h +6390 -0
  44. data/ext/iodine/fio_cli.c +431 -0
  45. data/ext/iodine/fio_cli.h +189 -0
  46. data/ext/iodine/fio_json_parser.h +687 -0
  47. data/ext/iodine/fio_siphash.c +157 -0
  48. data/ext/iodine/fio_siphash.h +37 -0
  49. data/ext/iodine/fio_tls.h +129 -0
  50. data/ext/iodine/fio_tls_missing.c +649 -0
  51. data/ext/iodine/fio_tls_openssl.c +1056 -0
  52. data/ext/iodine/fio_tmpfile.h +50 -0
  53. data/ext/iodine/fiobj.h +44 -0
  54. data/ext/iodine/fiobj4fio.h +21 -0
  55. data/ext/iodine/fiobj_ary.c +333 -0
  56. data/ext/iodine/fiobj_ary.h +139 -0
  57. data/ext/iodine/fiobj_data.c +1185 -0
  58. data/ext/iodine/fiobj_data.h +167 -0
  59. data/ext/iodine/fiobj_hash.c +409 -0
  60. data/ext/iodine/fiobj_hash.h +176 -0
  61. data/ext/iodine/fiobj_json.c +622 -0
  62. data/ext/iodine/fiobj_json.h +68 -0
  63. data/ext/iodine/fiobj_mem.h +71 -0
  64. data/ext/iodine/fiobj_mustache.c +317 -0
  65. data/ext/iodine/fiobj_mustache.h +62 -0
  66. data/ext/iodine/fiobj_numbers.c +344 -0
  67. data/ext/iodine/fiobj_numbers.h +127 -0
  68. data/ext/iodine/fiobj_str.c +433 -0
  69. data/ext/iodine/fiobj_str.h +172 -0
  70. data/ext/iodine/fiobject.c +620 -0
  71. data/ext/iodine/fiobject.h +654 -0
  72. data/ext/iodine/hpack.h +1923 -0
  73. data/ext/iodine/http.c +2736 -0
  74. data/ext/iodine/http.h +1019 -0
  75. data/ext/iodine/http1.c +825 -0
  76. data/ext/iodine/http1.h +29 -0
  77. data/ext/iodine/http1_parser.h +1835 -0
  78. data/ext/iodine/http_internal.c +1279 -0
  79. data/ext/iodine/http_internal.h +248 -0
  80. data/ext/iodine/http_mime_parser.h +350 -0
  81. data/ext/iodine/iodine.c +1433 -0
  82. data/ext/iodine/iodine.h +64 -0
  83. data/ext/iodine/iodine_caller.c +218 -0
  84. data/ext/iodine/iodine_caller.h +27 -0
  85. data/ext/iodine/iodine_connection.c +941 -0
  86. data/ext/iodine/iodine_connection.h +55 -0
  87. data/ext/iodine/iodine_defer.c +420 -0
  88. data/ext/iodine/iodine_defer.h +6 -0
  89. data/ext/iodine/iodine_fiobj2rb.h +120 -0
  90. data/ext/iodine/iodine_helpers.c +282 -0
  91. data/ext/iodine/iodine_helpers.h +12 -0
  92. data/ext/iodine/iodine_http.c +1280 -0
  93. data/ext/iodine/iodine_http.h +23 -0
  94. data/ext/iodine/iodine_json.c +302 -0
  95. data/ext/iodine/iodine_json.h +6 -0
  96. data/ext/iodine/iodine_mustache.c +567 -0
  97. data/ext/iodine/iodine_mustache.h +6 -0
  98. data/ext/iodine/iodine_pubsub.c +580 -0
  99. data/ext/iodine/iodine_pubsub.h +26 -0
  100. data/ext/iodine/iodine_rack_io.c +273 -0
  101. data/ext/iodine/iodine_rack_io.h +20 -0
  102. data/ext/iodine/iodine_store.c +142 -0
  103. data/ext/iodine/iodine_store.h +20 -0
  104. data/ext/iodine/iodine_tcp.c +346 -0
  105. data/ext/iodine/iodine_tcp.h +13 -0
  106. data/ext/iodine/iodine_tls.c +261 -0
  107. data/ext/iodine/iodine_tls.h +13 -0
  108. data/ext/iodine/mustache_parser.h +1546 -0
  109. data/ext/iodine/redis_engine.c +957 -0
  110. data/ext/iodine/redis_engine.h +79 -0
  111. data/ext/iodine/resp_parser.h +317 -0
  112. data/ext/iodine/scheduler.c +173 -0
  113. data/ext/iodine/scheduler.h +6 -0
  114. data/ext/iodine/websocket_parser.h +506 -0
  115. data/ext/iodine/websockets.c +752 -0
  116. data/ext/iodine/websockets.h +185 -0
  117. data/iodine.gemspec +50 -0
  118. data/lib/iodine/connection.rb +61 -0
  119. data/lib/iodine/json.rb +42 -0
  120. data/lib/iodine/mustache.rb +113 -0
  121. data/lib/iodine/pubsub.rb +55 -0
  122. data/lib/iodine/rack_utils.rb +43 -0
  123. data/lib/iodine/tls.rb +16 -0
  124. data/lib/iodine/version.rb +3 -0
  125. data/lib/iodine.rb +274 -0
  126. data/lib/rack/handler/iodine.rb +33 -0
  127. data/logo.png +0 -0
  128. metadata +284 -0
@@ -0,0 +1,620 @@
1
+ /*
2
+ Copyright: Boaz Segev, 2017-2019
3
+ License: MIT
4
+ */
5
+
6
+ /**
7
+ This facil.io core library provides wrappers around complex and (or) dynamic
8
+ types, abstracting some complexity and making dynamic type related tasks easier.
9
+ */
10
+
11
+ #include <fiobject.h>
12
+
13
+ #define FIO_ARY_NAME fiobj_stack
14
+ #define FIO_ARY_TYPE FIOBJ
15
+ #define FIO_ARY_INVALID FIOBJ_INVALID
16
+ /* don't free or compare objects, this stack shouldn't have side-effects */
17
+ #include <fio.h>
18
+
19
+ #include <stdarg.h>
20
+ #include <stdint.h>
21
+ #include <stdio.h>
22
+ #include <stdlib.h>
23
+ #include <string.h>
24
+
25
+ /* *****************************************************************************
26
+ Use the facil.io features when available, but override when missing.
27
+ ***************************************************************************** */
28
+ #ifndef fd_data /* defined in fio.c */
29
+
30
+ #pragma weak fio_malloc
31
+ void *fio_malloc(size_t size) {
32
+ void *m = malloc(size);
33
+ if (m)
34
+ memset(m, 0, size);
35
+ return m;
36
+ }
37
+
38
+ #pragma weak fio_calloc
39
+ void *__attribute__((weak)) fio_calloc(size_t size, size_t count) {
40
+ return calloc(size, count);
41
+ }
42
+
43
+ #pragma weak fio_free
44
+ void __attribute__((weak)) fio_free(void *ptr) { free(ptr); }
45
+
46
+ #pragma weak fio_realloc
47
+ void *__attribute__((weak)) fio_realloc(void *ptr, size_t new_size) {
48
+ return realloc(ptr, new_size);
49
+ }
50
+
51
+ #pragma weak fio_realloc2
52
+ void *__attribute__((weak))
53
+ fio_realloc2(void *ptr, size_t new_size, size_t valid_len) {
54
+ return realloc(ptr, new_size);
55
+ (void)valid_len;
56
+ }
57
+
58
+ #pragma weak fio_mmap
59
+ void *__attribute__((weak)) fio_mmap(size_t size) { return fio_malloc(size); }
60
+
61
+ /** The logging level */
62
+ #if DEBUG
63
+ #pragma weak FIO_LOG_LEVEL
64
+ int __attribute__((weak)) FIO_LOG_LEVEL = FIO_LOG_LEVEL_DEBUG;
65
+ #else
66
+ #pragma weak FIO_LOG_LEVEL
67
+ int __attribute__((weak)) FIO_LOG_LEVEL = FIO_LOG_LEVEL_INFO;
68
+ #endif
69
+
70
+ /**
71
+ * We include this in case the parser is used outside of facil.io.
72
+ */
73
+ int64_t __attribute__((weak)) fio_atol(char **pstr) {
74
+ return strtoll(*pstr, pstr, 0);
75
+ }
76
+ #pragma weak fio_atol
77
+
78
+ /**
79
+ * We include this in case the parser is used outside of facil.io.
80
+ */
81
+ double __attribute__((weak)) fio_atof(char **pstr) {
82
+ return strtod(*pstr, pstr);
83
+ }
84
+ #pragma weak fio_atof
85
+
86
+ /**
87
+ * A helper function that writes a signed int64_t to a string.
88
+ *
89
+ * No overflow guard is provided, make sure there's at least 68 bytes
90
+ * available (for base 2).
91
+ *
92
+ * Offers special support for base 2 (binary), base 8 (octal), base 10 and base
93
+ * 16 (hex). An unsupported base will silently default to base 10. Prefixes
94
+ * are automatically added (i.e., "0x" for hex and "0b" for base 2).
95
+ *
96
+ * Returns the number of bytes actually written (excluding the NUL
97
+ * terminator).
98
+ */
99
+ #pragma weak fio_ltoa
100
+ size_t __attribute__((weak)) fio_ltoa(char *dest, int64_t num, uint8_t base) {
101
+ const char notation[] = {'0', '1', '2', '3', '4', '5', '6', '7',
102
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
103
+
104
+ size_t len = 0;
105
+ char buf[48] = {0}; /* we only need up to 20 for base 10, but base 3 needs 41... */
106
+
107
+ if (!num)
108
+ goto zero;
109
+
110
+ switch (base) {
111
+ case 1: /* fallthrough */
112
+ case 2:
113
+ /* Base 2 */
114
+ {
115
+ uint64_t n = num; /* avoid bit shifting inconsistencies with signed bit */
116
+ uint8_t i = 0; /* counting bits */
117
+ dest[len++] = '0';
118
+ dest[len++] = 'b';
119
+
120
+ while ((i < 64) && (n & 0x8000000000000000) == 0) {
121
+ n = n << 1;
122
+ i++;
123
+ }
124
+ /* make sure the Binary representation doesn't appear signed. */
125
+ if (i) {
126
+ dest[len++] = '0';
127
+ }
128
+ /* write to dest. */
129
+ while (i < 64) {
130
+ dest[len++] = ((n & 0x8000000000000000) ? '1' : '0');
131
+ n = n << 1;
132
+ i++;
133
+ }
134
+ dest[len] = 0;
135
+ return len;
136
+ }
137
+ case 8:
138
+ /* Base 8 */
139
+ {
140
+ uint64_t l = 0;
141
+ if (num < 0) {
142
+ dest[len++] = '-';
143
+ num = 0 - num;
144
+ }
145
+ dest[len++] = '0';
146
+
147
+ while (num) {
148
+ buf[l++] = '0' + (num & 7);
149
+ num = num >> 3;
150
+ }
151
+ while (l) {
152
+ --l;
153
+ dest[len++] = buf[l];
154
+ }
155
+ dest[len] = 0;
156
+ return len;
157
+ }
158
+
159
+ case 16:
160
+ /* Base 16 */
161
+ {
162
+ uint64_t n = num; /* avoid bit shifting inconsistencies with signed bit */
163
+ uint8_t i = 0; /* counting bits */
164
+ dest[len++] = '0';
165
+ dest[len++] = 'x';
166
+ while (i < 8 && (n & 0xFF00000000000000) == 0) {
167
+ n = n << 8;
168
+ i++;
169
+ }
170
+ /* make sure the Hex representation doesn't appear signed. */
171
+ if (i && (n & 0x8000000000000000)) {
172
+ dest[len++] = '0';
173
+ dest[len++] = '0';
174
+ }
175
+ /* write the damn thing */
176
+ while (i < 8) {
177
+ uint8_t tmp = (n & 0xF000000000000000) >> 60;
178
+ dest[len++] = notation[tmp];
179
+ tmp = (n & 0x0F00000000000000) >> 56;
180
+ dest[len++] = notation[tmp];
181
+ i++;
182
+ n = n << 8;
183
+ }
184
+ dest[len] = 0;
185
+ return len;
186
+ }
187
+ case 3: /* fallthrough */
188
+ case 4: /* fallthrough */
189
+ case 5: /* fallthrough */
190
+ case 6: /* fallthrough */
191
+ case 7: /* fallthrough */
192
+ case 9: /* fallthrough */
193
+ /* rare bases */
194
+ if (num < 0) {
195
+ dest[len++] = '-';
196
+ num = 0 - num;
197
+ }
198
+ uint64_t l = 0;
199
+ while (num) {
200
+ uint64_t t = num / base;
201
+ buf[l++] = '0' + (num - (t * base));
202
+ num = t;
203
+ }
204
+ while (l) {
205
+ --l;
206
+ dest[len++] = buf[l];
207
+ }
208
+ dest[len] = 0;
209
+ return len;
210
+
211
+ default:
212
+ break;
213
+ }
214
+ /* Base 10, the default base */
215
+
216
+ if (num < 0) {
217
+ dest[len++] = '-';
218
+ num = 0 - num;
219
+ }
220
+ uint64_t l = 0;
221
+ while (num) {
222
+ uint64_t t = num / 10;
223
+ buf[l++] = '0' + (num - (t * 10));
224
+ num = t;
225
+ }
226
+ while (l) {
227
+ --l;
228
+ dest[len++] = buf[l];
229
+ }
230
+ dest[len] = 0;
231
+ return len;
232
+
233
+ zero:
234
+ switch (base) {
235
+ case 1: /* fallthrough */
236
+ case 2:
237
+ dest[len++] = '0';
238
+ dest[len++] = 'b';
239
+ /* fallthrough */
240
+ case 16:
241
+ dest[len++] = '0';
242
+ dest[len++] = 'x';
243
+ dest[len++] = '0';
244
+ }
245
+ dest[len++] = '0';
246
+ dest[len] = 0;
247
+ return len;
248
+ }
249
+
250
+ /**
251
+ * A helper function that converts between a double to a string.
252
+ *
253
+ * No overflow guard is provided, make sure there's at least 130 bytes
254
+ * available (for base 2).
255
+ *
256
+ * Supports base 2, base 10 and base 16. An unsupported base will silently
257
+ * default to base 10. Prefixes aren't added (i.e., no "0x" or "0b" at the
258
+ * beginning of the string).
259
+ *
260
+ * Returns the number of bytes actually written (excluding the NUL
261
+ * terminator).
262
+ */
263
+ #pragma weak fio_ftoa
264
+ size_t __attribute__((weak)) fio_ftoa(char *dest, double num, uint8_t base) {
265
+ if (base == 2 || base == 16) {
266
+ /* handle the binary / Hex representation the same as if it were an
267
+ * int64_t
268
+ */
269
+ int64_t *i = (void *)&num;
270
+ return fio_ltoa(dest, *i, base);
271
+ }
272
+
273
+ size_t written = sprintf(dest, "%g", num);
274
+ uint8_t need_zero = 1;
275
+ char *start = dest;
276
+ while (*start) {
277
+ if (*start == ',') // locale issues?
278
+ *start = '.';
279
+ if (*start == '.' || *start == 'e') {
280
+ need_zero = 0;
281
+ break;
282
+ }
283
+ start++;
284
+ }
285
+ if (need_zero) {
286
+ dest[written++] = '.';
287
+ dest[written++] = '0';
288
+ }
289
+ return written;
290
+ }
291
+
292
+ #endif
293
+ /* *****************************************************************************
294
+ the `fiobj_each2` function
295
+ ***************************************************************************** */
296
+
297
+ struct task_packet_s {
298
+ int (*task)(FIOBJ obj, void *arg);
299
+ void *arg;
300
+ fiobj_stack_s *stack;
301
+ FIOBJ next;
302
+ uintptr_t counter;
303
+ uint8_t stop;
304
+ uint8_t incomplete;
305
+ };
306
+
307
+ static int fiobj_task_wrapper(FIOBJ o, void *p_) {
308
+ struct task_packet_s *p = p_;
309
+ ++p->counter;
310
+ int ret = p->task(o, p->arg);
311
+ if (ret == -1) {
312
+ p->stop = 1;
313
+ return -1;
314
+ }
315
+ if (FIOBJ_IS_ALLOCATED(o) && FIOBJECT2VTBL(o)->each) {
316
+ p->incomplete = 1;
317
+ p->next = o;
318
+ return -1;
319
+ }
320
+ return 0;
321
+ }
322
+ /**
323
+ * Single layer iteration using a callback for each nested fio object.
324
+ *
325
+ * Accepts any `FIOBJ ` type but only collections (Arrays and Hashes) are
326
+ * processed. The container itself (the Array or the Hash) is **not** processed
327
+ * (unlike `fiobj_each2`).
328
+ *
329
+ * The callback task function must accept an object and an opaque user pointer.
330
+ *
331
+ * Hash objects pass along a `FIOBJ_T_COUPLET` object, containing
332
+ * references for both the key and the object. Keys shouldn't be altered once
333
+ * placed as a key (or the Hash will break). Collections (Arrays / Hashes) can't
334
+ * be used as keeys.
335
+ *
336
+ * If the callback returns -1, the loop is broken. Any other value is ignored.
337
+ *
338
+ * Returns the "stop" position, i.e., the number of items processed + the
339
+ * starting point.
340
+ */
341
+ size_t fiobj_each2(FIOBJ o, int (*task)(FIOBJ obj, void *arg), void *arg) {
342
+ if (!o || !FIOBJ_IS_ALLOCATED(o) || (FIOBJECT2VTBL(o)->each == NULL)) {
343
+ task(o, arg);
344
+ return 1;
345
+ }
346
+ /* run task for root object */
347
+ if (task(o, arg) == -1)
348
+ return 1;
349
+ uintptr_t pos = 0;
350
+ fiobj_stack_s stack = FIO_ARY_INIT;
351
+ struct task_packet_s packet = {
352
+ .task = task,
353
+ .arg = arg,
354
+ .stack = &stack,
355
+ .counter = 1,
356
+ };
357
+ do {
358
+ if (!pos)
359
+ packet.next = 0;
360
+ packet.incomplete = 0;
361
+ pos = FIOBJECT2VTBL(o)->each(o, pos, fiobj_task_wrapper, &packet);
362
+ if (packet.stop)
363
+ goto finish;
364
+ if (packet.incomplete) {
365
+ fiobj_stack_push(&stack, pos);
366
+ fiobj_stack_push(&stack, o);
367
+ }
368
+
369
+ if (packet.next) {
370
+ fiobj_stack_push(&stack, (FIOBJ)0);
371
+ fiobj_stack_push(&stack, packet.next);
372
+ }
373
+ o = FIOBJ_INVALID;
374
+ fiobj_stack_pop(&stack, &o);
375
+ fiobj_stack_pop(&stack, &pos);
376
+ } while (o);
377
+ finish:
378
+ fiobj_stack_free(&stack);
379
+ return packet.counter;
380
+ }
381
+
382
+ /* *****************************************************************************
383
+ Free complex objects (objects with nesting)
384
+ ***************************************************************************** */
385
+
386
+ static void fiobj_dealloc_task(FIOBJ o, void *stack_) {
387
+ // if (!o)
388
+ // fprintf(stderr, "* WARN: freeing a NULL no-object\n");
389
+ // else
390
+ // fprintf(stderr, "* freeing object %s\n", fiobj_obj2cstr(o).data);
391
+ if (!o || !FIOBJ_IS_ALLOCATED(o))
392
+ return;
393
+ if (OBJREF_REM(o))
394
+ return;
395
+ if (!FIOBJECT2VTBL(o)->each || !FIOBJECT2VTBL(o)->count(o)) {
396
+ FIOBJECT2VTBL(o)->dealloc(o, NULL, NULL);
397
+ return;
398
+ }
399
+ fiobj_stack_s *s = stack_;
400
+ fiobj_stack_push(s, o);
401
+ }
402
+ /**
403
+ * Decreases an object's reference count, releasing memory and
404
+ * resources.
405
+ *
406
+ * This function affects nested objects, meaning that when an Array or
407
+ * a Hash object is passed along, it's children (nested objects) are
408
+ * also freed.
409
+ */
410
+ void fiobj_free_complex_object(FIOBJ o) {
411
+ fiobj_stack_s stack = FIO_ARY_INIT;
412
+ do {
413
+ FIOBJECT2VTBL(o)->dealloc(o, fiobj_dealloc_task, &stack);
414
+ } while (!fiobj_stack_pop(&stack, &o));
415
+ fiobj_stack_free(&stack);
416
+ }
417
+
418
+ /* *****************************************************************************
419
+ Is Equal?
420
+ ***************************************************************************** */
421
+ #include <fiobj_hash.h>
422
+
423
+ static inline int fiobj_iseq_simple(const FIOBJ o, const FIOBJ o2) {
424
+ if (o == o2)
425
+ return 1;
426
+ if (!o || !o2)
427
+ return 0; /* they should have compared equal before. */
428
+ if (!FIOBJ_IS_ALLOCATED(o) || !FIOBJ_IS_ALLOCATED(o2))
429
+ return 0; /* they should have compared equal before. */
430
+ if (FIOBJECT2HEAD(o)->type != FIOBJECT2HEAD(o2)->type)
431
+ return 0; /* non-type equality is a barriar to equality. */
432
+ if (!FIOBJECT2VTBL(o)->is_eq(o, o2))
433
+ return 0;
434
+ return 1;
435
+ }
436
+
437
+ static int fiobj_iseq____internal_complex__task(FIOBJ o, void *ary_) {
438
+ fiobj_stack_s *ary = ary_;
439
+ fiobj_stack_push(ary, o);
440
+ if (fiobj_hash_key_in_loop())
441
+ fiobj_stack_push(ary, fiobj_hash_key_in_loop());
442
+ return 0;
443
+ }
444
+
445
+ /** used internally for complext nested tests (Array / Hash types) */
446
+ int fiobj_iseq____internal_complex__(FIOBJ o, FIOBJ o2) {
447
+ // if (FIOBJECT2VTBL(o)->each && FIOBJECT2VTBL(o)->count(o))
448
+ // return int fiobj_iseq____internal_complex__(const FIOBJ o, const FIOBJ
449
+ // o2);
450
+ fiobj_stack_s left = FIO_ARY_INIT, right = FIO_ARY_INIT, queue = FIO_ARY_INIT;
451
+ do {
452
+ fiobj_each1(o, 0, fiobj_iseq____internal_complex__task, &left);
453
+ fiobj_each1(o2, 0, fiobj_iseq____internal_complex__task, &right);
454
+ while (fiobj_stack_count(&left)) {
455
+ o = FIOBJ_INVALID;
456
+ o2 = FIOBJ_INVALID;
457
+ fiobj_stack_pop(&left, &o);
458
+ fiobj_stack_pop(&right, &o2);
459
+ if (!fiobj_iseq_simple(o, o2))
460
+ goto unequal;
461
+ if (FIOBJ_IS_ALLOCATED(o) && FIOBJECT2VTBL(o)->each &&
462
+ FIOBJECT2VTBL(o)->count(o)) {
463
+ fiobj_stack_push(&queue, o);
464
+ fiobj_stack_push(&queue, o2);
465
+ }
466
+ }
467
+ o = FIOBJ_INVALID;
468
+ o2 = FIOBJ_INVALID;
469
+ fiobj_stack_pop(&queue, &o2);
470
+ fiobj_stack_pop(&queue, &o);
471
+ if (!fiobj_iseq_simple(o, o2))
472
+ goto unequal;
473
+ } while (o);
474
+ fiobj_stack_free(&left);
475
+ fiobj_stack_free(&right);
476
+ fiobj_stack_free(&queue);
477
+ return 1;
478
+ unequal:
479
+ fiobj_stack_free(&left);
480
+ fiobj_stack_free(&right);
481
+ fiobj_stack_free(&queue);
482
+ return 0;
483
+ }
484
+
485
+ /* *****************************************************************************
486
+ Defaults / NOOPs
487
+ ***************************************************************************** */
488
+
489
+ void fiobject___noop_dealloc(FIOBJ o, void (*task)(FIOBJ, void *), void *arg) {
490
+ (void)o;
491
+ (void)task;
492
+ (void)arg;
493
+ }
494
+ void fiobject___simple_dealloc(FIOBJ o, void (*task)(FIOBJ, void *),
495
+ void *arg) {
496
+ fio_free(FIOBJ2PTR(o));
497
+ (void)task;
498
+ (void)arg;
499
+ }
500
+
501
+ uintptr_t fiobject___noop_count(const FIOBJ o) {
502
+ (void)o;
503
+ return 0;
504
+ }
505
+ size_t fiobject___noop_is_eq(const FIOBJ o1, const FIOBJ o2) {
506
+ (void)o1;
507
+ (void)o2;
508
+ return 0;
509
+ }
510
+
511
+ fio_str_info_s fiobject___noop_to_str(const FIOBJ o) {
512
+ (void)o;
513
+ return (fio_str_info_s){.len = 0, .data = NULL};
514
+ }
515
+ intptr_t fiobject___noop_to_i(const FIOBJ o) {
516
+ (void)o;
517
+ return 0;
518
+ }
519
+ double fiobject___noop_to_f(const FIOBJ o) {
520
+ (void)o;
521
+ return 0;
522
+ }
523
+
524
+ #if DEBUG
525
+
526
+ #include <fiobj_ary.h>
527
+ #include <fiobj_numbers.h>
528
+
529
+ static int fiobject_test_task(FIOBJ o, void *arg) {
530
+ ++((uintptr_t *)arg)[0];
531
+ if (!o)
532
+ fprintf(stderr, "* WARN: counting a NULL no-object\n");
533
+ // else
534
+ // fprintf(stderr, "* counting object %s\n", fiobj_obj2cstr(o).data);
535
+ return 0;
536
+ (void)o;
537
+ }
538
+
539
+ void fiobj_test_core(void) {
540
+ #define TEST_ASSERT(cond, ...) \
541
+ if (!(cond)) { \
542
+ fprintf(stderr, __VA_ARGS__); \
543
+ fprintf(stderr, "Testing failed.\n"); \
544
+ exit(-1); \
545
+ }
546
+ fprintf(stderr, "=== Testing Primitives\n");
547
+ FIOBJ o = fiobj_null();
548
+ TEST_ASSERT(o == (FIOBJ)FIOBJ_T_NULL, "fiobj_null isn't NULL!\n");
549
+ TEST_ASSERT(FIOBJ_TYPE(0) == FIOBJ_T_NULL, "NULL isn't NULL!\n");
550
+ TEST_ASSERT(FIOBJ_TYPE_IS(0, FIOBJ_T_NULL), "NULL isn't NULL! (2)\n");
551
+ TEST_ASSERT(!FIOBJ_IS_ALLOCATED(fiobj_null()),
552
+ "fiobj_null claims to be allocated!\n");
553
+ TEST_ASSERT(!FIOBJ_IS_ALLOCATED(fiobj_true()),
554
+ "fiobj_true claims to be allocated!\n");
555
+ TEST_ASSERT(!FIOBJ_IS_ALLOCATED(fiobj_false()),
556
+ "fiobj_false claims to be allocated!\n");
557
+ TEST_ASSERT(FIOBJ_TYPE(fiobj_true()) == FIOBJ_T_TRUE,
558
+ "fiobj_true isn't FIOBJ_T_TRUE!\n");
559
+ TEST_ASSERT(FIOBJ_TYPE_IS(fiobj_true(), FIOBJ_T_TRUE),
560
+ "fiobj_true isn't FIOBJ_T_TRUE! (2)\n");
561
+ TEST_ASSERT(FIOBJ_TYPE(fiobj_false()) == FIOBJ_T_FALSE,
562
+ "fiobj_false isn't FIOBJ_T_TRUE!\n");
563
+ TEST_ASSERT(FIOBJ_TYPE_IS(fiobj_false(), FIOBJ_T_FALSE),
564
+ "fiobj_false isn't FIOBJ_T_TRUE! (2)\n");
565
+ fiobj_free(o); /* testing for crash*/
566
+ fprintf(stderr, "* passed.\n");
567
+ fprintf(stderr, "=== Testing fioj_each2\n");
568
+ o = fiobj_ary_new2(4);
569
+ FIOBJ tmp = fiobj_ary_new();
570
+ fiobj_ary_push(o, tmp);
571
+ fiobj_ary_push(o, fiobj_true());
572
+ fiobj_ary_push(o, fiobj_null());
573
+ fiobj_ary_push(o, fiobj_num_new(10));
574
+ fiobj_ary_push(tmp, fiobj_num_new(13));
575
+ fiobj_ary_push(tmp, fiobj_hash_new());
576
+ FIOBJ key = fiobj_str_new("my key", 6);
577
+ fiobj_hash_set(fiobj_ary_entry(tmp, -1), key, fiobj_true());
578
+ fiobj_free(key);
579
+ /* we have root array + 4 children (w/ array) + 2 children (w/ hash) + 1 */
580
+ uintptr_t count = 0;
581
+ size_t each_ret = 0;
582
+ TEST_ASSERT(fiobj_each2(o, fiobject_test_task, (void *)&count) == 8,
583
+ "fiobj_each1 didn't count everything... (%d != %d)", (int)count,
584
+ (int)each_ret);
585
+ TEST_ASSERT(count == 8, "Something went wrong with the counter task... (%d)",
586
+ (int)count)
587
+ fprintf(stderr, "* passed.\n");
588
+ fprintf(stderr, "=== Testing fioj_iseq with nested items\n");
589
+ FIOBJ o2 = fiobj_ary_new2(4);
590
+ tmp = fiobj_ary_new();
591
+ fiobj_ary_push(o2, tmp);
592
+ fiobj_ary_push(o2, fiobj_true());
593
+ fiobj_ary_push(o2, fiobj_null());
594
+ fiobj_ary_push(o2, fiobj_num_new(10));
595
+ fiobj_ary_push(tmp, fiobj_num_new(13));
596
+ fiobj_ary_push(tmp, fiobj_hash_new());
597
+ key = fiobj_str_new("my key", 6);
598
+ fiobj_hash_set(fiobj_ary_entry(tmp, -1), key, fiobj_true());
599
+ fiobj_free(key);
600
+ TEST_ASSERT(!fiobj_iseq(o, FIOBJ_INVALID),
601
+ "Array and FIOBJ_INVALID can't be equal!");
602
+ TEST_ASSERT(!fiobj_iseq(o, fiobj_null()),
603
+ "Array and fiobj_null can't be equal!");
604
+ TEST_ASSERT(fiobj_iseq(o, o2), "Arrays aren't euqal!");
605
+ fiobj_free(o);
606
+ fiobj_free(o2);
607
+ TEST_ASSERT(fiobj_iseq(fiobj_null(), fiobj_null()),
608
+ "fiobj_null() not equal to self!");
609
+ TEST_ASSERT(fiobj_iseq(fiobj_false(), fiobj_false()),
610
+ "fiobj_false() not equal to self!");
611
+ TEST_ASSERT(fiobj_iseq(fiobj_true(), fiobj_true()),
612
+ "fiobj_true() not equal to self!");
613
+ TEST_ASSERT(!fiobj_iseq(fiobj_null(), fiobj_false()),
614
+ "fiobj_null eqal to fiobj_false!");
615
+ TEST_ASSERT(!fiobj_iseq(fiobj_null(), fiobj_true()),
616
+ "fiobj_null eqal to fiobj_true!");
617
+ fprintf(stderr, "* passed.\n");
618
+ }
619
+
620
+ #endif