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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -2
- data/CHANGELOG.md +22 -0
- data/LIMITS.md +19 -9
- data/README.md +92 -77
- data/SPEC-PubSub-Draft.md +113 -0
- data/SPEC-Websocket-Draft.md +127 -143
- data/bin/http-hello +0 -1
- data/bin/raw-rbhttp +1 -1
- data/bin/raw_broadcast +8 -10
- data/bin/updated api +2 -2
- data/bin/ws-broadcast +2 -4
- data/bin/ws-echo +2 -2
- data/examples/config.ru +13 -13
- data/examples/echo.ru +5 -6
- data/examples/hello.ru +2 -3
- data/examples/info.md +316 -0
- data/examples/pubsub_engine.ru +81 -0
- data/examples/redis.ru +9 -9
- data/examples/shootout.ru +45 -11
- data/ext/iodine/defer.c +194 -297
- data/ext/iodine/defer.h +61 -53
- data/ext/iodine/evio.c +0 -260
- data/ext/iodine/evio.h +50 -22
- data/ext/iodine/evio_callbacks.c +26 -0
- data/ext/iodine/evio_epoll.c +251 -0
- data/ext/iodine/evio_kqueue.c +193 -0
- data/ext/iodine/extconf.rb +1 -1
- data/ext/iodine/facil.c +1420 -542
- data/ext/iodine/facil.h +151 -64
- data/ext/iodine/fio_ary.h +418 -0
- data/ext/iodine/{base64.c → fio_base64.c} +33 -24
- data/ext/iodine/{base64.h → fio_base64.h} +6 -7
- data/ext/iodine/{fio_cli_helper.c → fio_cli.c} +77 -58
- data/ext/iodine/{fio_cli_helper.h → fio_cli.h} +9 -4
- data/ext/iodine/fio_hashmap.h +759 -0
- data/ext/iodine/fio_json_parser.h +651 -0
- data/ext/iodine/fio_llist.h +257 -0
- data/ext/iodine/fio_mem.c +672 -0
- data/ext/iodine/fio_mem.h +140 -0
- data/ext/iodine/fio_random.c +248 -0
- data/ext/iodine/{random.h → fio_random.h} +11 -14
- data/ext/iodine/{sha1.c → fio_sha1.c} +28 -24
- data/ext/iodine/{sha1.h → fio_sha1.h} +38 -16
- data/ext/iodine/{sha2.c → fio_sha2.c} +66 -49
- data/ext/iodine/{sha2.h → fio_sha2.h} +57 -26
- data/ext/iodine/{fiobj_internal.c → fio_siphash.c} +9 -90
- data/ext/iodine/fio_siphash.h +18 -0
- data/ext/iodine/fio_tmpfile.h +38 -0
- data/ext/iodine/fiobj.h +24 -7
- data/ext/iodine/fiobj4sock.h +23 -0
- data/ext/iodine/fiobj_ary.c +143 -226
- data/ext/iodine/fiobj_ary.h +17 -16
- data/ext/iodine/fiobj_data.c +1160 -0
- data/ext/iodine/fiobj_data.h +164 -0
- data/ext/iodine/fiobj_hash.c +298 -406
- data/ext/iodine/fiobj_hash.h +101 -54
- data/ext/iodine/fiobj_json.c +478 -601
- data/ext/iodine/fiobj_json.h +34 -9
- data/ext/iodine/fiobj_numbers.c +383 -51
- data/ext/iodine/fiobj_numbers.h +87 -11
- data/ext/iodine/fiobj_str.c +423 -184
- data/ext/iodine/fiobj_str.h +81 -32
- data/ext/iodine/fiobject.c +273 -522
- data/ext/iodine/fiobject.h +477 -112
- data/ext/iodine/http.c +2243 -83
- data/ext/iodine/http.h +842 -121
- data/ext/iodine/http1.c +810 -385
- data/ext/iodine/http1.h +16 -39
- data/ext/iodine/http1_parser.c +146 -74
- data/ext/iodine/http1_parser.h +15 -4
- data/ext/iodine/http_internal.c +1258 -0
- data/ext/iodine/http_internal.h +226 -0
- data/ext/iodine/http_mime_parser.h +341 -0
- data/ext/iodine/iodine.c +86 -68
- data/ext/iodine/iodine.h +26 -11
- data/ext/iodine/iodine_helpers.c +8 -7
- data/ext/iodine/iodine_http.c +487 -324
- data/ext/iodine/iodine_json.c +304 -0
- data/ext/iodine/iodine_json.h +6 -0
- data/ext/iodine/iodine_protocol.c +107 -45
- data/ext/iodine/iodine_pubsub.c +526 -225
- data/ext/iodine/iodine_pubsub.h +10 -0
- data/ext/iodine/iodine_websockets.c +268 -510
- data/ext/iodine/iodine_websockets.h +2 -4
- data/ext/iodine/pubsub.c +726 -432
- data/ext/iodine/pubsub.h +85 -103
- data/ext/iodine/rb-call.c +4 -4
- data/ext/iodine/rb-defer.c +46 -22
- data/ext/iodine/rb-fiobj2rb.h +117 -0
- data/ext/iodine/rb-rack-io.c +73 -238
- data/ext/iodine/rb-rack-io.h +2 -2
- data/ext/iodine/rb-registry.c +35 -93
- data/ext/iodine/rb-registry.h +1 -0
- data/ext/iodine/redis_engine.c +742 -304
- data/ext/iodine/redis_engine.h +42 -39
- data/ext/iodine/resp_parser.h +311 -0
- data/ext/iodine/sock.c +627 -490
- data/ext/iodine/sock.h +345 -297
- data/ext/iodine/spnlock.inc +15 -4
- data/ext/iodine/websocket_parser.h +16 -20
- data/ext/iodine/websockets.c +188 -257
- data/ext/iodine/websockets.h +24 -133
- data/lib/iodine.rb +52 -7
- data/lib/iodine/cli.rb +6 -24
- data/lib/iodine/json.rb +40 -0
- data/lib/iodine/version.rb +1 -1
- data/lib/iodine/websocket.rb +5 -3
- data/lib/rack/handler/iodine.rb +58 -13
- metadata +38 -48
- data/bin/ws-shootout +0 -107
- data/examples/broadcast.ru +0 -56
- data/ext/iodine/bscrypt-common.h +0 -116
- data/ext/iodine/bscrypt.h +0 -49
- data/ext/iodine/fio2resp.c +0 -60
- data/ext/iodine/fio2resp.h +0 -51
- data/ext/iodine/fio_dict.c +0 -446
- data/ext/iodine/fio_dict.h +0 -99
- data/ext/iodine/fio_hash_table.h +0 -370
- data/ext/iodine/fio_list.h +0 -111
- data/ext/iodine/fiobj_internal.h +0 -280
- data/ext/iodine/fiobj_primitives.c +0 -131
- data/ext/iodine/fiobj_primitives.h +0 -55
- data/ext/iodine/fiobj_sym.c +0 -135
- data/ext/iodine/fiobj_sym.h +0 -60
- data/ext/iodine/hex.c +0 -124
- data/ext/iodine/hex.h +0 -70
- data/ext/iodine/http1_request.c +0 -81
- data/ext/iodine/http1_request.h +0 -58
- data/ext/iodine/http1_response.c +0 -417
- data/ext/iodine/http1_response.h +0 -95
- data/ext/iodine/http_request.c +0 -111
- data/ext/iodine/http_request.h +0 -102
- data/ext/iodine/http_response.c +0 -1703
- data/ext/iodine/http_response.h +0 -250
- data/ext/iodine/misc.c +0 -182
- data/ext/iodine/misc.h +0 -74
- data/ext/iodine/random.c +0 -208
- data/ext/iodine/redis_connection.c +0 -278
- data/ext/iodine/redis_connection.h +0 -86
- data/ext/iodine/resp.c +0 -842
- data/ext/iodine/resp.h +0 -261
- data/ext/iodine/siphash.c +0 -154
- data/ext/iodine/siphash.h +0 -22
- data/ext/iodine/xor-crypt.c +0 -193
- data/ext/iodine/xor-crypt.h +0 -107
data/ext/iodine/fiobject.h
CHANGED
@@ -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
|
-
|
32
|
-
|
33
|
-
|
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
|
-
|
39
|
-
|
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
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
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
|
89
|
+
FIO_INLINE const char *fiobj_type_name(const FIOBJ obj);
|
68
90
|
|
69
91
|
/**
|
70
|
-
*
|
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
|
-
|
95
|
+
FIO_INLINE FIOBJ fiobj_dup(FIOBJ);
|
80
96
|
|
81
97
|
/**
|
82
|
-
*
|
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(
|
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
|
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
|
110
|
-
*
|
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
|
122
|
+
* IO objects return the length of their data.
|
115
123
|
*
|
116
124
|
* A type error results in 0.
|
117
125
|
*/
|
118
|
-
|
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
|
124
|
-
*
|
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
|
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
|
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
|
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 `
|
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
|
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(
|
167
|
-
|
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 `
|
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
|
-
*
|
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
|
-
|
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
|
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
|
210
|
+
* Hash objects are order sensitive. To be equal, Hash keys must match in order.
|
206
211
|
*
|
207
|
-
*
|
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
|
-
*
|
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
|
-
*
|
212
|
-
*
|
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
|
-
*
|
215
|
-
|
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
|
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
|
-
|
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
|
-
*
|
496
|
+
* Returns a C String (NUL terminated) using the `fio_cstr_s` data type.
|
226
497
|
*
|
227
|
-
*
|
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
|
-
*
|
230
|
-
* base 2 (binary MSB first) respectively.
|
504
|
+
* Numbers will be represented in base 10 numerical data.
|
231
505
|
*
|
232
|
-
*
|
506
|
+
* A type error results in NULL (i.e. object isn't a String).
|
233
507
|
*/
|
234
|
-
|
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
|
-
|
237
|
-
|
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
|
-
*
|
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
|
-
*
|
243
|
-
* (for base 2).
|
581
|
+
* Hash order will be tested when comapring Hashes.
|
244
582
|
*
|
245
|
-
*
|
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
|
-
*
|
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
|
-
|
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
|
-
*
|
609
|
+
* Single layer iteration using a callback for each nested fio object.
|
255
610
|
*
|
256
|
-
*
|
257
|
-
* (
|
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
|
-
*
|
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
|
624
|
+
* Returns the "stop" position, i.e., the number of items processed + the
|
625
|
+
* starting point.
|
264
626
|
*/
|
265
|
-
size_t
|
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
|
-
#
|
268
|
-
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
|