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,81 @@
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
+
8
+ /* *****************************************************************************
9
+ This file handles deallocation concerns.
10
+
11
+ Since Hash and Array objects will need to deallocate their children, the logic
12
+ regareding object type recorgnition and deallocation was centrelized here.
13
+ ***************************************************************************** */
14
+ #include "fiobj_types.h"
15
+
16
+ /* *****************************************************************************
17
+ Object Deallocation
18
+ ***************************************************************************** */
19
+
20
+ void fiobj_dealloc(fiobj_s *obj) {
21
+ if (!obj)
22
+ return;
23
+ if (OBJ2HEAD(obj).ref == 0) {
24
+ fprintf(stderr,
25
+ "ERROR: attempting to free an object that isn't a fiobj or already "
26
+ "freed (%p)\n",
27
+ (void *)obj);
28
+ kill(0, SIGABRT);
29
+ }
30
+ if (spn_sub(&OBJ2HEAD(obj).ref, 1))
31
+ return;
32
+ OBJ2HEAD(obj).vtable->free(obj);
33
+ }
34
+
35
+ /* *****************************************************************************
36
+ Deep Allocation (`fiobj_dup`)
37
+ ***************************************************************************** */
38
+
39
+ /* simply increrase the reference count for each object. */
40
+ static int dup_task_callback(fiobj_s *obj, void *arg) {
41
+ if (!obj)
42
+ return 0;
43
+ spn_add(&OBJ2HEAD(obj).ref, 1);
44
+ // if (obj->type == FIOBJ_T_COUPLET)
45
+ // spn_add(&OBJ2HEAD(obj2couplet(obj)->obj).ref, 1);
46
+ return 0;
47
+ (void)arg;
48
+ }
49
+
50
+ /** Increases an object's reference count. */
51
+ fiobj_s *fiobj_dup(fiobj_s *obj) {
52
+ fiobj_each2(obj, dup_task_callback, NULL);
53
+ return obj;
54
+ }
55
+
56
+ /* *****************************************************************************
57
+ Deep Deallocation (`fiobj_free`)
58
+ ***************************************************************************** */
59
+
60
+ static int dealloc_task_callback(fiobj_s *obj, void *arg) {
61
+ // if (!obj)
62
+ // return 0;
63
+ // if (obj->type == FIOBJ_T_COUPLET)
64
+ // fiobj_dealloc(obj2couplet(obj)->obj);
65
+ fiobj_dealloc(obj);
66
+ return 0;
67
+ (void)arg;
68
+ }
69
+ /**
70
+ * Decreases an object's reference count, releasing memory and
71
+ * resources.
72
+ *
73
+ * This function affects nested objects, meaning that when an Array or
74
+ * a Hashe object is passed along, it's children (nested objects) are
75
+ * also freed.
76
+ */
77
+ void fiobj_free(fiobj_s *obj) {
78
+ if (!obj)
79
+ return;
80
+ fiobj_each2(obj, dealloc_task_callback, NULL);
81
+ }
@@ -0,0 +1,290 @@
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
+
8
+ #include "fiobj_types.h"
9
+
10
+ /* *****************************************************************************
11
+ Array memory management
12
+ ***************************************************************************** */
13
+
14
+ /* This funcation manages the Array's memory. */
15
+ static void fiobj_ary_getmem(fiobj_s *ary, int64_t needed) {
16
+ /* we have enough memory, but we need to re-organize it. */
17
+ if (needed == -1) {
18
+ if (obj2ary(ary)->end < obj2ary(ary)->capa) {
19
+ /* since allocation can be cheaper than memmove (depending on size),
20
+ * we'll just shove everything to the end...
21
+ */
22
+ uint64_t len = obj2ary(ary)->end - obj2ary(ary)->start;
23
+ memmove(obj2ary(ary)->arry + (obj2ary(ary)->capa - len),
24
+ obj2ary(ary)->arry + obj2ary(ary)->start,
25
+ len * sizeof(*obj2ary(ary)->arry));
26
+ obj2ary(ary)->start = obj2ary(ary)->capa - len;
27
+ obj2ary(ary)->end = obj2ary(ary)->capa;
28
+
29
+ return;
30
+ }
31
+ /* add some breathing room for future `unshift`s */
32
+ needed =
33
+ 0 - ((obj2ary(ary)->capa <= 1024) ? (obj2ary(ary)->capa >> 1) : 1024);
34
+ }
35
+
36
+ /* FIFO support optimizes smaller FIFO ranges over bloating allocations. */
37
+ if (needed == 1 && obj2ary(ary)->start >= (obj2ary(ary)->capa >> 1)) {
38
+ uint64_t len = obj2ary(ary)->end - obj2ary(ary)->start;
39
+ memmove(obj2ary(ary)->arry + 2, obj2ary(ary)->arry + obj2ary(ary)->start,
40
+ len * sizeof(*obj2ary(ary)->arry));
41
+ obj2ary(ary)->start = 2;
42
+ obj2ary(ary)->end = len + 2;
43
+
44
+ return;
45
+ }
46
+
47
+ /* alocate using exponential growth, up to single page size. */
48
+ uint64_t updated_capa = obj2ary(ary)->capa;
49
+ uint64_t minimum =
50
+ obj2ary(ary)->capa + ((needed < 0) ? (0 - needed) : needed);
51
+ while (updated_capa <= minimum)
52
+ updated_capa =
53
+ (updated_capa <= 4096) ? (updated_capa << 1) : (updated_capa + 4096);
54
+
55
+ /* we assume memory allocation works. it's better to crash than to continue
56
+ * living without memory... besides, malloc is optimistic these days. */
57
+ obj2ary(ary)->arry =
58
+ realloc(obj2ary(ary)->arry, updated_capa * sizeof(*obj2ary(ary)->arry));
59
+ obj2ary(ary)->capa = updated_capa;
60
+ if (!obj2ary(ary)->arry)
61
+ perror("ERROR: fiobj array couldn't be reallocated"), exit(errno);
62
+
63
+ if (needed >= 0) /* we're done, realloc grows the top of the address space*/
64
+ return;
65
+
66
+ /* move everything to the max, since memmove could get expensive */
67
+ uint64_t len = obj2ary(ary)->end - obj2ary(ary)->start;
68
+ needed = obj2ary(ary)->capa - len;
69
+ memmove(obj2ary(ary)->arry + needed, obj2ary(ary)->arry + obj2ary(ary)->start,
70
+ len * sizeof(*obj2ary(ary)->arry));
71
+ obj2ary(ary)->end = needed + len;
72
+ obj2ary(ary)->start = needed;
73
+ }
74
+
75
+ /* *****************************************************************************
76
+ VTable
77
+ ***************************************************************************** */
78
+
79
+ static void fiobj_ary_dealloc(fiobj_s *a) {
80
+ free(obj2ary(a)->arry);
81
+ free(&OBJ2HEAD(a));
82
+ }
83
+
84
+ static size_t fiobj_ary_each1(fiobj_s *o, size_t start_at,
85
+ int (*task)(fiobj_s *obj, void *arg), void *arg) {
86
+ const uint64_t start_pos = obj2ary(o)->start;
87
+ start_at += start_pos;
88
+ while (start_at < obj2ary(o)->end &&
89
+ task(obj2ary(o)->arry[start_at++], arg) != -1)
90
+ ;
91
+ return start_at - start_pos;
92
+ }
93
+
94
+ static int fiobj_ary_is_eq(fiobj_s *self, fiobj_s *other) {
95
+ return (other && other->type == FIOBJ_T_ARRAY &&
96
+ obj2ary(self)->end - obj2ary(self)->start ==
97
+ obj2ary(other)->end - obj2ary(other)->start);
98
+ }
99
+
100
+ /** Returns the number of elements in the Array. */
101
+ static size_t fiobj_ary_count_items(fiobj_s *ary) {
102
+ return (obj2ary(ary)->end - obj2ary(ary)->start);
103
+ }
104
+
105
+ static struct fiobj_vtable_s FIOBJ_VTABLE_ARRAY = {
106
+ .free = fiobj_ary_dealloc,
107
+ .to_i = fiobj_noop_i,
108
+ .to_f = fiobj_noop_f,
109
+ .to_str = fiobj_noop_str,
110
+ .is_eq = fiobj_ary_is_eq,
111
+ .count = fiobj_ary_count_items,
112
+ .each1 = fiobj_ary_each1,
113
+ };
114
+ /* *****************************************************************************
115
+ Allocation
116
+ ***************************************************************************** */
117
+
118
+ static fiobj_s *fiobj_ary_alloc(size_t capa, size_t start_at) {
119
+ fiobj_head_s *head;
120
+ head = malloc(sizeof(*head) + sizeof(fio_ary_s));
121
+ if (!head)
122
+ perror("ERROR: fiobj array couldn't allocate memory"), exit(errno);
123
+ *head = (fiobj_head_s){
124
+ .ref = 1, .vtable = &FIOBJ_VTABLE_ARRAY,
125
+ };
126
+ *((fio_ary_s *)HEAD2OBJ(head)) =
127
+ (fio_ary_s){.start = start_at,
128
+ .end = start_at,
129
+ .capa = capa,
130
+ .arry = malloc(sizeof(fiobj_s *) * capa),
131
+ .type = FIOBJ_T_ARRAY};
132
+ return HEAD2OBJ(head);
133
+ }
134
+
135
+ /** Creates a mutable empty Array object. Use `fiobj_free` when done. */
136
+ fiobj_s *fiobj_ary_new(void) { return fiobj_ary_alloc(32, 8); }
137
+ /** Creates a mutable empty Array object with the requested capacity. */
138
+ fiobj_s *fiobj_ary_new2(size_t capa) { return fiobj_ary_alloc(capa, 0); }
139
+
140
+ /** Returns the number of elements in the Array. */
141
+ size_t fiobj_ary_count(fiobj_s *ary) {
142
+ if (!ary)
143
+ return 0;
144
+ return (obj2ary(ary)->end - obj2ary(ary)->start);
145
+ }
146
+
147
+ /* *****************************************************************************
148
+ Array direct entry access API
149
+ ***************************************************************************** */
150
+
151
+ /**
152
+ * Returns a temporary object owned by the Array.
153
+ *
154
+ * Negative values are retrived from the end of the array. i.e., `-1`
155
+ * is the last item.
156
+ */
157
+ fiobj_s *fiobj_ary_entry(fiobj_s *ary, int64_t pos) {
158
+ if (!ary || ary->type != FIOBJ_T_ARRAY)
159
+ return NULL;
160
+ /* position is relative to `start`*/
161
+ if (pos >= 0) {
162
+ pos = pos + obj2ary(ary)->start;
163
+ if ((uint64_t)pos >= obj2ary(ary)->end)
164
+ return NULL;
165
+ return obj2ary(ary)->arry[pos];
166
+ }
167
+ /* position is relative to `end`*/
168
+ pos = (int64_t)obj2ary(ary)->end + pos;
169
+ if (pos < 0)
170
+ return NULL;
171
+ if ((uint64_t)pos < obj2ary(ary)->start)
172
+ return NULL;
173
+ return obj2ary(ary)->arry[pos];
174
+ }
175
+
176
+ /**
177
+ * Sets an object at the requested position.
178
+ */
179
+ void fiobj_ary_set(fiobj_s *ary, fiobj_s *obj, int64_t pos) {
180
+ if (!ary || ary->type != FIOBJ_T_ARRAY) {
181
+ /* function takes ownership of memory even if an error occurs. */
182
+ fiobj_free(obj);
183
+ return;
184
+ }
185
+
186
+ /* test for memory and request memory if missing, promises valid bounds. */
187
+ if (pos >= 0) {
188
+ if ((uint64_t)pos + obj2ary(ary)->start >= obj2ary(ary)->capa)
189
+ fiobj_ary_getmem(ary, (((uint64_t)pos + obj2ary(ary)->start) -
190
+ (obj2ary(ary)->capa - 1)));
191
+ } else if (pos + (int64_t)obj2ary(ary)->end < 0)
192
+ fiobj_ary_getmem(ary, pos + obj2ary(ary)->end);
193
+
194
+ if (pos >= 0) {
195
+ /* position relative to start */
196
+ pos = pos + obj2ary(ary)->start;
197
+ /* initialize empty spaces, if any, setting new boundries */
198
+ while ((uint64_t)pos >= obj2ary(ary)->end)
199
+ obj2ary(ary)->arry[(obj2ary(ary)->end)++] = NULL;
200
+ } else {
201
+ /* position relative to end */
202
+ pos = pos + (int64_t)obj2ary(ary)->end;
203
+ /* initialize empty spaces, if any, setting new boundries */
204
+ while (obj2ary(ary)->start > (uint64_t)pos)
205
+ obj2ary(ary)->arry[--(obj2ary(ary)->start)] = NULL;
206
+ }
207
+
208
+ /* check for an existing object and set new objects */
209
+ if (obj2ary(ary)->arry[pos])
210
+ fiobj_free(obj2ary(ary)->arry[pos]);
211
+ obj2ary(ary)->arry[pos] = obj;
212
+ }
213
+
214
+ /* *****************************************************************************
215
+ Array push / shift API
216
+ ***************************************************************************** */
217
+
218
+ /**
219
+ * Pushes an object to the end of the Array.
220
+ */
221
+ void fiobj_ary_push(fiobj_s *ary, fiobj_s *obj) {
222
+ if (!ary || ary->type != FIOBJ_T_ARRAY) {
223
+ /* function takes ownership of memory even if an error occurs. */
224
+ fiobj_free(obj);
225
+ return;
226
+ }
227
+ if (obj2ary(ary)->capa <= obj2ary(ary)->end)
228
+ fiobj_ary_getmem(ary, 1);
229
+ obj2ary(ary)->arry[(obj2ary(ary)->end)++] = obj;
230
+ }
231
+
232
+ /** Pops an object from the end of the Array. */
233
+ fiobj_s *fiobj_ary_pop(fiobj_s *ary) {
234
+ if (!ary || ary->type != FIOBJ_T_ARRAY ||
235
+ obj2ary(ary)->start == obj2ary(ary)->end)
236
+ return NULL;
237
+ fiobj_s *ret = obj2ary(ary)->arry[--(obj2ary(ary)->end)];
238
+ return ret;
239
+ }
240
+
241
+ /**
242
+ * Unshifts an object to the begining of the Array. This could be
243
+ * expensive.
244
+ */
245
+ void fiobj_ary_unshift(fiobj_s *ary, fiobj_s *obj) {
246
+ if (!ary || ary->type != FIOBJ_T_ARRAY) {
247
+ /* function takes ownership of memory even if an error occurs. */
248
+ fiobj_free(obj);
249
+ return;
250
+ }
251
+ if (obj2ary(ary)->start == 0)
252
+ fiobj_ary_getmem(ary, -1);
253
+ obj2ary(ary)->arry[--(obj2ary(ary)->start)] = obj;
254
+ }
255
+
256
+ /** Shifts an object from the beginning of the Array. */
257
+ fiobj_s *fiobj_ary_shift(fiobj_s *ary) {
258
+ if (!ary || ary->type != FIOBJ_T_ARRAY)
259
+ return NULL;
260
+ if (obj2ary(ary)->start == obj2ary(ary)->end)
261
+ return NULL;
262
+ fiobj_s *ret = obj2ary(ary)->arry[(obj2ary(ary)->start)++];
263
+ return ret;
264
+ }
265
+
266
+ /* *****************************************************************************
267
+ Array compacting (untested)
268
+ ***************************************************************************** */
269
+
270
+ /**
271
+ * Removes any NULL *pointers* from an Array, keeping all Objects (including
272
+ * explicit NULL objects) in the array.
273
+ *
274
+ * This action is O(n) where n in the length of the array.
275
+ * It could get expensive.
276
+ */
277
+ void fiobj_ary_compact(fiobj_s *ary) {
278
+ if (!ary || ary->type != FIOBJ_T_ARRAY)
279
+ return;
280
+ fiobj_s **reader = obj2ary(ary)->arry + obj2ary(ary)->start;
281
+ fiobj_s **writer = obj2ary(ary)->arry + obj2ary(ary)->start;
282
+ while (reader < (obj2ary(ary)->arry + obj2ary(ary)->end)) {
283
+ if (*reader == NULL) {
284
+ reader++;
285
+ continue;
286
+ }
287
+ *(writer++) = *(reader++);
288
+ }
289
+ obj2ary(ary)->end = (uint64_t)(writer - obj2ary(ary)->arry);
290
+ }
@@ -0,0 +1,260 @@
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
+
8
+ #include "fiobj_types.h"
9
+
10
+ /* *****************************************************************************
11
+ No-op for vtable
12
+ ***************************************************************************** */
13
+
14
+ fio_cstr_s fiobj_noop_str(fiobj_s *obj) {
15
+ return (fio_cstr_s){.data = NULL};
16
+ (void)obj;
17
+ }
18
+ int64_t fiobj_noop_i(fiobj_s *obj) {
19
+ return 0;
20
+ (void)obj;
21
+ }
22
+ double fiobj_noop_f(fiobj_s *obj) {
23
+ return 0;
24
+ (void)obj;
25
+ }
26
+
27
+ size_t fiobj_noop_count(fiobj_s *obj) {
28
+ return 0;
29
+ (void)obj;
30
+ }
31
+
32
+ size_t fiobj_noop_each1(fiobj_s *obj, size_t start_at,
33
+ int (*task)(fiobj_s *obj, void *arg), void *arg) {
34
+ return 0;
35
+ (void)obj;
36
+ (void)start_at;
37
+ (void)task;
38
+ (void)arg;
39
+ }
40
+
41
+ void fiobj_simple_dealloc(fiobj_s *o) { free(&OBJ2HEAD(o)); }
42
+
43
+ /* *****************************************************************************
44
+ Generic Object API
45
+ ***************************************************************************** */
46
+
47
+ /**
48
+ * Returns a Number's value.
49
+ *
50
+ * If a String or Symbol are passed to the function, they will be
51
+ * parsed assuming base 10 numerical data.
52
+ *
53
+ * A type error results in 0.
54
+ */
55
+ int64_t fiobj_obj2num(fiobj_s *obj) {
56
+ if (!obj)
57
+ return 0;
58
+ return OBJ2HEAD(obj).vtable->to_i(obj);
59
+ }
60
+
61
+ /**
62
+ * Returns a Float's value.
63
+ *
64
+ * If a String or Symbol are passed to the function, they will be
65
+ * parsed assuming base 10 numerical data.
66
+ *
67
+ * A type error results in 0.
68
+ */
69
+ double fiobj_obj2float(fiobj_s *obj) {
70
+ if (!obj)
71
+ return 0;
72
+ return OBJ2HEAD(obj).vtable->to_f(obj);
73
+ }
74
+
75
+ /**
76
+ * Returns a C String (NUL terminated) using the `fio_cstr_s` data type.
77
+ */
78
+ fio_cstr_s fiobj_obj2cstr(fiobj_s *obj) {
79
+ if (!obj)
80
+ return (fio_cstr_s){.buffer = NULL, .len = 0};
81
+ return OBJ2HEAD(obj).vtable->to_str(obj);
82
+ }
83
+
84
+ /**
85
+ * Single layer iteration using a callback for each nested fio object.
86
+ */
87
+ size_t fiobj_each1(fiobj_s *obj, size_t start_at,
88
+ int (*task)(fiobj_s *obj, void *arg), void *arg) {
89
+ return OBJ2HEAD(obj).vtable->each1(obj, start_at, task, arg);
90
+ }
91
+
92
+ /* *****************************************************************************
93
+ Object Iteration (`fiobj_each2`)
94
+ ***************************************************************************** */
95
+
96
+ static __thread fiobj_s *fiobj_cyclic_protection = NULL;
97
+ fiobj_s *fiobj_each_get_cyclic(void) { return fiobj_cyclic_protection; }
98
+
99
+ static uint8_t already_processed(fiobj_s *nested, fiobj_s *obj) {
100
+ #if FIOBJ_NESTING_PROTECTION
101
+ size_t end = obj2ary(nested)->end;
102
+ for (size_t i = obj2ary(nested)->start; i < end; i++) {
103
+ if (obj2ary(nested)->arry[i] == obj)
104
+ return 1;
105
+ }
106
+ #endif
107
+ OBJREF_ADD(obj);
108
+ fiobj_ary_push(nested, obj);
109
+ return 0;
110
+ }
111
+
112
+ struct fiobj_each2_task_s {
113
+ fiobj_s *nested;
114
+ int (*task)(fiobj_s *obj, void *arg);
115
+ void *arg;
116
+ fiobj_s *child;
117
+ };
118
+
119
+ int fiobj_each2_task_wrapper(fiobj_s *obj, void *data_) {
120
+ struct fiobj_each2_task_s *data = data_;
121
+ if (!obj || !OBJ2HEAD(obj).vtable->count(obj))
122
+ return data->task(obj, data->arg);
123
+
124
+ data->child = obj->type == FIOBJ_T_COUPLET ? obj2couplet(obj)->obj : obj;
125
+ if (already_processed(data->nested, data->child)) {
126
+ data->child = NULL;
127
+ fiobj_cyclic_protection = obj;
128
+ int ret = data->task(NULL, data->arg);
129
+ fiobj_cyclic_protection = NULL;
130
+ return ret;
131
+ }
132
+ data->task(obj, data->arg);
133
+ return -1;
134
+ }
135
+ /**
136
+ * Deep itteration using a callback for each fio object, including the parent.
137
+ * If the callback returns -1, the loop is broken. Any other value is ignored.
138
+ */
139
+ void fiobj_each2(fiobj_s *obj, int (*task)(fiobj_s *obj, void *arg),
140
+ void *arg) {
141
+ /* optimize simple items */
142
+ if (!obj || OBJ2HEAD(obj).vtable->count(obj) == 0) {
143
+ task(obj, arg);
144
+ return;
145
+ }
146
+ /* Prepare for layerd iteration */
147
+ uintptr_t count = 0;
148
+ fiobj_s *nested = fiobj_ary_new2(64);
149
+ fiobj_s *state = fiobj_ary_new2(128);
150
+ struct fiobj_each2_task_s data = {.task = task, .arg = arg, .nested = nested};
151
+ OBJREF_ADD(obj);
152
+ fiobj_ary_push(nested, obj);
153
+ if (task(obj, arg) == -1)
154
+ goto finish;
155
+ rebase:
156
+ /* resume ? */
157
+ data.child = NULL;
158
+ count =
159
+ OBJ2HEAD(obj).vtable->each1(obj, count, fiobj_each2_task_wrapper, &data);
160
+ if (data.child) {
161
+ fiobj_ary_push(state, obj);
162
+ fiobj_ary_push(state, (void *)count);
163
+ count = 0;
164
+ obj = data.child;
165
+ goto rebase;
166
+ }
167
+
168
+ /* any more nested layers left to handle? */
169
+ if (fiobj_ary_count(state)) {
170
+ count = (uintptr_t)fiobj_ary_pop(state);
171
+ obj = fiobj_ary_pop(state);
172
+ goto rebase;
173
+ }
174
+
175
+ finish:
176
+ while ((obj = fiobj_ary_pop(nested)))
177
+ fiobj_dealloc(obj);
178
+ fiobj_dealloc(nested);
179
+ fiobj_dealloc(state);
180
+ return;
181
+ }
182
+
183
+ /* *****************************************************************************
184
+ Object Comparison (`fiobj_iseq`)
185
+ ***************************************************************************** */
186
+
187
+ static inline int fiobj_iseq_check(fiobj_s *obj1, fiobj_s *obj2) {
188
+ if (obj1 == obj2)
189
+ return 1;
190
+ if (!obj1 || !obj2)
191
+ return 0;
192
+ return OBJ2HEAD(obj1).vtable->is_eq(obj1, obj2);
193
+ }
194
+
195
+ struct fiobj_iseq_data_s {
196
+ fiobj_s *root;
197
+ fiobj_s *nested;
198
+ fiobj_s *parallel;
199
+ uintptr_t count;
200
+ uint8_t is_hash;
201
+ uint8_t err;
202
+ };
203
+
204
+ int fiobj_iseq_task(fiobj_s *obj, void *data_) {
205
+ struct fiobj_iseq_data_s *data = data_;
206
+ if (fiobj_cyclic_protection)
207
+ obj = fiobj_cyclic_protection;
208
+
209
+ if (data->root == obj)
210
+ return 0;
211
+
212
+ if (data->count >= OBJ2HEAD(data->root).vtable->count(data->root)) {
213
+ data->count = (uintptr_t)fiobj_ary_pop(data->nested);
214
+ data->parallel = fiobj_ary_pop(data->nested);
215
+ data->root = fiobj_ary_pop(data->nested);
216
+ data->is_hash = (data->root->type == FIOBJ_T_HASH);
217
+ }
218
+
219
+ fiobj_s *obj2;
220
+ if (data->is_hash) {
221
+ obj2 = fiobj_hash_get(data->parallel, fiobj_couplet2key(obj));
222
+ obj = fiobj_couplet2obj(obj);
223
+ } else {
224
+ obj2 = fiobj_ary_entry(data->parallel, data->count);
225
+ }
226
+ data->count++;
227
+ if (!fiobj_iseq_check(obj, obj2)) {
228
+ data->err = 1;
229
+ return -1;
230
+ }
231
+ if (!fiobj_cyclic_protection && OBJ2HEAD(obj).vtable->count(obj)) {
232
+ fiobj_ary_push(data->nested, data->root);
233
+ fiobj_ary_push(data->nested, data->parallel);
234
+ fiobj_ary_push(data->nested, (fiobj_s *)data->count);
235
+ data->count = 0;
236
+ data->root = obj;
237
+ data->parallel = obj2;
238
+ data->is_hash = (obj->type == FIOBJ_T_HASH);
239
+ }
240
+ return 0;
241
+ }
242
+
243
+ int fiobj_iseq(fiobj_s *obj1, fiobj_s *obj2) {
244
+ if (OBJ2HEAD(obj1).vtable->count(obj1) == 0)
245
+ return fiobj_iseq_check(obj1, obj2);
246
+ if (!fiobj_iseq_check(obj1, obj2))
247
+ return 0;
248
+ struct fiobj_iseq_data_s data = {
249
+ .root = obj1,
250
+ .nested = fiobj_ary_new2(128),
251
+ .is_hash = obj1->type == FIOBJ_T_HASH,
252
+ // .count = OBJ2HEAD(obj1).vtable->count(obj1),
253
+ .parallel = obj2,
254
+ };
255
+ // fiobj_ary_push(data.nested, obj2);
256
+ // fiobj_ary_push(data.nested, (fiobj_s *)OBJ2HEAD(obj1).vtable->count(obj1));
257
+ fiobj_each2(obj1, fiobj_iseq_task, &data);
258
+ fiobj_dealloc(data.nested);
259
+ return data.err == 0;
260
+ }