isomorfeus-iodine 0.7.44

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  3. data/.gitignore +20 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +32 -0
  6. data/.yardopts +8 -0
  7. data/CHANGELOG.md +1038 -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 +44 -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/config.ru +56 -0
  25. data/examples/echo.ru +59 -0
  26. data/examples/hello.ru +29 -0
  27. data/examples/pubsub_engine.ru +81 -0
  28. data/examples/redis.ru +70 -0
  29. data/examples/shootout.ru +73 -0
  30. data/examples/sub-protocols.ru +90 -0
  31. data/examples/tcp_client.rb +66 -0
  32. data/examples/x-sendfile.ru +14 -0
  33. data/exe/iodine +277 -0
  34. data/ext/iodine/extconf.rb +109 -0
  35. data/ext/iodine/fio.c +11985 -0
  36. data/ext/iodine/fio.h +6373 -0
  37. data/ext/iodine/fio_cli.c +431 -0
  38. data/ext/iodine/fio_cli.h +189 -0
  39. data/ext/iodine/fio_json_parser.h +687 -0
  40. data/ext/iodine/fio_siphash.c +157 -0
  41. data/ext/iodine/fio_siphash.h +37 -0
  42. data/ext/iodine/fio_tls.h +129 -0
  43. data/ext/iodine/fio_tls_missing.c +649 -0
  44. data/ext/iodine/fio_tls_openssl.c +1056 -0
  45. data/ext/iodine/fio_tmpfile.h +50 -0
  46. data/ext/iodine/fiobj.h +44 -0
  47. data/ext/iodine/fiobj4fio.h +21 -0
  48. data/ext/iodine/fiobj_ary.c +333 -0
  49. data/ext/iodine/fiobj_ary.h +139 -0
  50. data/ext/iodine/fiobj_data.c +1185 -0
  51. data/ext/iodine/fiobj_data.h +167 -0
  52. data/ext/iodine/fiobj_hash.c +409 -0
  53. data/ext/iodine/fiobj_hash.h +176 -0
  54. data/ext/iodine/fiobj_json.c +622 -0
  55. data/ext/iodine/fiobj_json.h +68 -0
  56. data/ext/iodine/fiobj_mem.h +71 -0
  57. data/ext/iodine/fiobj_mustache.c +317 -0
  58. data/ext/iodine/fiobj_mustache.h +62 -0
  59. data/ext/iodine/fiobj_numbers.c +344 -0
  60. data/ext/iodine/fiobj_numbers.h +127 -0
  61. data/ext/iodine/fiobj_str.c +433 -0
  62. data/ext/iodine/fiobj_str.h +172 -0
  63. data/ext/iodine/fiobject.c +620 -0
  64. data/ext/iodine/fiobject.h +654 -0
  65. data/ext/iodine/hpack.h +1923 -0
  66. data/ext/iodine/http.c +2754 -0
  67. data/ext/iodine/http.h +1002 -0
  68. data/ext/iodine/http1.c +912 -0
  69. data/ext/iodine/http1.h +29 -0
  70. data/ext/iodine/http1_parser.h +873 -0
  71. data/ext/iodine/http_internal.c +1278 -0
  72. data/ext/iodine/http_internal.h +237 -0
  73. data/ext/iodine/http_mime_parser.h +350 -0
  74. data/ext/iodine/iodine.c +1430 -0
  75. data/ext/iodine/iodine.h +63 -0
  76. data/ext/iodine/iodine_caller.c +218 -0
  77. data/ext/iodine/iodine_caller.h +27 -0
  78. data/ext/iodine/iodine_connection.c +933 -0
  79. data/ext/iodine/iodine_connection.h +55 -0
  80. data/ext/iodine/iodine_defer.c +420 -0
  81. data/ext/iodine/iodine_defer.h +6 -0
  82. data/ext/iodine/iodine_fiobj2rb.h +120 -0
  83. data/ext/iodine/iodine_helpers.c +282 -0
  84. data/ext/iodine/iodine_helpers.h +12 -0
  85. data/ext/iodine/iodine_http.c +1171 -0
  86. data/ext/iodine/iodine_http.h +23 -0
  87. data/ext/iodine/iodine_json.c +302 -0
  88. data/ext/iodine/iodine_json.h +6 -0
  89. data/ext/iodine/iodine_mustache.c +567 -0
  90. data/ext/iodine/iodine_mustache.h +6 -0
  91. data/ext/iodine/iodine_pubsub.c +580 -0
  92. data/ext/iodine/iodine_pubsub.h +26 -0
  93. data/ext/iodine/iodine_rack_io.c +281 -0
  94. data/ext/iodine/iodine_rack_io.h +20 -0
  95. data/ext/iodine/iodine_store.c +142 -0
  96. data/ext/iodine/iodine_store.h +20 -0
  97. data/ext/iodine/iodine_tcp.c +346 -0
  98. data/ext/iodine/iodine_tcp.h +13 -0
  99. data/ext/iodine/iodine_tls.c +261 -0
  100. data/ext/iodine/iodine_tls.h +13 -0
  101. data/ext/iodine/mustache_parser.h +1546 -0
  102. data/ext/iodine/redis_engine.c +957 -0
  103. data/ext/iodine/redis_engine.h +79 -0
  104. data/ext/iodine/resp_parser.h +317 -0
  105. data/ext/iodine/websocket_parser.h +505 -0
  106. data/ext/iodine/websockets.c +735 -0
  107. data/ext/iodine/websockets.h +185 -0
  108. data/isomorfeus-iodine.gemspec +42 -0
  109. data/lib/iodine/connection.rb +61 -0
  110. data/lib/iodine/json.rb +42 -0
  111. data/lib/iodine/mustache.rb +113 -0
  112. data/lib/iodine/pubsub.rb +55 -0
  113. data/lib/iodine/rack_utils.rb +43 -0
  114. data/lib/iodine/tls.rb +16 -0
  115. data/lib/iodine/version.rb +3 -0
  116. data/lib/iodine.rb +274 -0
  117. data/lib/rack/handler/iodine.rb +33 -0
  118. data/logo.png +0 -0
  119. metadata +271 -0
@@ -0,0 +1,654 @@
1
+ #ifndef H_FIOBJECT_H
2
+ /*
3
+ Copyright: Boaz Segev, 2017-2019
4
+ License: MIT
5
+ */
6
+
7
+ /**
8
+ This facil.io core library provides wrappers around complex and (or) dynamic
9
+ types, abstracting some complexity and making dynamic type related tasks easier.
10
+ */
11
+ #define H_FIOBJECT_H
12
+
13
+ #ifndef _GNU_SOURCE
14
+ #define _GNU_SOURCE
15
+ #endif
16
+
17
+ #include <stdarg.h>
18
+ #include <stdint.h>
19
+ #include <stdio.h>
20
+ #include <stdlib.h>
21
+
22
+ #include <limits.h>
23
+ #include <stdarg.h>
24
+ #include <stdint.h>
25
+ #include <stdio.h>
26
+ #include <stdlib.h>
27
+
28
+ #include <fio_siphash.h>
29
+
30
+ #include <fio.h>
31
+
32
+ #if !defined(__GNUC__) && !defined(__clang__) && !defined(FIO_GNUC_BYPASS)
33
+ #define __attribute__(...)
34
+ #define __has_include(...) 0
35
+ #define __has_builtin(...) 0
36
+ #define FIO_GNUC_BYPASS 1
37
+ #elif !defined(__clang__) && !defined(__has_builtin)
38
+ #define __has_builtin(...) 0
39
+ #define FIO_GNUC_BYPASS 1
40
+ #endif
41
+
42
+ #ifdef __cplusplus
43
+ extern "C" {
44
+ #endif
45
+
46
+ /* *****************************************************************************
47
+ Core Types
48
+ ***************************************************************************** */
49
+
50
+ typedef enum __attribute__((packed)) {
51
+ FIOBJ_T_NUMBER = 0x01,
52
+ FIOBJ_T_NULL = 0x06,
53
+ FIOBJ_T_TRUE = 0x16,
54
+ FIOBJ_T_FALSE = 0x26,
55
+ FIOBJ_T_FLOAT,
56
+ FIOBJ_T_STRING,
57
+ FIOBJ_T_ARRAY,
58
+ FIOBJ_T_HASH,
59
+ FIOBJ_T_DATA,
60
+ FIOBJ_T_UNKNOWN
61
+ } fiobj_type_enum;
62
+
63
+ typedef uintptr_t FIOBJ;
64
+
65
+ /** a Macro retriving an object's type. Use FIOBJ_TYPE_IS(x) for testing. */
66
+ #define FIOBJ_TYPE(obj) fiobj_type((obj))
67
+ #define FIOBJ_TYPE_IS(obj, type) fiobj_type_is((obj), (type))
68
+ #define FIOBJ_IS_NULL(obj) (!obj || obj == (FIOBJ)FIOBJ_T_NULL)
69
+ #define FIOBJ_INVALID 0
70
+
71
+ #ifndef FIO_STR_INFO_TYPE
72
+ /** A String information type, reports information about a C string. */
73
+ typedef struct fio_str_info_s {
74
+ size_t capa; /* Buffer capacity, if the string is writable. */
75
+ size_t len; /* String length. */
76
+ char *data; /* String's first byte. */
77
+ } fio_str_info_s;
78
+ #define FIO_STR_INFO_TYPE
79
+ #endif
80
+
81
+ /* *****************************************************************************
82
+ Primitives
83
+ ***************************************************************************** */
84
+
85
+ #define FIO_INLINE static inline __attribute__((unused))
86
+
87
+ FIO_INLINE FIOBJ fiobj_null(void) { return (FIOBJ)FIOBJ_T_NULL; }
88
+ FIO_INLINE FIOBJ fiobj_true(void) { return (FIOBJ)FIOBJ_T_TRUE; }
89
+ FIO_INLINE FIOBJ fiobj_false(void) { return (FIOBJ)FIOBJ_T_FALSE; }
90
+
91
+ /* *****************************************************************************
92
+ Generic Object API
93
+ ***************************************************************************** */
94
+
95
+ /** Returns a C string naming the objects dynamic type. */
96
+ FIO_INLINE const char *fiobj_type_name(const FIOBJ obj);
97
+
98
+ /**
99
+ * Heuristic copy with a preference for copy reference(!) to minimize
100
+ * allocations.
101
+ *
102
+ * Always returns the value passed along.
103
+ */
104
+ FIO_INLINE FIOBJ fiobj_dup(FIOBJ);
105
+
106
+ /**
107
+ * Frees the object and any of it's "children".
108
+ *
109
+ * This function affects nested objects, meaning that when an Array or
110
+ * a Hash object is passed along, it's children (nested objects) are
111
+ * also freed.
112
+ */
113
+ FIO_INLINE void fiobj_free(FIOBJ);
114
+
115
+ /**
116
+ * Tests if an object evaluates as TRUE.
117
+ *
118
+ * This is object type specific. For example, empty strings might evaluate as
119
+ * FALSE, even though they aren't a boolean type.
120
+ */
121
+ FIO_INLINE int fiobj_is_true(const FIOBJ);
122
+
123
+ /**
124
+ * Returns an Object's numerical value.
125
+ *
126
+ * If a String is passed to the function, it will be parsed assuming base 10
127
+ * numerical data.
128
+ *
129
+ * Hashes and Arrays return their object count.
130
+ *
131
+ * IO objects return the length of their data.
132
+ *
133
+ * A type error results in 0.
134
+ */
135
+ FIO_INLINE intptr_t fiobj_obj2num(const FIOBJ obj);
136
+
137
+ /**
138
+ * Returns a Float's value.
139
+ *
140
+ * If a String is passed to the function, they will benparsed assuming base 10
141
+ * numerical data.
142
+ *
143
+ * A type error results in 0.
144
+ */
145
+ FIO_INLINE double fiobj_obj2float(const FIOBJ obj);
146
+
147
+ /**
148
+ * Returns a C String (NUL terminated) using the `fio_str_info_s` data type.
149
+ *
150
+ * The Sting in binary safe and might contain NUL bytes in the middle as well as
151
+ * a terminating NUL.
152
+ *
153
+ * If a a Number or a Float are passed to the function, they
154
+ * will be parsed as a *temporary*, thread-safe, String.
155
+ *
156
+ * Numbers will be represented in base 10 numerical data.
157
+ *
158
+ * A type error results in NULL (i.e. object isn't a String).
159
+ */
160
+ FIO_INLINE fio_str_info_s fiobj_obj2cstr(const FIOBJ obj);
161
+
162
+ /**
163
+ * Calculates an Objects's SipHash value for possible use as a HashMap key.
164
+ *
165
+ * The Object MUST answer to the fiobj_obj2cstr, or the result is unusable. In
166
+ * other words, Hash Objects and Arrays can NOT be used for Hash keys.
167
+ */
168
+ FIO_INLINE uint64_t fiobj_obj2hash(const FIOBJ o);
169
+
170
+ /**
171
+ * Single layer iteration using a callback for each nested fio object.
172
+ *
173
+ * Accepts any `FIOBJ ` type but only collections (Arrays and Hashes) are
174
+ * processed. The container itself (the Array or the Hash) is **not** processed
175
+ * (unlike `fiobj_each2`).
176
+ *
177
+ * The callback task function must accept an object and an opaque user pointer.
178
+ *
179
+ * Hash objects pass along only the value object. The keys can be accessed using
180
+ * the `fiobj_hash_key_in_loop` function.
181
+ *
182
+ * If the callback returns -1, the loop is broken. Any other value is ignored.
183
+ *
184
+ * Returns the "stop" position, i.e., the number of items processed + the
185
+ * starting point.
186
+ */
187
+ FIO_INLINE size_t fiobj_each1(FIOBJ, size_t start_at,
188
+ int (*task)(FIOBJ obj, void *arg), void *arg);
189
+
190
+ /**
191
+ * Deep iteration using a callback for each fio object, including the parent.
192
+ *
193
+ * Accepts any `FIOBJ ` type.
194
+ *
195
+ * Collections (Arrays, Hashes) are deeply probed and shouldn't be edited
196
+ * during an `fiobj_each2` call (or weird things may happen).
197
+ *
198
+ * The callback task function must accept an object and an opaque user pointer.
199
+ *
200
+ * Hash objects keys are available using the `fiobj_hash_key_in_loop` function.
201
+ *
202
+ * Notice that when passing collections to the function, the collection itself
203
+ * is sent to the callback followed by it's children (if any). This is true also
204
+ * for nested collections (a nested Hash will be sent first, followed by the
205
+ * nested Hash's children and then followed by the rest of it's siblings.
206
+ *
207
+ * If the callback returns -1, the loop is broken. Any other value is ignored.
208
+ */
209
+ size_t fiobj_each2(FIOBJ, int (*task)(FIOBJ obj, void *arg), void *arg);
210
+
211
+ /**
212
+ * Deeply compare two objects. No hashing or recursive function calls are
213
+ * involved.
214
+ *
215
+ * Uses a similar algorithm to `fiobj_each2`, except adjusted to two objects.
216
+ *
217
+ * Hash objects are order sensitive. To be equal, Hash keys must match in order.
218
+ *
219
+ * Returns 1 if true and 0 if false.
220
+ */
221
+ FIO_INLINE int fiobj_iseq(const FIOBJ obj1, const FIOBJ obj2);
222
+
223
+ /* *****************************************************************************
224
+ Object Type Identification
225
+ ***************************************************************************** */
226
+
227
+ #define FIOBJECT_NUMBER_FLAG 1
228
+
229
+ #if UINTPTR_MAX < 0xFFFFFFFFFFFFFFFF
230
+ #define FIOBJECT_PRIMITIVE_FLAG 2
231
+ #define FIOBJECT_STRING_FLAG 0
232
+ #define FIOBJECT_HASH_FLAG 0
233
+ #define FIOBJECT_TYPE_MASK (~(uintptr_t)3)
234
+ #else
235
+ #define FIOBJECT_PRIMITIVE_FLAG 6
236
+ #define FIOBJECT_STRING_FLAG 2
237
+ #define FIOBJECT_HASH_FLAG 4
238
+ #define FIOBJECT_TYPE_MASK (~(uintptr_t)7)
239
+ #endif
240
+
241
+ #define FIOBJ_NUMBER_SIGN_MASK ((~((uintptr_t)0)) >> 1)
242
+ #define FIOBJ_NUMBER_SIGN_BIT (~FIOBJ_NUMBER_SIGN_MASK)
243
+ #define FIOBJ_NUMBER_SIGN_EXCLUDE_BIT (FIOBJ_NUMBER_SIGN_BIT >> 1)
244
+
245
+ #define FIOBJ_IS_ALLOCATED(o) \
246
+ ((o) && ((o)&FIOBJECT_NUMBER_FLAG) == 0 && \
247
+ ((o)&FIOBJECT_PRIMITIVE_FLAG) != FIOBJECT_PRIMITIVE_FLAG)
248
+ #define FIOBJ2PTR(o) ((void *)((o)&FIOBJECT_TYPE_MASK))
249
+
250
+ FIO_INLINE fiobj_type_enum fiobj_type(FIOBJ o) {
251
+ if (!o)
252
+ return FIOBJ_T_NULL;
253
+ if (o & FIOBJECT_NUMBER_FLAG)
254
+ return FIOBJ_T_NUMBER;
255
+ if ((o & FIOBJECT_PRIMITIVE_FLAG) == FIOBJECT_PRIMITIVE_FLAG)
256
+ return (fiobj_type_enum)o;
257
+ if (FIOBJECT_STRING_FLAG &&
258
+ (o & FIOBJECT_PRIMITIVE_FLAG) == FIOBJECT_STRING_FLAG)
259
+ return FIOBJ_T_STRING;
260
+ if (FIOBJECT_HASH_FLAG && (o & FIOBJECT_PRIMITIVE_FLAG) == FIOBJECT_HASH_FLAG)
261
+ return FIOBJ_T_HASH;
262
+ return ((fiobj_type_enum *)FIOBJ2PTR(o))[0];
263
+ }
264
+
265
+ /**
266
+ * This is faster than getting the type, since the switch statement is
267
+ * optimized away (it's calculated during compile time).
268
+ */
269
+ FIO_INLINE size_t fiobj_type_is(FIOBJ o, fiobj_type_enum type) {
270
+ switch (type) {
271
+ case FIOBJ_T_NUMBER:
272
+ return (o & FIOBJECT_NUMBER_FLAG) ||
273
+ ((fiobj_type_enum *)o)[0] == FIOBJ_T_NUMBER;
274
+ case FIOBJ_T_NULL:
275
+ return !o || o == fiobj_null();
276
+ case FIOBJ_T_TRUE:
277
+ return o == fiobj_true();
278
+ case FIOBJ_T_FALSE:
279
+ return o == fiobj_false();
280
+ case FIOBJ_T_STRING:
281
+ return (FIOBJECT_STRING_FLAG && (o & FIOBJECT_NUMBER_FLAG) == 0 &&
282
+ (o & FIOBJECT_PRIMITIVE_FLAG) == FIOBJECT_STRING_FLAG) ||
283
+ (FIOBJECT_STRING_FLAG == 0 && FIOBJ_IS_ALLOCATED(o) &&
284
+ ((fiobj_type_enum *)FIOBJ2PTR(o))[0] == FIOBJ_T_STRING);
285
+ case FIOBJ_T_HASH:
286
+ if (FIOBJECT_HASH_FLAG) {
287
+ return ((o & FIOBJECT_NUMBER_FLAG) == 0 &&
288
+ (o & FIOBJECT_PRIMITIVE_FLAG) == FIOBJECT_HASH_FLAG);
289
+ }
290
+ /* fallthrough */
291
+ case FIOBJ_T_FLOAT:
292
+ case FIOBJ_T_ARRAY:
293
+ case FIOBJ_T_DATA:
294
+ case FIOBJ_T_UNKNOWN:
295
+ return FIOBJ_IS_ALLOCATED(o) &&
296
+ ((fiobj_type_enum *)FIOBJ2PTR(o))[0] == type;
297
+ }
298
+ return FIOBJ_IS_ALLOCATED(o) && ((fiobj_type_enum *)FIOBJ2PTR(o))[0] == type;
299
+ }
300
+
301
+ /* *****************************************************************************
302
+ Object Header
303
+ ***************************************************************************** */
304
+
305
+ typedef struct {
306
+ /* a String allowing logging type data. */
307
+ const char *class_name;
308
+ /* deallocate root object's memory, perform task for each nested object. */
309
+ void (*const dealloc)(FIOBJ, void (*task)(FIOBJ, void *), void *);
310
+ /* return the number of normal nested object */
311
+ uintptr_t (*const count)(const FIOBJ);
312
+ /* tests the object for truthfulness. */
313
+ size_t (*const is_true)(const FIOBJ);
314
+ /* tests if two objects are equal. */
315
+ size_t (*const is_eq)(const FIOBJ, const FIOBJ);
316
+ /* iterates through the normal nested objects (ignore deep nesting) */
317
+ size_t (*const each)(FIOBJ, size_t start_at, int (*task)(FIOBJ, void *),
318
+ void *);
319
+ /* object value as String */
320
+ fio_str_info_s (*const to_str)(const FIOBJ);
321
+ /* object value as Integer */
322
+ intptr_t (*const to_i)(const FIOBJ);
323
+ /* object value as Float */
324
+ double (*const to_f)(const FIOBJ);
325
+ } fiobj_object_vtable_s;
326
+
327
+ typedef struct {
328
+ /* must be first */
329
+ fiobj_type_enum type;
330
+ /* reference counter */
331
+ uint32_t ref;
332
+ } fiobj_object_header_s;
333
+
334
+ extern const fiobj_object_vtable_s FIOBJECT_VTABLE_NUMBER;
335
+ extern const fiobj_object_vtable_s FIOBJECT_VTABLE_FLOAT;
336
+ extern const fiobj_object_vtable_s FIOBJECT_VTABLE_STRING;
337
+ extern const fiobj_object_vtable_s FIOBJECT_VTABLE_ARRAY;
338
+ extern const fiobj_object_vtable_s FIOBJECT_VTABLE_HASH;
339
+ extern const fiobj_object_vtable_s FIOBJECT_VTABLE_DATA;
340
+
341
+ #define FIOBJECT2VTBL(o) fiobj_type_vtable(o)
342
+ #define FIOBJECT2HEAD(o) (((fiobj_object_header_s *)FIOBJ2PTR((o))))
343
+
344
+ FIO_INLINE const fiobj_object_vtable_s *fiobj_type_vtable(FIOBJ o) {
345
+ switch (FIOBJ_TYPE(o)) {
346
+ case FIOBJ_T_NUMBER:
347
+ return &FIOBJECT_VTABLE_NUMBER;
348
+ case FIOBJ_T_FLOAT:
349
+ return &FIOBJECT_VTABLE_FLOAT;
350
+ case FIOBJ_T_STRING:
351
+ return &FIOBJECT_VTABLE_STRING;
352
+ case FIOBJ_T_ARRAY:
353
+ return &FIOBJECT_VTABLE_ARRAY;
354
+ case FIOBJ_T_HASH:
355
+ return &FIOBJECT_VTABLE_HASH;
356
+ case FIOBJ_T_DATA:
357
+ return &FIOBJECT_VTABLE_DATA;
358
+ case FIOBJ_T_NULL:
359
+ case FIOBJ_T_TRUE:
360
+ case FIOBJ_T_FALSE:
361
+ case FIOBJ_T_UNKNOWN:
362
+ return NULL;
363
+ }
364
+ return NULL;
365
+ }
366
+
367
+ /* *****************************************************************************
368
+ Atomic reference counting
369
+ ***************************************************************************** */
370
+
371
+ /* C11 Atomics are defined? */
372
+ #if defined(__ATOMIC_RELAXED)
373
+ /** An atomic addition operation */
374
+ #define fiobj_ref_inc(o) \
375
+ __atomic_add_fetch(&FIOBJECT2HEAD(o)->ref, 1, __ATOMIC_SEQ_CST)
376
+ /** An atomic subtraction operation */
377
+ #define fiobj_ref_dec(o) \
378
+ __atomic_sub_fetch(&FIOBJECT2HEAD(o)->ref, 1, __ATOMIC_SEQ_CST)
379
+
380
+ /* Select the correct compiler builtin method. */
381
+ #elif defined(__has_builtin) && !FIO_GNUC_BYPASS
382
+
383
+ #if __has_builtin(__sync_fetch_and_or)
384
+ /** An atomic addition operation */
385
+ #define fiobj_ref_inc(o) __sync_add_and_fetch(&FIOBJECT2HEAD(o)->ref, 1)
386
+ /** An atomic subtraction operation */
387
+ #define fiobj_ref_dec(o) __sync_sub_and_fetch(&FIOBJECT2HEAD(o)->ref, 1)
388
+
389
+ #else
390
+ #error missing required atomic options.
391
+ #endif /* defined(__has_builtin) */
392
+
393
+ #elif __GNUC__ > 3
394
+ /** An atomic addition operation */
395
+ #define fiobj_ref_inc(o) __sync_add_and_fetch(&FIOBJECT2HEAD(o)->ref, 1)
396
+ /** An atomic subtraction operation */
397
+ #define fiobj_ref_dec(o) __sync_sub_and_fetch(&FIOBJECT2HEAD(o)->ref, 1)
398
+
399
+ #else
400
+ #error missing required atomic options.
401
+ #endif
402
+
403
+ #define OBJREF_ADD(o) fiobj_ref_inc(o)
404
+ #define OBJREF_REM(o) fiobj_ref_dec(o)
405
+
406
+ /* *****************************************************************************
407
+ Inlined Functions
408
+ ***************************************************************************** */
409
+
410
+ /** Returns a C string naming the objects dynamic type. */
411
+ FIO_INLINE const char *fiobj_type_name(const FIOBJ o) {
412
+ if (o & FIOBJECT_NUMBER_FLAG)
413
+ return "Number";
414
+ if (FIOBJ_IS_ALLOCATED(o))
415
+ return FIOBJECT2VTBL(o)->class_name;
416
+ if (!o)
417
+ return "NULL";
418
+ return "Primitive";
419
+ }
420
+
421
+ /** used internally to free objects with nested objects. */
422
+ void fiobj_free_complex_object(FIOBJ o);
423
+
424
+ /**
425
+ * Copy by reference(!) - increases an object's (and any nested object's)
426
+ * reference count.
427
+ *
428
+ * Always returns the value passed along.
429
+ */
430
+ FIO_INLINE FIOBJ fiobj_dup(FIOBJ o) {
431
+ if (FIOBJ_IS_ALLOCATED(o))
432
+ OBJREF_ADD(o);
433
+ return o;
434
+ }
435
+
436
+ /**
437
+ * Decreases an object's reference count, releasing memory and
438
+ * resources.
439
+ *
440
+ * This function affects nested objects, meaning that when an Array or
441
+ * a Hash object is passed along, it's children (nested objects) are
442
+ * also freed.
443
+ *
444
+ * Returns the number of existing references or zero if memory was released.
445
+ */
446
+ FIO_INLINE void fiobj_free(FIOBJ o) {
447
+ if (!FIOBJ_IS_ALLOCATED(o))
448
+ return;
449
+ if (fiobj_ref_dec(o))
450
+ return;
451
+ if (FIOBJECT2VTBL(o)->each && FIOBJECT2VTBL(o)->count(o))
452
+ fiobj_free_complex_object(o);
453
+ else
454
+ FIOBJECT2VTBL(o)->dealloc(o, NULL, NULL);
455
+ }
456
+
457
+ /**
458
+ * Tests if an object evaluates as TRUE.
459
+ *
460
+ * This is object type specific. For example, empty strings might evaluate as
461
+ * FALSE, even though they aren't a boolean type.
462
+ */
463
+ FIO_INLINE int fiobj_is_true(const FIOBJ o) {
464
+ if (o & FIOBJECT_NUMBER_FLAG)
465
+ return ((uintptr_t)o >> 1) != 0;
466
+ if ((o & FIOBJECT_PRIMITIVE_FLAG) == FIOBJECT_PRIMITIVE_FLAG)
467
+ return o == FIOBJ_T_TRUE;
468
+ return (int)(FIOBJECT2VTBL(o)->is_true(o));
469
+ }
470
+
471
+ /**
472
+ * Returns an object's numerical value.
473
+ *
474
+ * If a String or Symbol are passed to the function, they will be
475
+ * parsed assuming base 10 numerical data.
476
+ *
477
+ * Hashes and Arrays return their object count.
478
+ *
479
+ * IO and File objects return their underlying file descriptor.
480
+ *
481
+ * A type error results in 0.
482
+ */
483
+ FIO_INLINE intptr_t fiobj_obj2num(const FIOBJ o) {
484
+ if (o & FIOBJECT_NUMBER_FLAG) {
485
+ const uintptr_t sign =
486
+ (o & FIOBJ_NUMBER_SIGN_BIT)
487
+ ? (FIOBJ_NUMBER_SIGN_BIT | FIOBJ_NUMBER_SIGN_EXCLUDE_BIT)
488
+ : 0;
489
+ return (intptr_t)(((o & FIOBJ_NUMBER_SIGN_MASK) >> 1) | sign);
490
+ }
491
+ if (!o || !FIOBJ_IS_ALLOCATED(o))
492
+ return o == FIOBJ_T_TRUE;
493
+ return FIOBJECT2VTBL(o)->to_i(o);
494
+ }
495
+
496
+ /** Converts a number to a temporary, thread safe, C string object */
497
+ fio_str_info_s fio_ltocstr(long);
498
+
499
+ /** Converts a float to a temporary, thread safe, C string object */
500
+ fio_str_info_s fio_ftocstr(double);
501
+
502
+ /**
503
+ * Returns a C String (NUL terminated) using the `fio_str_info_s` data type.
504
+ *
505
+ * The String is binary safe and might contain NUL bytes in the middle as well as
506
+ * a terminating NUL.
507
+ *
508
+ * If a a Number or a Float are passed to the function, they
509
+ * will be parsed as a *temporary*, thread-safe, String.
510
+ *
511
+ * Numbers will be represented in base 10 numerical data.
512
+ *
513
+ * A type error results in NULL (i.e. object isn't a String).
514
+ */
515
+ FIO_INLINE fio_str_info_s fiobj_obj2cstr(const FIOBJ o) {
516
+ if (!o) {
517
+ fio_str_info_s ret = {0, 4, (char *)"null"};
518
+ return ret;
519
+ }
520
+ if (o & FIOBJECT_NUMBER_FLAG)
521
+ return fio_ltocstr(((intptr_t)o) >> 1);
522
+ if ((o & FIOBJECT_PRIMITIVE_FLAG) == FIOBJECT_PRIMITIVE_FLAG) {
523
+ switch ((fiobj_type_enum)o) {
524
+ case FIOBJ_T_NULL: {
525
+ fio_str_info_s ret = {0, 4, (char *)"null"};
526
+ return ret;
527
+ }
528
+ case FIOBJ_T_FALSE: {
529
+ fio_str_info_s ret = {0, 5, (char *)"false"};
530
+ return ret;
531
+ }
532
+ case FIOBJ_T_TRUE: {
533
+ fio_str_info_s ret = {0, 4, (char *)"true"};
534
+ return ret;
535
+ }
536
+ default:
537
+ break;
538
+ }
539
+ }
540
+ return FIOBJECT2VTBL(o)->to_str(o);
541
+ }
542
+
543
+ /* referenced here */
544
+ uint64_t fiobj_str_hash(FIOBJ o);
545
+ /**
546
+ * Calculates an Objects's SipHash value for possible use as a HashMap key.
547
+ *
548
+ * The Object MUST answer to the fiobj_obj2cstr, or the result is unusable. In
549
+ * other words, Hash Objects and Arrays can NOT be used for Hash keys.
550
+ */
551
+ FIO_INLINE uint64_t fiobj_obj2hash(const FIOBJ o) {
552
+ if (FIOBJ_TYPE_IS(o, FIOBJ_T_STRING))
553
+ return fiobj_str_hash(o);
554
+ if (!FIOBJ_IS_ALLOCATED(o))
555
+ return (uint64_t)o;
556
+ fio_str_info_s s = fiobj_obj2cstr(o);
557
+ return FIO_HASH_FN(s.data, s.len, (uintptr_t)&fiobj_each2,
558
+ (uintptr_t)&fiobj_free_complex_object);
559
+ }
560
+
561
+ FIO_INLINE uint64_t fiobj_hash_string(const void *data, size_t len) {
562
+ return FIO_HASH_FN(data, len, (uintptr_t)&fiobj_each2,
563
+ (uintptr_t)&fiobj_free_complex_object);
564
+ }
565
+
566
+ /**
567
+ * Returns a Float's value.
568
+ *
569
+ * If a String or Symbol are passed to the function, they will be
570
+ * parsed assuming base 10 numerical data.
571
+ *
572
+ * Hashes and Arrays return their object count.
573
+ *
574
+ * IO and File objects return their underlying file descriptor.
575
+ *
576
+ * A type error results in 0.
577
+ */
578
+ FIO_INLINE double fiobj_obj2float(const FIOBJ o) {
579
+ if (o & FIOBJECT_NUMBER_FLAG)
580
+ return (double)(fiobj_obj2num(o));
581
+ if (!o || (o & FIOBJECT_PRIMITIVE_FLAG) == FIOBJECT_PRIMITIVE_FLAG)
582
+ return (double)(o == FIOBJ_T_TRUE);
583
+ return FIOBJECT2VTBL(o)->to_f(o);
584
+ }
585
+
586
+ /** used internally for complext nested tests (Array / Hash types) */
587
+ int fiobj_iseq____internal_complex__(FIOBJ o, FIOBJ o2);
588
+ /**
589
+ * Deeply compare two objects. No hashing or recursive function calls are
590
+ * involved.
591
+ *
592
+ * Uses a similar algorithm to `fiobj_each2`, except adjusted to two objects.
593
+ *
594
+ * Hash order will be tested when comapring Hashes.
595
+ *
596
+ * KNOWN ISSUES:
597
+ *
598
+ * * Temporarily broken for collections (Arrays / Hashes).
599
+ *
600
+ * * Hash order will be tested as well as the Hash content, which means that
601
+ * equal Hashes might be considered unequal if their order doesn't match.
602
+ *
603
+ * Returns 1 if true and 0 if false.
604
+ */
605
+ FIO_INLINE int fiobj_iseq(const FIOBJ o, const FIOBJ o2) {
606
+ if (o == o2)
607
+ return 1;
608
+ if (!o || !o2)
609
+ return 0; /* they should have compared equal before. */
610
+ if (!FIOBJ_IS_ALLOCATED(o) || !FIOBJ_IS_ALLOCATED(o2))
611
+ return 0; /* they should have compared equal before. */
612
+ if (FIOBJECT2HEAD(o)->type != FIOBJECT2HEAD(o2)->type)
613
+ return 0; /* non-type equality is a barriar to equality. */
614
+ if (!FIOBJECT2VTBL(o)->is_eq(o, o2))
615
+ return 0;
616
+ if (FIOBJECT2VTBL(o)->each && FIOBJECT2VTBL(o)->count(o))
617
+ return fiobj_iseq____internal_complex__((FIOBJ)o, (FIOBJ)o2);
618
+ return 1;
619
+ }
620
+
621
+ /**
622
+ * Single layer iteration using a callback for each nested fio object.
623
+ *
624
+ * Accepts any `FIOBJ ` type but only collections (Arrays and Hashes) are
625
+ * processed. The container itself (the Array or the Hash) is **not** processed
626
+ * (unlike `fiobj_each2`).
627
+ *
628
+ * The callback task function must accept an object and an opaque user pointer.
629
+ *
630
+ * Hash objects pass along a `FIOBJ_T_COUPLET` object, containing
631
+ * references for both the key and the object. Keys shouldn't be altered once
632
+ * placed as a key (or the Hash will break). Collections (Arrays / Hashes) can't
633
+ * be used as keeys.
634
+ *
635
+ * If the callback returns -1, the loop is broken. Any other value is ignored.
636
+ *
637
+ * Returns the "stop" position, i.e., the number of items processed + the
638
+ * starting point.
639
+ */
640
+ FIO_INLINE size_t fiobj_each1(FIOBJ o, size_t start_at,
641
+ int (*task)(FIOBJ obj, void *arg), void *arg) {
642
+ if (FIOBJ_IS_ALLOCATED(o) && FIOBJECT2VTBL(o)->each)
643
+ return FIOBJECT2VTBL(o)->each(o, start_at, task, arg);
644
+ return 0;
645
+ }
646
+
647
+ #if DEBUG
648
+ void fiobj_test_core(void);
649
+ #endif
650
+
651
+ #ifdef __cplusplus
652
+ } /* closing brace for extern "C" */
653
+ #endif
654
+ #endif