iodine 0.4.19 → 0.5.0

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