rage-iodine 1.7.58
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
- data/.github/workflows/ruby.yml +42 -0
- data/.gitignore +20 -0
- data/.rspec +2 -0
- data/.yardopts +8 -0
- data/CHANGELOG.md +1098 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +21 -0
- data/LIMITS.md +41 -0
- data/README.md +782 -0
- data/Rakefile +23 -0
- data/SPEC-PubSub-Draft.md +159 -0
- data/SPEC-WebSocket-Draft.md +239 -0
- data/bin/console +22 -0
- data/bin/info.md +353 -0
- data/bin/mustache_bench.rb +100 -0
- data/bin/poc/Gemfile.lock +23 -0
- data/bin/poc/README.md +37 -0
- data/bin/poc/config.ru +66 -0
- data/bin/poc/gemfile +1 -0
- data/bin/poc/www/index.html +57 -0
- data/examples/async_task.ru +92 -0
- data/examples/bates/README.md +3 -0
- data/examples/bates/config.ru +342 -0
- data/examples/bates/david+bold.pdf +0 -0
- data/examples/bates/public/drop-pdf.png +0 -0
- data/examples/bates/public/index.html +600 -0
- data/examples/config.ru +59 -0
- data/examples/echo.ru +59 -0
- data/examples/etag.ru +16 -0
- data/examples/hello.ru +29 -0
- data/examples/pubsub_engine.ru +81 -0
- data/examples/rack3.ru +12 -0
- data/examples/redis.ru +70 -0
- data/examples/shootout.ru +73 -0
- data/examples/sub-protocols.ru +90 -0
- data/examples/tcp_client.rb +66 -0
- data/examples/x-sendfile.ru +14 -0
- data/exe/iodine +280 -0
- data/ext/iodine/extconf.rb +110 -0
- data/ext/iodine/fio.c +12096 -0
- data/ext/iodine/fio.h +6390 -0
- data/ext/iodine/fio_cli.c +431 -0
- data/ext/iodine/fio_cli.h +189 -0
- data/ext/iodine/fio_json_parser.h +687 -0
- data/ext/iodine/fio_siphash.c +157 -0
- data/ext/iodine/fio_siphash.h +37 -0
- data/ext/iodine/fio_tls.h +129 -0
- data/ext/iodine/fio_tls_missing.c +649 -0
- data/ext/iodine/fio_tls_openssl.c +1056 -0
- data/ext/iodine/fio_tmpfile.h +50 -0
- data/ext/iodine/fiobj.h +44 -0
- data/ext/iodine/fiobj4fio.h +21 -0
- data/ext/iodine/fiobj_ary.c +333 -0
- data/ext/iodine/fiobj_ary.h +139 -0
- data/ext/iodine/fiobj_data.c +1185 -0
- data/ext/iodine/fiobj_data.h +167 -0
- data/ext/iodine/fiobj_hash.c +409 -0
- data/ext/iodine/fiobj_hash.h +176 -0
- data/ext/iodine/fiobj_json.c +622 -0
- data/ext/iodine/fiobj_json.h +68 -0
- data/ext/iodine/fiobj_mem.h +71 -0
- data/ext/iodine/fiobj_mustache.c +317 -0
- data/ext/iodine/fiobj_mustache.h +62 -0
- data/ext/iodine/fiobj_numbers.c +344 -0
- data/ext/iodine/fiobj_numbers.h +127 -0
- data/ext/iodine/fiobj_str.c +433 -0
- data/ext/iodine/fiobj_str.h +172 -0
- data/ext/iodine/fiobject.c +620 -0
- data/ext/iodine/fiobject.h +654 -0
- data/ext/iodine/hpack.h +1923 -0
- data/ext/iodine/http.c +2736 -0
- data/ext/iodine/http.h +1019 -0
- data/ext/iodine/http1.c +825 -0
- data/ext/iodine/http1.h +29 -0
- data/ext/iodine/http1_parser.h +1835 -0
- data/ext/iodine/http_internal.c +1279 -0
- data/ext/iodine/http_internal.h +248 -0
- data/ext/iodine/http_mime_parser.h +350 -0
- data/ext/iodine/iodine.c +1433 -0
- data/ext/iodine/iodine.h +64 -0
- data/ext/iodine/iodine_caller.c +218 -0
- data/ext/iodine/iodine_caller.h +27 -0
- data/ext/iodine/iodine_connection.c +941 -0
- data/ext/iodine/iodine_connection.h +55 -0
- data/ext/iodine/iodine_defer.c +420 -0
- data/ext/iodine/iodine_defer.h +6 -0
- data/ext/iodine/iodine_fiobj2rb.h +120 -0
- data/ext/iodine/iodine_helpers.c +282 -0
- data/ext/iodine/iodine_helpers.h +12 -0
- data/ext/iodine/iodine_http.c +1280 -0
- data/ext/iodine/iodine_http.h +23 -0
- data/ext/iodine/iodine_json.c +302 -0
- data/ext/iodine/iodine_json.h +6 -0
- data/ext/iodine/iodine_mustache.c +567 -0
- data/ext/iodine/iodine_mustache.h +6 -0
- data/ext/iodine/iodine_pubsub.c +580 -0
- data/ext/iodine/iodine_pubsub.h +26 -0
- data/ext/iodine/iodine_rack_io.c +273 -0
- data/ext/iodine/iodine_rack_io.h +20 -0
- data/ext/iodine/iodine_store.c +142 -0
- data/ext/iodine/iodine_store.h +20 -0
- data/ext/iodine/iodine_tcp.c +346 -0
- data/ext/iodine/iodine_tcp.h +13 -0
- data/ext/iodine/iodine_tls.c +261 -0
- data/ext/iodine/iodine_tls.h +13 -0
- data/ext/iodine/mustache_parser.h +1546 -0
- data/ext/iodine/redis_engine.c +957 -0
- data/ext/iodine/redis_engine.h +79 -0
- data/ext/iodine/resp_parser.h +317 -0
- data/ext/iodine/scheduler.c +173 -0
- data/ext/iodine/scheduler.h +6 -0
- data/ext/iodine/websocket_parser.h +506 -0
- data/ext/iodine/websockets.c +752 -0
- data/ext/iodine/websockets.h +185 -0
- data/iodine.gemspec +50 -0
- data/lib/iodine/connection.rb +61 -0
- data/lib/iodine/json.rb +42 -0
- data/lib/iodine/mustache.rb +113 -0
- data/lib/iodine/pubsub.rb +55 -0
- data/lib/iodine/rack_utils.rb +43 -0
- data/lib/iodine/tls.rb +16 -0
- data/lib/iodine/version.rb +3 -0
- data/lib/iodine.rb +274 -0
- data/lib/rack/handler/iodine.rb +33 -0
- data/logo.png +0 -0
- metadata +284 -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
|