iodine 0.4.14 → 0.4.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of iodine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +61 -33
- data/README.md +7 -5
- data/ext/iodine/base64.c +12 -0
- data/ext/iodine/defer.c +211 -108
- data/ext/iodine/defer.h +7 -0
- data/ext/iodine/facil.c +5 -1
- data/ext/iodine/facil.h +1 -1
- data/ext/iodine/fio2resp.c +19 -30
- data/ext/iodine/fio2resp.h +2 -1
- data/ext/iodine/fio_cli_helper.c +2 -2
- data/ext/iodine/fiobj.h +11 -624
- data/ext/iodine/fiobj_ary.c +65 -26
- data/ext/iodine/fiobj_ary.h +106 -0
- data/ext/iodine/fiobj_hash.c +175 -115
- data/ext/iodine/fiobj_hash.h +128 -0
- data/ext/iodine/fiobj_internal.c +189 -0
- data/ext/iodine/{fiobj_types.h → fiobj_internal.h} +126 -136
- data/ext/iodine/fiobj_json.c +161 -207
- data/ext/iodine/fiobj_json.h +43 -0
- data/ext/iodine/fiobj_numbers.c +53 -35
- data/ext/iodine/fiobj_numbers.h +49 -0
- data/ext/iodine/fiobj_primitives.c +103 -70
- data/ext/iodine/fiobj_primitives.h +55 -0
- data/ext/iodine/fiobj_str.c +171 -59
- data/ext/iodine/fiobj_str.h +113 -0
- data/ext/iodine/fiobj_sym.c +46 -124
- data/ext/iodine/fiobj_sym.h +60 -0
- data/ext/iodine/fiobject.c +589 -0
- data/ext/iodine/fiobject.h +276 -0
- data/ext/iodine/pubsub.h +1 -1
- data/ext/iodine/resp.c +2 -2
- data/ext/iodine/siphash.c +11 -0
- data/ext/iodine/spnlock.inc +7 -5
- data/ext/iodine/websocket_parser.h +44 -7
- data/lib/iodine/version.rb +1 -1
- metadata +13 -8
- data/ext/iodine/fiobj_alloc.c +0 -81
- data/ext/iodine/fiobj_generic.c +0 -260
- data/ext/iodine/fiobj_io.c +0 -58
- data/ext/iodine/fiobj_misc.c +0 -213
- data/ext/iodine/fiobj_tests.c +0 -474
data/ext/iodine/fiobj_sym.c
CHANGED
@@ -5,154 +5,70 @@ License: MIT
|
|
5
5
|
Feel free to copy, use and enjoy according to the license provided.
|
6
6
|
*/
|
7
7
|
|
8
|
-
#include "
|
8
|
+
#include "fiobj_internal.h"
|
9
9
|
|
10
10
|
/* *****************************************************************************
|
11
|
-
|
11
|
+
Symbol Type
|
12
12
|
***************************************************************************** */
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
#else
|
23
|
-
/* no need */
|
24
|
-
#define sip_local64(i) (i)
|
25
|
-
#endif
|
26
|
-
|
27
|
-
/* 64Bit left rotation, inlined. */
|
28
|
-
#define lrot64(i, bits) \
|
29
|
-
(((uint64_t)(i) << (bits)) | ((uint64_t)(i) >> (64 - (bits))))
|
30
|
-
|
31
|
-
uint64_t fiobj_sym_hash(const void *data, size_t len) {
|
32
|
-
/* initialize the 4 words */
|
33
|
-
uint64_t v0 = (0x0706050403020100ULL ^ 0x736f6d6570736575ULL);
|
34
|
-
uint64_t v1 = (0x0f0e0d0c0b0a0908ULL ^ 0x646f72616e646f6dULL);
|
35
|
-
uint64_t v2 = (0x0706050403020100ULL ^ 0x6c7967656e657261ULL);
|
36
|
-
uint64_t v3 = (0x0f0e0d0c0b0a0908ULL ^ 0x7465646279746573ULL);
|
37
|
-
const uint64_t *w64 = data;
|
38
|
-
uint8_t len_mod = len & 255;
|
39
|
-
union {
|
40
|
-
uint64_t i;
|
41
|
-
uint8_t str[8];
|
42
|
-
} word;
|
43
|
-
|
44
|
-
#define hash_map_SipRound \
|
45
|
-
do { \
|
46
|
-
v2 += v3; \
|
47
|
-
v3 = lrot64(v3, 16) ^ v2; \
|
48
|
-
v0 += v1; \
|
49
|
-
v1 = lrot64(v1, 13) ^ v0; \
|
50
|
-
v0 = lrot64(v0, 32); \
|
51
|
-
v2 += v1; \
|
52
|
-
v0 += v3; \
|
53
|
-
v1 = lrot64(v1, 17) ^ v2; \
|
54
|
-
v3 = lrot64(v3, 21) ^ v0; \
|
55
|
-
v2 = lrot64(v2, 32); \
|
56
|
-
} while (0);
|
57
|
-
|
58
|
-
while (len >= 8) {
|
59
|
-
word.i = sip_local64(*w64);
|
60
|
-
v3 ^= word.i;
|
61
|
-
/* Sip Rounds */
|
62
|
-
hash_map_SipRound;
|
63
|
-
hash_map_SipRound;
|
64
|
-
v0 ^= word.i;
|
65
|
-
w64 += 1;
|
66
|
-
len -= 8;
|
67
|
-
}
|
68
|
-
word.i = 0;
|
69
|
-
uint8_t *pos = word.str;
|
70
|
-
uint8_t *w8 = (void *)w64;
|
71
|
-
switch (len) { /* fallthrough is intentional */
|
72
|
-
case 7:
|
73
|
-
pos[6] = w8[6];
|
74
|
-
case 6:
|
75
|
-
pos[5] = w8[5];
|
76
|
-
case 5:
|
77
|
-
pos[4] = w8[4];
|
78
|
-
case 4:
|
79
|
-
pos[3] = w8[3];
|
80
|
-
case 3:
|
81
|
-
pos[2] = w8[2];
|
82
|
-
case 2:
|
83
|
-
pos[1] = w8[1];
|
84
|
-
case 1:
|
85
|
-
pos[0] = w8[0];
|
86
|
-
}
|
87
|
-
word.str[7] = len_mod;
|
88
|
-
|
89
|
-
/* last round */
|
90
|
-
v3 ^= word.i;
|
91
|
-
hash_map_SipRound;
|
92
|
-
hash_map_SipRound;
|
93
|
-
v0 ^= word.i;
|
94
|
-
/* Finalization */
|
95
|
-
v2 ^= 0xff;
|
96
|
-
/* d iterations of SipRound */
|
97
|
-
hash_map_SipRound;
|
98
|
-
hash_map_SipRound;
|
99
|
-
hash_map_SipRound;
|
100
|
-
hash_map_SipRound;
|
101
|
-
/* XOR it all together */
|
102
|
-
v0 ^= v1 ^ v2 ^ v3;
|
103
|
-
#undef hash_map_SipRound
|
104
|
-
return v0;
|
105
|
-
}
|
14
|
+
typedef struct {
|
15
|
+
struct fiobj_vtable_s *vtable;
|
16
|
+
uintptr_t hash;
|
17
|
+
uint64_t len;
|
18
|
+
char str[];
|
19
|
+
} fiobj_sym_s;
|
20
|
+
|
21
|
+
#define obj2sym(o) ((fiobj_sym_s *)(o))
|
106
22
|
|
107
23
|
/* *****************************************************************************
|
108
24
|
Symbol VTable
|
109
25
|
***************************************************************************** */
|
110
26
|
|
111
|
-
static int fiobj_sym_is_eq(fiobj_s *self, fiobj_s *other) {
|
112
|
-
if (
|
27
|
+
static int fiobj_sym_is_eq(const fiobj_s *self, const fiobj_s *other) {
|
28
|
+
if (other->type != self->type)
|
113
29
|
return 0;
|
114
30
|
return obj2sym(self)->hash == obj2sym(other)->hash;
|
115
31
|
}
|
116
32
|
|
117
|
-
static fio_cstr_s fio_sym2str(fiobj_s *o) {
|
33
|
+
static fio_cstr_s fio_sym2str(const fiobj_s *o) {
|
118
34
|
return (fio_cstr_s){.buffer = obj2sym(o)->str, .len = obj2sym(o)->len};
|
119
35
|
}
|
120
|
-
static int64_t fio_sym2i(fiobj_s *o) {
|
121
|
-
char *s =
|
36
|
+
static int64_t fio_sym2i(const fiobj_s *o) {
|
37
|
+
char *s = obj2sym(o)->str;
|
122
38
|
return fio_atol(&s);
|
123
39
|
}
|
124
|
-
static double fio_sym2f(fiobj_s *o) {
|
40
|
+
static double fio_sym2f(const fiobj_s *o) {
|
125
41
|
char *s = obj2sym(o)->str;
|
126
42
|
return fio_atof(&s);
|
127
43
|
}
|
128
44
|
|
129
45
|
static struct fiobj_vtable_s FIOBJ_VTABLE_SYMBOL = {
|
46
|
+
.name = "Symbol",
|
130
47
|
.free = fiobj_simple_dealloc,
|
131
48
|
.to_i = fio_sym2i,
|
132
49
|
.to_f = fio_sym2f,
|
133
50
|
.to_str = fio_sym2str,
|
134
51
|
.is_eq = fiobj_sym_is_eq,
|
52
|
+
.is_true = fiobj_noop_true,
|
135
53
|
.count = fiobj_noop_count,
|
54
|
+
.unwrap = fiobj_noop_unwrap,
|
136
55
|
.each1 = fiobj_noop_each1,
|
137
56
|
};
|
138
57
|
|
58
|
+
const uintptr_t FIOBJ_T_SYMBOL = (uintptr_t)(&FIOBJ_VTABLE_SYMBOL);
|
59
|
+
|
139
60
|
/* *****************************************************************************
|
140
61
|
Symbol API
|
141
62
|
***************************************************************************** */
|
142
63
|
|
143
64
|
static inline fiobj_s *fiobj_sym_alloc(size_t len) {
|
144
|
-
|
145
|
-
|
146
|
-
if (!head)
|
65
|
+
fiobj_s *o = fiobj_alloc(sizeof(fiobj_sym_s) + len + 1);
|
66
|
+
if (!o)
|
147
67
|
perror("ERROR: fiobj symbol couldn't allocate memory"), exit(errno);
|
148
|
-
*
|
149
|
-
.
|
68
|
+
*obj2sym(o) = (fiobj_sym_s){
|
69
|
+
.vtable = &FIOBJ_VTABLE_SYMBOL, .len = len,
|
150
70
|
};
|
151
|
-
|
152
|
-
.type = FIOBJ_T_SYMBOL, .len = len,
|
153
|
-
};
|
154
|
-
obj2sym(HEAD2OBJ(head))->str[len] = 0;
|
155
|
-
return HEAD2OBJ(head);
|
71
|
+
return o;
|
156
72
|
}
|
157
73
|
|
158
74
|
/** Creates a Symbol object. Use `fiobj_free`. */
|
@@ -160,10 +76,24 @@ fiobj_s *fiobj_sym_new(const char *str, size_t len) {
|
|
160
76
|
fiobj_s *s = fiobj_sym_alloc(len);
|
161
77
|
if (str)
|
162
78
|
memcpy(obj2sym(s)->str, str, len);
|
79
|
+
obj2sym(s)->str[len] = 0;
|
163
80
|
obj2sym(s)->hash = (uintptr_t)fiobj_sym_hash(str, len);
|
164
81
|
return s;
|
165
82
|
}
|
166
83
|
|
84
|
+
/** Finalizes a pre-allocated Symbol buffer to set it's final length and
|
85
|
+
* calculate it's final hashing value. */
|
86
|
+
fiobj_s *fiobj_sym_reinitialize(fiobj_s *s, const size_t len) {
|
87
|
+
if (obj2sym(s)->len < len)
|
88
|
+
fprintf(stderr,
|
89
|
+
"FATAL ERROR: facil.io Symbol object reinitialization error.\n"),
|
90
|
+
exit(-1);
|
91
|
+
obj2sym(s)->len = len;
|
92
|
+
obj2sym(s)->str[len] = 0;
|
93
|
+
obj2sym(s)->hash = (uintptr_t)fiobj_sym_hash(obj2sym(s)->str, len);
|
94
|
+
return s;
|
95
|
+
}
|
96
|
+
|
167
97
|
/** Creates a Symbol object using a printf like interface. */
|
168
98
|
__attribute__((format(printf, 1, 0))) fiobj_s *
|
169
99
|
fiobj_symvprintf(const char *format, va_list argv) {
|
@@ -172,16 +102,15 @@ fiobj_symvprintf(const char *format, va_list argv) {
|
|
172
102
|
va_copy(argv_cpy, argv);
|
173
103
|
int len = vsnprintf(NULL, 0, format, argv_cpy);
|
174
104
|
va_end(argv_cpy);
|
175
|
-
if (len
|
105
|
+
if (len <= 0) {
|
176
106
|
sym = fiobj_sym_alloc(0);
|
177
|
-
(
|
178
|
-
}
|
179
|
-
if (len <= 0)
|
107
|
+
obj2sym(sym)->hash = fiobj_sym_hash(NULL, 0);
|
180
108
|
return sym;
|
109
|
+
}
|
181
110
|
sym = fiobj_sym_alloc(len); /* adds 1 to len, for NUL */
|
182
|
-
vsnprintf((
|
183
|
-
(
|
184
|
-
|
111
|
+
vsnprintf(obj2sym(sym)->str, len + 1, format, argv);
|
112
|
+
obj2sym(sym)->str[len] = 0; /* enforce NUL */
|
113
|
+
obj2sym(sym)->hash = (uintptr_t)fiobj_sym_hash(obj2sym(sym)->str, len);
|
185
114
|
return sym;
|
186
115
|
}
|
187
116
|
__attribute__((format(printf, 1, 2))) fiobj_s *
|
@@ -193,13 +122,6 @@ fiobj_symprintf(const char *format, ...) {
|
|
193
122
|
return sym;
|
194
123
|
}
|
195
124
|
|
196
|
-
/** Returns 1 if both Symbols are equal and 0 if not. */
|
197
|
-
int fiobj_sym_iseql(fiobj_s *sym1, fiobj_s *sym2) {
|
198
|
-
if (sym1->type != FIOBJ_T_SYMBOL || sym2->type != FIOBJ_T_SYMBOL)
|
199
|
-
return 0;
|
200
|
-
return (((fio_sym_s *)sym1)->hash == ((fio_sym_s *)sym2)->hash);
|
201
|
-
}
|
202
|
-
|
203
125
|
/**
|
204
126
|
* Returns a symbol's identifier.
|
205
127
|
*
|
@@ -0,0 +1,60 @@
|
|
1
|
+
#ifndef H_FIOBJ_SYMBOL_H
|
2
|
+
/*
|
3
|
+
Copyright: Boaz Segev, 2017
|
4
|
+
License: MIT
|
5
|
+
*/
|
6
|
+
|
7
|
+
/**
|
8
|
+
*/
|
9
|
+
#define H_FIOBJ_SYMBOL_H
|
10
|
+
|
11
|
+
#include "fiobject.h"
|
12
|
+
|
13
|
+
#ifdef __cplusplus
|
14
|
+
extern "C" {
|
15
|
+
#endif
|
16
|
+
|
17
|
+
/** Symbol type identifier */
|
18
|
+
extern const uintptr_t FIOBJ_T_SYMBOL;
|
19
|
+
|
20
|
+
/* *****************************************************************************
|
21
|
+
Symbol API
|
22
|
+
***************************************************************************** */
|
23
|
+
|
24
|
+
/** Creates a Symbol object. Use `fiobj_free`. */
|
25
|
+
fiobj_s *fiobj_sym_new(const char *str, size_t len);
|
26
|
+
|
27
|
+
/** Creates a Symbol object using a printf like interface. */
|
28
|
+
__attribute__((format(printf, 1, 0))) fiobj_s *
|
29
|
+
fiobj_symvprintf(const char *format, va_list argv);
|
30
|
+
|
31
|
+
/** Creates a Symbol object using a printf like interface. */
|
32
|
+
__attribute__((format(printf, 1, 2))) fiobj_s *
|
33
|
+
fiobj_symprintf(const char *format, ...);
|
34
|
+
|
35
|
+
/**
|
36
|
+
* Returns a symbol's identifier.
|
37
|
+
*
|
38
|
+
* The unique identifier is calculated using SipHash and is equal for all Symbol
|
39
|
+
* objects that were created using the same data.
|
40
|
+
*/
|
41
|
+
uintptr_t fiobj_sym_id(fiobj_s *sym);
|
42
|
+
|
43
|
+
/* *****************************************************************************
|
44
|
+
Risky Symbol API
|
45
|
+
***************************************************************************** */
|
46
|
+
|
47
|
+
/**
|
48
|
+
* Reinitializes a pre-allocated Symbol buffer to set it's final length and
|
49
|
+
* calculate it's final hashing value.
|
50
|
+
*
|
51
|
+
* NEVER use this on a symbol that was already used in other objects, such as a
|
52
|
+
* Hash.
|
53
|
+
*/
|
54
|
+
fiobj_s *fiobj_sym_reinitialize(fiobj_s *s, const size_t len);
|
55
|
+
|
56
|
+
#ifdef __cplusplus
|
57
|
+
} /* extern "C" */
|
58
|
+
#endif
|
59
|
+
|
60
|
+
#endif
|
@@ -0,0 +1,589 @@
|
|
1
|
+
/*
|
2
|
+
Copyright: Boaz Segev, 2017
|
3
|
+
License: MIT
|
4
|
+
*/
|
5
|
+
|
6
|
+
/**
|
7
|
+
This facil.io core library provides wrappers around complex and (or) dynamic
|
8
|
+
types, abstracting some complexity and making dynamic type related tasks easier.
|
9
|
+
|
10
|
+
|
11
|
+
The library offers a rudementry protection against cyclic references using the
|
12
|
+
`FIOBJ_NESTING_PROTECTION` flag (i.e., nesting an Array within itself)...
|
13
|
+
however, this isn't fully tested and the performance price is high.
|
14
|
+
*/
|
15
|
+
#include "fiobj_internal.h"
|
16
|
+
#include "fiobj_primitives.h"
|
17
|
+
|
18
|
+
#include <stdarg.h>
|
19
|
+
#include <stdint.h>
|
20
|
+
#include <stdio.h>
|
21
|
+
#include <stdlib.h>
|
22
|
+
|
23
|
+
/* *****************************************************************************
|
24
|
+
Cyclic Protection helpers & API
|
25
|
+
***************************************************************************** */
|
26
|
+
|
27
|
+
static __thread fiobj_s *fiobj_cyclic_protection = NULL;
|
28
|
+
fiobj_s *fiobj_each_get_cyclic(void) { return fiobj_cyclic_protection; }
|
29
|
+
|
30
|
+
static inline fiobj_s *protected_pop_obj(fio_ls_s *queue, fio_ls_s *history) {
|
31
|
+
#if FIOBJ_NESTING_PROTECTION
|
32
|
+
fiobj_cyclic_protection = NULL;
|
33
|
+
|
34
|
+
fiobj_s *obj = fio_ls_pop(queue);
|
35
|
+
if (!obj)
|
36
|
+
return NULL;
|
37
|
+
fiobj_s *child = OBJVTBL(obj)->unwrap(obj);
|
38
|
+
if (!child)
|
39
|
+
return obj;
|
40
|
+
if (OBJVTBL(child)->count(child) == 0)
|
41
|
+
return obj;
|
42
|
+
fio_ls_s *pos = history->next;
|
43
|
+
while (pos != history) {
|
44
|
+
if (child == pos->obj) {
|
45
|
+
fiobj_cyclic_protection = obj;
|
46
|
+
return NULL;
|
47
|
+
}
|
48
|
+
pos = pos->next;
|
49
|
+
}
|
50
|
+
return obj;
|
51
|
+
#else
|
52
|
+
return fio_ls_pop(queue);
|
53
|
+
(void)history;
|
54
|
+
#endif
|
55
|
+
}
|
56
|
+
|
57
|
+
static inline void protected_push_obj(const fiobj_s *obj, fio_ls_s *history) {
|
58
|
+
#if FIOBJ_NESTING_PROTECTION
|
59
|
+
fio_ls_push(history, OBJVTBL(obj)->unwrap(obj));
|
60
|
+
#else
|
61
|
+
(void)obj;
|
62
|
+
(void)history;
|
63
|
+
#endif
|
64
|
+
}
|
65
|
+
|
66
|
+
/* *****************************************************************************
|
67
|
+
Generic Object API
|
68
|
+
***************************************************************************** */
|
69
|
+
|
70
|
+
/** Returns a C string naming the objects dynamic type. */
|
71
|
+
const char *fiobj_type_name(const fiobj_s *obj) { return OBJVTBL(obj)->name; }
|
72
|
+
|
73
|
+
/**
|
74
|
+
* Copy by reference(!) - increases an object's (and any nested object's)
|
75
|
+
* reference count.
|
76
|
+
*
|
77
|
+
* Always returns the value passed along.
|
78
|
+
*
|
79
|
+
* Future implementations might provide `fiobj_dup2` providing a deep copy.
|
80
|
+
*
|
81
|
+
* We don't need this feature just yet, so I'm not working on it.
|
82
|
+
*/
|
83
|
+
fiobj_s *fiobj_dup(fiobj_s *obj) {
|
84
|
+
OBJREF_ADD(obj);
|
85
|
+
return obj;
|
86
|
+
}
|
87
|
+
|
88
|
+
static int fiobj_free_or_mark(fiobj_s *o, void *arg) {
|
89
|
+
if (!o)
|
90
|
+
return 0;
|
91
|
+
#if FIOBJ_NESTING_PROTECTION
|
92
|
+
if (OBJ2HEAD(o)->ref == 0) /* maybe a nested returning... */
|
93
|
+
return 0;
|
94
|
+
#elif DEBUG
|
95
|
+
if (OBJ2HEAD(o)->ref == 0) {
|
96
|
+
fprintf(stderr,
|
97
|
+
"ERROR: attempting to free an object that isn't a fiobj or already "
|
98
|
+
"freed (%p)\n",
|
99
|
+
(void *)o);
|
100
|
+
kill(0, SIGABRT);
|
101
|
+
}
|
102
|
+
#endif
|
103
|
+
|
104
|
+
if (OBJREF_REM(o))
|
105
|
+
return 0;
|
106
|
+
|
107
|
+
/* reference count is zero: free memory or add to queue */
|
108
|
+
|
109
|
+
/* test for wrapped object (i.e., Hash Couplet) */
|
110
|
+
fiobj_s *child = OBJVTBL(o)->unwrap(o);
|
111
|
+
|
112
|
+
if (child != o) {
|
113
|
+
if (!child || OBJREF_REM(child)) {
|
114
|
+
OBJVTBL(o)->free(o);
|
115
|
+
return 0;
|
116
|
+
}
|
117
|
+
OBJVTBL(o)->free(o);
|
118
|
+
o = child;
|
119
|
+
}
|
120
|
+
|
121
|
+
if (OBJVTBL(o)->count(o)) {
|
122
|
+
fio_ls_push(arg, o);
|
123
|
+
} else
|
124
|
+
OBJVTBL(o)->free(o);
|
125
|
+
|
126
|
+
/* handle nesting / wrapping (i.e., Array, Hash, Couplets ) */
|
127
|
+
return 0;
|
128
|
+
}
|
129
|
+
|
130
|
+
/**
|
131
|
+
* Decreases an object's reference count, releasing memory and
|
132
|
+
* resources.
|
133
|
+
*
|
134
|
+
* This function affects nested objects, meaning that when an Array or
|
135
|
+
* a Hash object is passed along, it's children (nested objects) are
|
136
|
+
* also freed.
|
137
|
+
*/
|
138
|
+
void fiobj_free(fiobj_s *o) {
|
139
|
+
#if DEBUG
|
140
|
+
if (!o)
|
141
|
+
return;
|
142
|
+
if (OBJ2HEAD(o)->ref == 0) {
|
143
|
+
fprintf(stderr,
|
144
|
+
"ERROR: attempting to free an object that isn't a fiobj or already "
|
145
|
+
"freed (%p)\n",
|
146
|
+
(void *)o);
|
147
|
+
kill(0, SIGABRT);
|
148
|
+
}
|
149
|
+
#endif
|
150
|
+
if (!o || OBJREF_REM(o))
|
151
|
+
return;
|
152
|
+
|
153
|
+
/* handle wrapping */
|
154
|
+
{
|
155
|
+
fiobj_s *child = OBJVTBL(o)->unwrap(o);
|
156
|
+
if (child != o) {
|
157
|
+
OBJVTBL(o)->free(o);
|
158
|
+
if (OBJREF_REM(child))
|
159
|
+
return;
|
160
|
+
o = child;
|
161
|
+
}
|
162
|
+
}
|
163
|
+
if (OBJVTBL(o)->count(o) == 0) {
|
164
|
+
OBJVTBL(o)->free(o);
|
165
|
+
return;
|
166
|
+
}
|
167
|
+
/* nested free */
|
168
|
+
fio_ls_s queue = FIO_LS_INIT(queue);
|
169
|
+
fio_ls_s history = FIO_LS_INIT(history);
|
170
|
+
while (o) {
|
171
|
+
/* the queue always contains valid enumerable objects that are unwrapped. */
|
172
|
+
OBJVTBL(o)->each1(o, 0, fiobj_free_or_mark, &queue);
|
173
|
+
fio_ls_push(&history, o);
|
174
|
+
o = protected_pop_obj(&queue, &history);
|
175
|
+
}
|
176
|
+
/* clean up and free enumerables */
|
177
|
+
while ((o = fio_ls_pop(&history)))
|
178
|
+
OBJVTBL(o)->free(o);
|
179
|
+
return;
|
180
|
+
}
|
181
|
+
|
182
|
+
/**
|
183
|
+
* Attempts to return the object's current reference count.
|
184
|
+
*
|
185
|
+
* This is mostly for testing rather than normal library operations.
|
186
|
+
*/
|
187
|
+
uintptr_t fiobj_reference_count(const fiobj_s *o) { return OBJ2HEAD(o)->ref; }
|
188
|
+
|
189
|
+
/**
|
190
|
+
* Tests if an object evaluates as TRUE.
|
191
|
+
*
|
192
|
+
* This is object type specific. For example, empty strings might evaluate as
|
193
|
+
* FALSE, even though they aren't a boolean type.
|
194
|
+
*/
|
195
|
+
int fiobj_is_true(const fiobj_s *o) { return (o && OBJVTBL(o)->is_true(o)); }
|
196
|
+
|
197
|
+
/**
|
198
|
+
* Returns an Object's numerical value.
|
199
|
+
*
|
200
|
+
* If a String or Symbol are passed to the function, they will be
|
201
|
+
* parsed assuming base 10 numerical data.
|
202
|
+
*
|
203
|
+
* Hashes and Arrays return their object count.
|
204
|
+
*
|
205
|
+
* IO and File objects return their underlying file descriptor.
|
206
|
+
*
|
207
|
+
* A type error results in 0.
|
208
|
+
*/
|
209
|
+
int64_t fiobj_obj2num(const fiobj_s *o) { return o ? OBJVTBL(o)->to_i(o) : 0; }
|
210
|
+
|
211
|
+
/**
|
212
|
+
* Returns a Float's value.
|
213
|
+
*
|
214
|
+
* If a String or Symbol are passed to the function, they will be
|
215
|
+
* parsed assuming base 10 numerical data.
|
216
|
+
*
|
217
|
+
* Hashes and Arrays return their object count.
|
218
|
+
*
|
219
|
+
* IO and File objects return their underlying file descriptor.
|
220
|
+
*
|
221
|
+
* A type error results in 0.
|
222
|
+
*/
|
223
|
+
double fiobj_obj2float(const fiobj_s *o) { return o ? OBJVTBL(o)->to_f(o) : 0; }
|
224
|
+
|
225
|
+
/**
|
226
|
+
* Returns a C String (NUL terminated) using the `fio_cstr_s` data type.
|
227
|
+
*
|
228
|
+
* The Sting in binary safe and might contain NUL bytes in the middle as well as
|
229
|
+
* a terminating NUL.
|
230
|
+
*
|
231
|
+
* If a Symbol, a Number or a Float are passed to the function, they
|
232
|
+
* will be parsed as a *temporary*, thread-safe, String.
|
233
|
+
*
|
234
|
+
* Numbers will be represented in base 10 numerical data.
|
235
|
+
*
|
236
|
+
* A type error results in NULL (i.e. object isn't a String).
|
237
|
+
*/
|
238
|
+
fio_cstr_s fiobj_obj2cstr(const fiobj_s *o) {
|
239
|
+
return o ? OBJVTBL(o)->to_str(o) : fiobj_noop_str(NULL);
|
240
|
+
}
|
241
|
+
|
242
|
+
/**
|
243
|
+
* Single layer iteration using a callback for each nested fio object.
|
244
|
+
*
|
245
|
+
* Accepts any `fiobj_s *` type but only collections (Arrays and Hashes) are
|
246
|
+
* processed. The container itself (the Array or the Hash) is **not** processed
|
247
|
+
* (unlike `fiobj_each2`).
|
248
|
+
*
|
249
|
+
* The callback task function must accept an object and an opaque user pointer.
|
250
|
+
*
|
251
|
+
* Hash objects pass along a `FIOBJ_T_COUPLET` object, containing
|
252
|
+
* references for both the key (Symbol) and the object (any object).
|
253
|
+
*
|
254
|
+
* If the callback returns -1, the loop is broken. Any other value is ignored.
|
255
|
+
*
|
256
|
+
* Returns the "stop" position, i.e., the number of items processed + the
|
257
|
+
* starting point.
|
258
|
+
*/
|
259
|
+
size_t fiobj_each1(fiobj_s *o, size_t start_at,
|
260
|
+
int (*task)(fiobj_s *obj, void *arg), void *arg) {
|
261
|
+
return o ? OBJVTBL(o)->each1(o, start_at, task, arg) : 0;
|
262
|
+
}
|
263
|
+
|
264
|
+
/* *****************************************************************************
|
265
|
+
Nested concern (each2, is_eq)
|
266
|
+
***************************************************************************** */
|
267
|
+
|
268
|
+
static int each2_add_to_queue(fiobj_s *obj, void *arg) {
|
269
|
+
fio_ls_s *const queue = arg;
|
270
|
+
fio_ls_unshift(queue, obj);
|
271
|
+
return 0;
|
272
|
+
}
|
273
|
+
|
274
|
+
/**
|
275
|
+
* Deep iteration using a callback for each fio object, including the parent.
|
276
|
+
*
|
277
|
+
*
|
278
|
+
* Notice that when passing collections to the function, the collection itself
|
279
|
+
* is sent to the callback followed by it's children (if any). This is true also
|
280
|
+
* for nested collections (a nested Hash will be sent first, followed by the
|
281
|
+
* nested Hash's children and then followed by the rest of it's siblings.
|
282
|
+
*
|
283
|
+
* If the callback returns -1, the loop is broken. Any other value is ignored.
|
284
|
+
*/
|
285
|
+
void fiobj_each2(fiobj_s *obj, int (*task)(fiobj_s *obj, void *arg),
|
286
|
+
void *arg) {
|
287
|
+
if (!obj)
|
288
|
+
goto single;
|
289
|
+
size_t count = OBJVTBL(obj)->count(obj);
|
290
|
+
if (!count)
|
291
|
+
goto single;
|
292
|
+
|
293
|
+
fio_ls_s queue = FIO_LS_INIT(queue), history = FIO_LS_INIT(history);
|
294
|
+
while (obj || queue.next != &queue) {
|
295
|
+
int i = task(obj, arg);
|
296
|
+
if (i == -1)
|
297
|
+
goto finish;
|
298
|
+
if (obj && OBJVTBL(obj)->count(obj)) {
|
299
|
+
protected_push_obj(obj, &history);
|
300
|
+
OBJVTBL(obj)->each1(obj, 0, each2_add_to_queue, queue.next);
|
301
|
+
}
|
302
|
+
obj = protected_pop_obj(&queue, &history);
|
303
|
+
}
|
304
|
+
finish:
|
305
|
+
while (fio_ls_pop(&history))
|
306
|
+
;
|
307
|
+
while (fio_ls_pop(&queue))
|
308
|
+
;
|
309
|
+
return;
|
310
|
+
single:
|
311
|
+
task(obj, arg);
|
312
|
+
return;
|
313
|
+
}
|
314
|
+
|
315
|
+
/**
|
316
|
+
* Deeply compare two objects. No hashing is involved.
|
317
|
+
*
|
318
|
+
* KNOWN ISSUES:
|
319
|
+
*
|
320
|
+
* * Cyclic nesting might cause this function to hang (much like `fiobj_each2`).
|
321
|
+
*
|
322
|
+
* * `FIOBJ_NESTING_PROTECTION` might be ignored when testing nested objects.
|
323
|
+
*
|
324
|
+
* * Hash order might be ignored when comapring Hashes, which means that equal
|
325
|
+
* Hases might behave differently during iteration.
|
326
|
+
*
|
327
|
+
*/
|
328
|
+
int fiobj_iseq(const fiobj_s *self, const fiobj_s *other) {
|
329
|
+
if (self == other)
|
330
|
+
return 1;
|
331
|
+
if (!self)
|
332
|
+
return other->type == FIOBJ_T_NULL;
|
333
|
+
if (!other)
|
334
|
+
return self->type == FIOBJ_T_NULL;
|
335
|
+
|
336
|
+
if (!OBJVTBL(self)->is_eq(self, other))
|
337
|
+
return 0;
|
338
|
+
if (!OBJVTBL(self)->count(self))
|
339
|
+
return 1;
|
340
|
+
|
341
|
+
uint8_t eq = 0;
|
342
|
+
fio_ls_s self_queue = FIO_LS_INIT(self_queue);
|
343
|
+
fio_ls_s self_history = FIO_LS_INIT(self_history);
|
344
|
+
fio_ls_s other_queue = FIO_LS_INIT(other_queue);
|
345
|
+
fio_ls_s other_history = FIO_LS_INIT(other_history);
|
346
|
+
|
347
|
+
while (self) {
|
348
|
+
protected_push_obj(self, &self_history);
|
349
|
+
protected_push_obj(other, &other_history);
|
350
|
+
OBJVTBL(self)->each1((fiobj_s *)self, 0, each2_add_to_queue,
|
351
|
+
self_queue.next);
|
352
|
+
OBJVTBL(other)->each1((fiobj_s *)other, 0, each2_add_to_queue,
|
353
|
+
other_queue.next);
|
354
|
+
while (self_queue.next != &self_queue || self) {
|
355
|
+
self = protected_pop_obj(&self_queue, &self_history);
|
356
|
+
other = protected_pop_obj(&other_queue, &other_history);
|
357
|
+
if (self == other)
|
358
|
+
continue;
|
359
|
+
if (!self && other->type != FIOBJ_T_NULL)
|
360
|
+
goto finish;
|
361
|
+
if (!other && self->type != FIOBJ_T_NULL)
|
362
|
+
goto finish;
|
363
|
+
if (OBJVTBL(self)->count(self))
|
364
|
+
break;
|
365
|
+
if (!OBJVTBL(self)->is_eq(self, other))
|
366
|
+
goto finish;
|
367
|
+
}
|
368
|
+
if (self && !OBJVTBL(self)->is_eq(self, other))
|
369
|
+
goto finish;
|
370
|
+
}
|
371
|
+
eq = 1;
|
372
|
+
|
373
|
+
finish:
|
374
|
+
while (fio_ls_pop(&self_history))
|
375
|
+
;
|
376
|
+
while (fio_ls_pop(&self_queue))
|
377
|
+
;
|
378
|
+
while (fio_ls_pop(&other_history))
|
379
|
+
;
|
380
|
+
while (fio_ls_pop(&other_queue))
|
381
|
+
;
|
382
|
+
return eq;
|
383
|
+
}
|
384
|
+
|
385
|
+
/* *****************************************************************************
|
386
|
+
Number and Float Helpers
|
387
|
+
***************************************************************************** */
|
388
|
+
static const char hex_notation[] = {'0', '1', '2', '3', '4', '5', '6', '7',
|
389
|
+
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
390
|
+
|
391
|
+
/**
|
392
|
+
* A helper function that converts between String data to a signed int64_t.
|
393
|
+
*
|
394
|
+
* Numbers are assumed to be in base 10. `0x##` (or `x##`) and `0b##` (or
|
395
|
+
* `b##`) are recognized as base 16 and base 2 (binary MSB first)
|
396
|
+
* respectively.
|
397
|
+
*/
|
398
|
+
int64_t fio_atol(char **pstr) {
|
399
|
+
char *str = *pstr;
|
400
|
+
uint64_t result = 0;
|
401
|
+
uint8_t invert = 0;
|
402
|
+
while (str[0] == '0')
|
403
|
+
str++;
|
404
|
+
while (str[0] == '-') {
|
405
|
+
invert ^= 1;
|
406
|
+
str++;
|
407
|
+
}
|
408
|
+
while (str[0] == '0')
|
409
|
+
str++;
|
410
|
+
if (str[0] == 'b' || str[0] == 'B') {
|
411
|
+
/* base 2 */
|
412
|
+
str++;
|
413
|
+
while (str[0] == '0' || str[0] == '1') {
|
414
|
+
result = (result << 1) | (str[0] == '1');
|
415
|
+
str++;
|
416
|
+
}
|
417
|
+
} else if (str[0] == 'x' || str[0] == 'X') {
|
418
|
+
/* base 16 */
|
419
|
+
uint8_t tmp;
|
420
|
+
str++;
|
421
|
+
while (1) {
|
422
|
+
if (str[0] >= '0' && str[0] <= '9')
|
423
|
+
tmp = str[0] - '0';
|
424
|
+
else if (str[0] >= 'A' && str[0] <= 'F')
|
425
|
+
tmp = str[0] - ('A' - 10);
|
426
|
+
else if (str[0] >= 'a' && str[0] <= 'f')
|
427
|
+
tmp = str[0] - ('a' - 10);
|
428
|
+
else
|
429
|
+
goto finish;
|
430
|
+
result = (result << 4) | tmp;
|
431
|
+
str++;
|
432
|
+
}
|
433
|
+
} else {
|
434
|
+
/* base 10 */
|
435
|
+
const char *end = str;
|
436
|
+
while (end[0] >= '0' && end[0] <= '9' && (uintptr_t)(end - str) < 22)
|
437
|
+
end++;
|
438
|
+
if ((uintptr_t)(end - str) > 21) /* too large for a number */
|
439
|
+
return 0;
|
440
|
+
|
441
|
+
while (str < end) {
|
442
|
+
result = (result * 10) + (str[0] - '0');
|
443
|
+
str++;
|
444
|
+
}
|
445
|
+
}
|
446
|
+
finish:
|
447
|
+
if (invert)
|
448
|
+
result = 0 - result;
|
449
|
+
*pstr = str;
|
450
|
+
return (int64_t)result;
|
451
|
+
}
|
452
|
+
|
453
|
+
/** A helper function that convers between String data to a signed double. */
|
454
|
+
double fio_atof(char **pstr) { return strtold(*pstr, pstr); }
|
455
|
+
|
456
|
+
/* *****************************************************************************
|
457
|
+
String Helpers
|
458
|
+
***************************************************************************** */
|
459
|
+
|
460
|
+
/**
|
461
|
+
* A helper function that convers between a signed int64_t to a string.
|
462
|
+
*
|
463
|
+
* No overflow guard is provided, make sure there's at least 66 bytes
|
464
|
+
* available (for base 2).
|
465
|
+
*
|
466
|
+
* Supports base 2, base 10 and base 16. An unsupported base will silently
|
467
|
+
* default to base 10. Prefixes aren't added (i.e., no "0x" or "0b" at the
|
468
|
+
* beginning of the string).
|
469
|
+
*
|
470
|
+
* Returns the number of bytes actually written (excluding the NUL
|
471
|
+
* terminator).
|
472
|
+
*/
|
473
|
+
size_t fio_ltoa(char *dest, int64_t num, uint8_t base) {
|
474
|
+
if (!num) {
|
475
|
+
*(dest++) = '0';
|
476
|
+
*(dest++) = 0;
|
477
|
+
return 1;
|
478
|
+
}
|
479
|
+
|
480
|
+
size_t len = 0;
|
481
|
+
|
482
|
+
if (base == 2) {
|
483
|
+
uint64_t n = num; /* avoid bit shifting inconsistencies with signed bit */
|
484
|
+
uint8_t i = 0; /* counting bits */
|
485
|
+
|
486
|
+
while ((i < 64) && (n & 0x8000000000000000) == 0) {
|
487
|
+
n = n << 1;
|
488
|
+
i++;
|
489
|
+
}
|
490
|
+
/* make sure the Binary representation doesn't appear signed. */
|
491
|
+
if (i) {
|
492
|
+
dest[len++] = '0';
|
493
|
+
}
|
494
|
+
/* write to dest. */
|
495
|
+
while (i < 64) {
|
496
|
+
dest[len++] = ((n & 0x8000000000000000) ? '1' : '0');
|
497
|
+
n = n << 1;
|
498
|
+
i++;
|
499
|
+
}
|
500
|
+
dest[len] = 0;
|
501
|
+
return len;
|
502
|
+
|
503
|
+
} else if (base == 16) {
|
504
|
+
uint64_t n = num; /* avoid bit shifting inconsistencies with signed bit */
|
505
|
+
uint8_t i = 0; /* counting bytes */
|
506
|
+
uint8_t tmp = 0;
|
507
|
+
while (i < 8 && (n & 0xFF00000000000000) == 0) {
|
508
|
+
n = n << 8;
|
509
|
+
i++;
|
510
|
+
}
|
511
|
+
/* make sure the Hex representation doesn't appear signed. */
|
512
|
+
if (i && (n & 0x8000000000000000)) {
|
513
|
+
dest[len++] = '0';
|
514
|
+
dest[len++] = '0';
|
515
|
+
}
|
516
|
+
/* write the damn thing */
|
517
|
+
while (i < 8) {
|
518
|
+
tmp = (n & 0xF000000000000000) >> 60;
|
519
|
+
dest[len++] = hex_notation[tmp];
|
520
|
+
tmp = (n & 0x0F00000000000000) >> 56;
|
521
|
+
dest[len++] = hex_notation[tmp];
|
522
|
+
i++;
|
523
|
+
n = n << 8;
|
524
|
+
}
|
525
|
+
dest[len] = 0;
|
526
|
+
return len;
|
527
|
+
}
|
528
|
+
|
529
|
+
/* fallback to base 10 */
|
530
|
+
uint64_t rem = 0;
|
531
|
+
uint64_t factor = 1;
|
532
|
+
if (num < 0) {
|
533
|
+
dest[len++] = '-';
|
534
|
+
num = 0 - num;
|
535
|
+
}
|
536
|
+
|
537
|
+
while (num / factor)
|
538
|
+
factor *= 10;
|
539
|
+
|
540
|
+
while (factor > 1) {
|
541
|
+
factor = factor / 10;
|
542
|
+
rem = (rem * 10);
|
543
|
+
dest[len++] = '0' + ((num / factor) - rem);
|
544
|
+
rem += ((num / factor) - rem);
|
545
|
+
}
|
546
|
+
dest[len] = 0;
|
547
|
+
return len;
|
548
|
+
}
|
549
|
+
|
550
|
+
/**
|
551
|
+
* A helper function that convers between a double to a string.
|
552
|
+
*
|
553
|
+
* No overflow guard is provided, make sure there's at least 130 bytes
|
554
|
+
* available (for base 2).
|
555
|
+
*
|
556
|
+
* Supports base 2, base 10 and base 16. An unsupported base will silently
|
557
|
+
* default to base 10. Prefixes aren't added (i.e., no "0x" or "0b" at the
|
558
|
+
* beginning of the string).
|
559
|
+
*
|
560
|
+
* Returns the number of bytes actually written (excluding the NUL
|
561
|
+
* terminator).
|
562
|
+
*/
|
563
|
+
size_t fio_ftoa(char *dest, double num, uint8_t base) {
|
564
|
+
if (base == 2 || base == 16) {
|
565
|
+
/* handle the binary / Hex representation the same as if it were an
|
566
|
+
* int64_t
|
567
|
+
*/
|
568
|
+
int64_t *i = (void *)#
|
569
|
+
return fio_ltoa(dest, *i, base);
|
570
|
+
}
|
571
|
+
|
572
|
+
size_t written = sprintf(dest, "%g", num);
|
573
|
+
uint8_t need_zero = 1;
|
574
|
+
char *start = dest;
|
575
|
+
while (*start) {
|
576
|
+
if (*start == ',') // locale issues?
|
577
|
+
*start = '.';
|
578
|
+
if (*start == '.' || *start == 'e') {
|
579
|
+
need_zero = 0;
|
580
|
+
break;
|
581
|
+
}
|
582
|
+
start++;
|
583
|
+
}
|
584
|
+
if (need_zero) {
|
585
|
+
dest[written++] = '.';
|
586
|
+
dest[written++] = '0';
|
587
|
+
}
|
588
|
+
return written;
|
589
|
+
}
|