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/fiobj_str.h
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
#ifndef H_FIOBJ_STR_H
|
2
2
|
/*
|
3
|
-
Copyright: Boaz Segev, 2017
|
3
|
+
Copyright: Boaz Segev, 2017-2018
|
4
4
|
License: MIT
|
5
5
|
*/
|
6
6
|
#define H_FIOBJ_STR_H
|
@@ -11,27 +11,20 @@ License: MIT
|
|
11
11
|
extern "C" {
|
12
12
|
#endif
|
13
13
|
|
14
|
-
|
15
|
-
extern const uintptr_t FIOBJ_T_STRING;
|
16
|
-
|
17
|
-
/** Static String type identifier */
|
18
|
-
extern const uintptr_t FIOBJ_T_STRING_STATIC;
|
19
|
-
|
20
|
-
#define FIOBJ_IS_STRING(obj) \
|
21
|
-
(obj->type == FIOBJ_T_STRING || obj->type == FIOBJ_T_STRING_STATIC)
|
14
|
+
#define FIOBJ_IS_STRING(obj) FIOBJ_TYPE_IS((obj), FIOBJ_T_STRING)
|
22
15
|
|
23
16
|
/* *****************************************************************************
|
24
|
-
String
|
17
|
+
API: Creating a String Object
|
25
18
|
***************************************************************************** */
|
26
19
|
|
27
20
|
/** Creates a String object. Remember to use `fiobj_free`. */
|
28
|
-
|
21
|
+
FIOBJ fiobj_str_new(const char *str, size_t len);
|
29
22
|
|
30
23
|
/** Creates a buffer String object. capa includes NUL.
|
31
24
|
*
|
32
25
|
* Remember to use `fiobj_free`.
|
33
26
|
*/
|
34
|
-
|
27
|
+
FIOBJ fiobj_str_buf(size_t capa);
|
35
28
|
|
36
29
|
/**
|
37
30
|
* Creates a static String object from a static C string. Remember `fiobj_free`.
|
@@ -43,68 +36,124 @@ fiobj_s *fiobj_str_buf(size_t capa);
|
|
43
36
|
*
|
44
37
|
* NOTICE: static strings can't be written to.
|
45
38
|
*/
|
46
|
-
|
39
|
+
FIOBJ fiobj_str_static(const char *str, size_t len);
|
47
40
|
|
48
41
|
/** Creates a copy from an existing String. Remember to use `fiobj_free`. */
|
49
|
-
|
42
|
+
static inline __attribute__((unused)) FIOBJ fiobj_str_copy(FIOBJ src) {
|
43
|
+
fio_cstr_s s = fiobj_obj2cstr(src);
|
44
|
+
return fiobj_str_new(s.data, s.len);
|
45
|
+
}
|
46
|
+
|
47
|
+
/**
|
48
|
+
* Creates a String object. Remember to use `fiobj_free`.
|
49
|
+
*
|
50
|
+
* The ownership of the memory indicated by `str` will now "move" to the object.
|
51
|
+
*
|
52
|
+
* The original memory MUST be allocated using `fio_malloc` (NOT the system's
|
53
|
+
* `malloc`) and it will be freed by the `fiobj` library using `fio_free`.
|
54
|
+
*/
|
55
|
+
FIOBJ fiobj_str_move(char *str, size_t len, size_t capacity);
|
56
|
+
|
57
|
+
/**
|
58
|
+
* Returns a thread-static temporary string. Avoid calling `fiobj_dup` or
|
59
|
+
* `fiobj_free`.
|
60
|
+
*/
|
61
|
+
FIOBJ fiobj_str_tmp(void);
|
50
62
|
|
51
63
|
/** Creates a String object using a printf like interface. */
|
52
|
-
__attribute__((format(printf, 1, 0)))
|
53
|
-
|
64
|
+
__attribute__((format(printf, 1, 0))) FIOBJ fiobj_strvprintf(const char *format,
|
65
|
+
va_list argv);
|
54
66
|
|
55
67
|
/** Creates a String object using a printf like interface. */
|
56
|
-
__attribute__((format(printf, 1, 2)))
|
57
|
-
|
68
|
+
__attribute__((format(printf, 1, 2))) FIOBJ fiobj_strprintf(const char *format,
|
69
|
+
...);
|
58
70
|
|
59
71
|
/** Dumps the `filename` file's contents into a new String. If `limit == 0`,
|
60
72
|
* than the data will be read until EOF.
|
61
73
|
*
|
62
|
-
* If the file can't be located, opened or read, or if `start_at` is
|
63
|
-
* EOF position,
|
74
|
+
* If the file can't be located, opened or read, or if `start_at` is out of
|
75
|
+
* bounds (i.e., beyond the EOF position), FIOBJ_INVALID is returned.
|
76
|
+
*
|
77
|
+
* If `start_at` is negative, it will be computed from the end of the file.
|
64
78
|
*
|
65
79
|
* Remember to use `fiobj_free`.
|
66
80
|
*
|
67
|
-
* NOTE: Requires a UNIX system, otherwise always returns
|
81
|
+
* NOTE: Requires a UNIX system, otherwise always returns FIOBJ_INVALID.
|
82
|
+
*/
|
83
|
+
FIOBJ fiobj_str_readfile(const char *filename, intptr_t start_at,
|
84
|
+
intptr_t limit);
|
85
|
+
|
86
|
+
/* *****************************************************************************
|
87
|
+
API: Editing a String
|
88
|
+
***************************************************************************** */
|
89
|
+
|
90
|
+
/**
|
91
|
+
* Prevents the String object from being changed.
|
92
|
+
*
|
93
|
+
* When a String is used as a key for a Hash, it is automatically frozenn to
|
94
|
+
* prevent the Hash from becoming broken.
|
95
|
+
*
|
96
|
+
* A call to `fiobj_str_hash` or `fiobj_obj2hash` will automactically freeze the
|
97
|
+
* String.
|
68
98
|
*/
|
69
|
-
|
70
|
-
|
99
|
+
void fiobj_str_freeze(FIOBJ str);
|
100
|
+
|
71
101
|
/**
|
72
102
|
* Confirms the requested capacity is available and allocates as required.
|
73
103
|
*
|
74
104
|
* Returns updated capacity.
|
75
105
|
*/
|
76
|
-
size_t fiobj_str_capa_assert(
|
106
|
+
size_t fiobj_str_capa_assert(FIOBJ str, size_t size);
|
77
107
|
|
78
108
|
/** Return's a String's capacity, if any. This should include the NUL byte. */
|
79
|
-
size_t fiobj_str_capa(
|
109
|
+
size_t fiobj_str_capa(FIOBJ str);
|
80
110
|
|
81
111
|
/** Resizes a String object, allocating more memory if required. */
|
82
|
-
void fiobj_str_resize(
|
112
|
+
void fiobj_str_resize(FIOBJ str, size_t size);
|
83
113
|
|
84
114
|
/** Deallocates any unnecessary memory (if supported by OS). */
|
85
|
-
void fiobj_str_minimize(
|
115
|
+
void fiobj_str_minimize(FIOBJ str);
|
86
116
|
|
87
117
|
/** Empties a String's data. */
|
88
|
-
void fiobj_str_clear(
|
118
|
+
void fiobj_str_clear(FIOBJ str);
|
89
119
|
|
90
120
|
/**
|
91
121
|
* Writes data at the end of the string, resizing the string as required.
|
92
122
|
* Returns the new length of the String
|
93
123
|
*/
|
94
|
-
size_t fiobj_str_write(
|
124
|
+
size_t fiobj_str_write(FIOBJ dest, const char *data, size_t len);
|
95
125
|
|
96
126
|
/**
|
97
127
|
* Writes data at the end of the string, resizing the string as required.
|
98
128
|
* Returns the new length of the String
|
99
129
|
*/
|
100
130
|
__attribute__((format(printf, 2, 3))) size_t
|
101
|
-
fiobj_str_write2(
|
131
|
+
fiobj_str_write2(FIOBJ dest, const char *format, ...);
|
102
132
|
|
103
133
|
/**
|
104
134
|
* Writes data at the end of the string, resizing the string as required.
|
105
|
-
*
|
135
|
+
*
|
136
|
+
* Remember to call `fiobj_free` to free the source (when done with it).
|
137
|
+
*
|
138
|
+
* Returns the new length of the String.
|
139
|
+
*/
|
140
|
+
size_t fiobj_str_join(FIOBJ dest, FIOBJ source);
|
141
|
+
|
142
|
+
/* *****************************************************************************
|
143
|
+
API: String Values
|
144
|
+
***************************************************************************** */
|
145
|
+
|
146
|
+
/**
|
147
|
+
* Calculates a String's SipHash value for possible use as a HashMap key.
|
148
|
+
*
|
149
|
+
* Hashing the String's value automatically freezes the string, preventing
|
150
|
+
* future changes.
|
106
151
|
*/
|
107
|
-
|
152
|
+
uint64_t fiobj_str_hash(FIOBJ o);
|
153
|
+
|
154
|
+
#if DEBUG
|
155
|
+
void fiobj_test_string(void);
|
156
|
+
#endif
|
108
157
|
|
109
158
|
#ifdef __cplusplus
|
110
159
|
} /* extern "C" */
|
data/ext/iodine/fiobject.c
CHANGED
@@ -1,19 +1,18 @@
|
|
1
1
|
/*
|
2
|
-
Copyright: Boaz Segev, 2017
|
2
|
+
Copyright: Boaz Segev, 2017-2018
|
3
3
|
License: MIT
|
4
4
|
*/
|
5
5
|
|
6
6
|
/**
|
7
7
|
This facil.io core library provides wrappers around complex and (or) dynamic
|
8
8
|
types, abstracting some complexity and making dynamic type related tasks easier.
|
9
|
+
*/
|
10
|
+
#include "fiobject.h"
|
9
11
|
|
12
|
+
#include "fio_ary.h"
|
10
13
|
|
11
|
-
|
12
|
-
|
13
|
-
however, this isn't fully tested and the performance price is high.
|
14
|
-
*/
|
15
|
-
#include "fiobj_internal.h"
|
16
|
-
#include "fiobj_primitives.h"
|
14
|
+
#define FIO_OVERRIDE_MALLOC 1
|
15
|
+
#include "fio_mem.h"
|
17
16
|
|
18
17
|
#include <stdarg.h>
|
19
18
|
#include <stdint.h>
|
@@ -21,569 +20,321 @@ however, this isn't fully tested and the performance price is high.
|
|
21
20
|
#include <stdlib.h>
|
22
21
|
|
23
22
|
/* *****************************************************************************
|
24
|
-
|
23
|
+
the `fiobj_each2` function
|
25
24
|
***************************************************************************** */
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
if (
|
41
|
-
|
42
|
-
|
43
|
-
while (pos != history) {
|
44
|
-
if (child == pos->obj) {
|
45
|
-
fiobj_cyclic_protection = obj;
|
46
|
-
return NULL;
|
47
|
-
}
|
48
|
-
pos = pos->next;
|
25
|
+
struct task_packet_s {
|
26
|
+
int (*task)(FIOBJ obj, void *arg);
|
27
|
+
void *arg;
|
28
|
+
fio_ary_s *stack;
|
29
|
+
FIOBJ next;
|
30
|
+
uintptr_t counter;
|
31
|
+
uint8_t stop;
|
32
|
+
uint8_t incomplete;
|
33
|
+
};
|
34
|
+
|
35
|
+
static int fiobj_task_wrapper(FIOBJ o, void *p_) {
|
36
|
+
struct task_packet_s *p = p_;
|
37
|
+
++p->counter;
|
38
|
+
int ret = p->task(o, p->arg);
|
39
|
+
if (ret == -1) {
|
40
|
+
p->stop = 1;
|
41
|
+
return -1;
|
49
42
|
}
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
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;
|
43
|
+
if (FIOBJ_IS_ALLOCATED(o) && FIOBJECT2VTBL(o)->each) {
|
44
|
+
p->incomplete = 1;
|
45
|
+
p->next = o;
|
46
|
+
return -1;
|
119
47
|
}
|
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
48
|
return 0;
|
128
49
|
}
|
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
50
|
/**
|
243
51
|
* Single layer iteration using a callback for each nested fio object.
|
244
52
|
*
|
245
|
-
* Accepts any `
|
53
|
+
* Accepts any `FIOBJ ` type but only collections (Arrays and Hashes) are
|
246
54
|
* processed. The container itself (the Array or the Hash) is **not** processed
|
247
55
|
* (unlike `fiobj_each2`).
|
248
56
|
*
|
249
57
|
* The callback task function must accept an object and an opaque user pointer.
|
250
58
|
*
|
251
59
|
* Hash objects pass along a `FIOBJ_T_COUPLET` object, containing
|
252
|
-
* references for both the key
|
60
|
+
* references for both the key and the object. Keys shouldn't be altered once
|
61
|
+
* placed as a key (or the Hash will break). Collections (Arrays / Hashes) can't
|
62
|
+
* be used as keeys.
|
253
63
|
*
|
254
64
|
* If the callback returns -1, the loop is broken. Any other value is ignored.
|
255
65
|
*
|
256
66
|
* Returns the "stop" position, i.e., the number of items processed + the
|
257
67
|
* starting point.
|
258
68
|
*/
|
259
|
-
size_t
|
260
|
-
|
261
|
-
|
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)
|
69
|
+
size_t fiobj_each2(FIOBJ o, int (*task)(FIOBJ obj, void *arg), void *arg) {
|
70
|
+
if (!o || !FIOBJ_IS_ALLOCATED(o) || (FIOBJECT2VTBL(o)->each == NULL)) {
|
71
|
+
task(o, arg);
|
330
72
|
return 1;
|
331
|
-
|
332
|
-
|
333
|
-
if (
|
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))
|
73
|
+
}
|
74
|
+
/* run task for root object */
|
75
|
+
if (task(o, arg) == -1)
|
339
76
|
return 1;
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
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))
|
77
|
+
uintptr_t pos = 0;
|
78
|
+
fio_ary_s stack = FIO_ARY_INIT;
|
79
|
+
struct task_packet_s packet = {
|
80
|
+
.task = task, .arg = arg, .stack = &stack, .counter = 1,
|
81
|
+
};
|
82
|
+
do {
|
83
|
+
if (!pos)
|
84
|
+
packet.next = 0;
|
85
|
+
packet.incomplete = 0;
|
86
|
+
pos = FIOBJECT2VTBL(o)->each(o, pos, fiobj_task_wrapper, &packet);
|
87
|
+
if (packet.stop)
|
369
88
|
goto finish;
|
370
|
-
|
371
|
-
|
89
|
+
if (packet.incomplete) {
|
90
|
+
fio_ary_push(&stack, (void *)pos);
|
91
|
+
fio_ary_push(&stack, (void *)o);
|
92
|
+
}
|
372
93
|
|
94
|
+
if (packet.next) {
|
95
|
+
fio_ary_push(&stack, (void *)0);
|
96
|
+
fio_ary_push(&stack, (void *)packet.next);
|
97
|
+
}
|
98
|
+
o = (FIOBJ)fio_ary_pop(&stack);
|
99
|
+
pos = (uintptr_t)fio_ary_pop(&stack);
|
100
|
+
} while (o);
|
373
101
|
finish:
|
374
|
-
|
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;
|
102
|
+
fio_ary_free(&stack);
|
103
|
+
return packet.counter;
|
383
104
|
}
|
384
105
|
|
385
106
|
/* *****************************************************************************
|
386
|
-
|
107
|
+
Free complex objects (objects with nesting)
|
387
108
|
***************************************************************************** */
|
388
|
-
static const char hex_notation[] = {'0', '1', '2', '3', '4', '5', '6', '7',
|
389
|
-
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
390
109
|
|
110
|
+
static void fiobj_dealloc_task(FIOBJ o, void *stack_) {
|
111
|
+
// if (!o)
|
112
|
+
// fprintf(stderr, "* WARN: freeing a NULL no-object\n");
|
113
|
+
// else
|
114
|
+
// fprintf(stderr, "* freeing object %s\n", fiobj_obj2cstr(o).data);
|
115
|
+
if (!o || !FIOBJ_IS_ALLOCATED(o))
|
116
|
+
return;
|
117
|
+
if (OBJREF_REM(o))
|
118
|
+
return;
|
119
|
+
if (!FIOBJECT2VTBL(o)->each || !FIOBJECT2VTBL(o)->count(o)) {
|
120
|
+
FIOBJECT2VTBL(o)->dealloc(o, NULL, NULL);
|
121
|
+
return;
|
122
|
+
}
|
123
|
+
fio_ary_s *s = stack_;
|
124
|
+
fio_ary_push(s, (void *)o);
|
125
|
+
}
|
391
126
|
/**
|
392
|
-
*
|
127
|
+
* Decreases an object's reference count, releasing memory and
|
128
|
+
* resources.
|
393
129
|
*
|
394
|
-
*
|
395
|
-
*
|
396
|
-
*
|
130
|
+
* This function affects nested objects, meaning that when an Array or
|
131
|
+
* a Hash object is passed along, it's children (nested objects) are
|
132
|
+
* also freed.
|
397
133
|
*/
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
while (
|
403
|
-
|
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;
|
134
|
+
void fiobj_free_complex_object(FIOBJ o) {
|
135
|
+
fio_ary_s stack = FIO_ARY_INIT;
|
136
|
+
do {
|
137
|
+
FIOBJECT2VTBL(o)->dealloc(o, fiobj_dealloc_task, &stack);
|
138
|
+
} while ((o = (FIOBJ)fio_ary_pop(&stack)));
|
139
|
+
fio_ary_free(&stack);
|
451
140
|
}
|
452
141
|
|
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
142
|
/* *****************************************************************************
|
457
|
-
|
143
|
+
Is Equal?
|
458
144
|
***************************************************************************** */
|
145
|
+
#include "fiobj_hash.h"
|
459
146
|
|
460
|
-
|
461
|
-
|
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;
|
147
|
+
static inline int fiobj_iseq_simple(const FIOBJ o, const FIOBJ o2) {
|
148
|
+
if (o == o2)
|
477
149
|
return 1;
|
478
|
-
|
479
|
-
|
480
|
-
|
150
|
+
if (!o || !o2)
|
151
|
+
return 0; /* they should have compared equal before. */
|
152
|
+
if (!FIOBJ_IS_ALLOCATED(o) || !FIOBJ_IS_ALLOCATED(o2))
|
153
|
+
return 0; /* they should have compared equal before. */
|
154
|
+
if (FIOBJECT2HEAD(o)->type != FIOBJECT2HEAD(o2)->type)
|
155
|
+
return 0; /* non-type equality is a barriar to equality. */
|
156
|
+
if (!FIOBJECT2VTBL(o)->is_eq(o, o2))
|
157
|
+
return 0;
|
158
|
+
return 1;
|
159
|
+
}
|
481
160
|
|
482
|
-
|
483
|
-
|
484
|
-
|
161
|
+
int fiobj_iseq____internal_complex__task(FIOBJ o, void *ary_) {
|
162
|
+
fio_ary_s *ary = ary_;
|
163
|
+
fio_ary_push(ary, (void *)o);
|
164
|
+
if (fiobj_hash_key_in_loop())
|
165
|
+
fio_ary_push(ary, (void *)fiobj_hash_key_in_loop());
|
166
|
+
return 0;
|
167
|
+
}
|
485
168
|
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
while (
|
496
|
-
|
497
|
-
|
498
|
-
|
169
|
+
/** used internally for complext nested tests (Array / Hash types) */
|
170
|
+
int fiobj_iseq____internal_complex__(FIOBJ o, FIOBJ o2) {
|
171
|
+
// if (FIOBJECT2VTBL(o)->each && FIOBJECT2VTBL(o)->count(o))
|
172
|
+
// return int fiobj_iseq____internal_complex__(const FIOBJ o, const FIOBJ
|
173
|
+
// o2);
|
174
|
+
fio_ary_s left = FIO_ARY_INIT, right = FIO_ARY_INIT, queue = FIO_ARY_INIT;
|
175
|
+
do {
|
176
|
+
fiobj_each1(o, 0, fiobj_iseq____internal_complex__task, &left);
|
177
|
+
fiobj_each1(o2, 0, fiobj_iseq____internal_complex__task, &right);
|
178
|
+
while (fio_ary_count(&left)) {
|
179
|
+
o = (FIOBJ)fio_ary_pop(&left);
|
180
|
+
o2 = (FIOBJ)fio_ary_pop(&right);
|
181
|
+
if (!fiobj_iseq_simple(o, o2))
|
182
|
+
goto unequal;
|
183
|
+
if (FIOBJ_IS_ALLOCATED(o) && FIOBJECT2VTBL(o)->each &&
|
184
|
+
FIOBJECT2VTBL(o)->count(o)) {
|
185
|
+
fio_ary_push(&queue, (void *)o);
|
186
|
+
fio_ary_push(&queue, (void *)o2);
|
187
|
+
}
|
499
188
|
}
|
500
|
-
|
501
|
-
|
189
|
+
o2 = (FIOBJ)fio_ary_pop(&queue);
|
190
|
+
o = (FIOBJ)fio_ary_pop(&queue);
|
191
|
+
if (!fiobj_iseq_simple(o, o2))
|
192
|
+
goto unequal;
|
193
|
+
} while (o);
|
194
|
+
fio_ary_free(&left);
|
195
|
+
fio_ary_free(&right);
|
196
|
+
fio_ary_free(&queue);
|
197
|
+
return 1;
|
198
|
+
unequal:
|
199
|
+
fio_ary_free(&left);
|
200
|
+
fio_ary_free(&right);
|
201
|
+
fio_ary_free(&queue);
|
202
|
+
return 0;
|
203
|
+
}
|
502
204
|
|
503
|
-
|
504
|
-
|
505
|
-
|
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
|
-
}
|
205
|
+
/* *****************************************************************************
|
206
|
+
Defaults / NOOPs
|
207
|
+
***************************************************************************** */
|
528
208
|
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
209
|
+
void fiobject___noop_dealloc(FIOBJ o, void (*task)(FIOBJ, void *), void *arg) {
|
210
|
+
(void)o;
|
211
|
+
(void)task;
|
212
|
+
(void)arg;
|
213
|
+
}
|
214
|
+
void fiobject___simple_dealloc(FIOBJ o, void (*task)(FIOBJ, void *),
|
215
|
+
void *arg) {
|
216
|
+
free(FIOBJ2PTR(o));
|
217
|
+
(void)task;
|
218
|
+
(void)arg;
|
219
|
+
}
|
536
220
|
|
537
|
-
|
538
|
-
|
221
|
+
uintptr_t fiobject___noop_count(const FIOBJ o) {
|
222
|
+
(void)o;
|
223
|
+
return 0;
|
224
|
+
}
|
225
|
+
size_t fiobject___noop_is_eq(const FIOBJ o1, const FIOBJ o2) {
|
226
|
+
(void)o1;
|
227
|
+
(void)o2;
|
228
|
+
return 0;
|
229
|
+
}
|
539
230
|
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
231
|
+
fio_cstr_s fiobject___noop_to_str(const FIOBJ o) {
|
232
|
+
(void)o;
|
233
|
+
return (fio_cstr_s){.len = 0, .data = NULL};
|
234
|
+
}
|
235
|
+
intptr_t fiobject___noop_to_i(const FIOBJ o) {
|
236
|
+
(void)o;
|
237
|
+
return 0;
|
238
|
+
}
|
239
|
+
double fiobject___noop_to_f(const FIOBJ o) {
|
240
|
+
(void)o;
|
241
|
+
return 0;
|
548
242
|
}
|
549
243
|
|
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
|
-
}
|
244
|
+
#if DEBUG
|
571
245
|
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
246
|
+
#include "fiobj_ary.h"
|
247
|
+
#include "fiobj_numbers.h"
|
248
|
+
|
249
|
+
static int fiobject_test_task(FIOBJ o, void *arg) {
|
250
|
+
++((uintptr_t *)arg)[0];
|
251
|
+
if (!o)
|
252
|
+
fprintf(stderr, "* WARN: counting a NULL no-object\n");
|
253
|
+
// else
|
254
|
+
// fprintf(stderr, "* counting object %s\n", fiobj_obj2cstr(o).data);
|
255
|
+
return 0;
|
256
|
+
(void)o;
|
257
|
+
}
|
258
|
+
|
259
|
+
void fiobj_test_core(void) {
|
260
|
+
#define TEST_ASSERT(cond, ...) \
|
261
|
+
if (!(cond)) { \
|
262
|
+
fprintf(stderr, __VA_ARGS__); \
|
263
|
+
fprintf(stderr, "Testing failed.\n"); \
|
264
|
+
exit(-1); \
|
587
265
|
}
|
588
|
-
|
266
|
+
fprintf(stderr, "=== Testing Primitives\n");
|
267
|
+
FIOBJ o = fiobj_null();
|
268
|
+
TEST_ASSERT(o == (FIOBJ)FIOBJ_T_NULL, "fiobj_null isn't NULL!\n");
|
269
|
+
TEST_ASSERT(FIOBJ_TYPE(0) == FIOBJ_T_NULL, "NULL isn't NULL!\n");
|
270
|
+
TEST_ASSERT(FIOBJ_TYPE_IS(0, FIOBJ_T_NULL), "NULL isn't NULL! (2)\n");
|
271
|
+
TEST_ASSERT(!FIOBJ_IS_ALLOCATED(fiobj_null()),
|
272
|
+
"fiobj_null claims to be allocated!\n");
|
273
|
+
TEST_ASSERT(!FIOBJ_IS_ALLOCATED(fiobj_true()),
|
274
|
+
"fiobj_true claims to be allocated!\n");
|
275
|
+
TEST_ASSERT(!FIOBJ_IS_ALLOCATED(fiobj_false()),
|
276
|
+
"fiobj_false claims to be allocated!\n");
|
277
|
+
TEST_ASSERT(FIOBJ_TYPE(fiobj_true()) == FIOBJ_T_TRUE,
|
278
|
+
"fiobj_true isn't FIOBJ_T_TRUE!\n");
|
279
|
+
TEST_ASSERT(FIOBJ_TYPE_IS(fiobj_true(), FIOBJ_T_TRUE),
|
280
|
+
"fiobj_true isn't FIOBJ_T_TRUE! (2)\n");
|
281
|
+
TEST_ASSERT(FIOBJ_TYPE(fiobj_false()) == FIOBJ_T_FALSE,
|
282
|
+
"fiobj_false isn't FIOBJ_T_TRUE!\n");
|
283
|
+
TEST_ASSERT(FIOBJ_TYPE_IS(fiobj_false(), FIOBJ_T_FALSE),
|
284
|
+
"fiobj_false isn't FIOBJ_T_TRUE! (2)\n");
|
285
|
+
fiobj_free(o); /* testing for crash*/
|
286
|
+
fprintf(stderr, "* passed.\n");
|
287
|
+
fprintf(stderr, "=== Testing fioj_each2\n");
|
288
|
+
o = fiobj_ary_new2(4);
|
289
|
+
FIOBJ tmp = fiobj_ary_new();
|
290
|
+
fiobj_ary_push(o, tmp);
|
291
|
+
fiobj_ary_push(o, fiobj_true());
|
292
|
+
fiobj_ary_push(o, fiobj_null());
|
293
|
+
fiobj_ary_push(o, fiobj_num_new(10));
|
294
|
+
fiobj_ary_push(tmp, fiobj_num_new(13));
|
295
|
+
fiobj_ary_push(tmp, fiobj_hash_new());
|
296
|
+
FIOBJ key = fiobj_str_new("my key", 6);
|
297
|
+
fiobj_hash_set(fiobj_ary_entry(tmp, -1), key, fiobj_true());
|
298
|
+
fiobj_free(key);
|
299
|
+
/* we have root array + 4 children (w/ array) + 2 children (w/ hash) + 1 */
|
300
|
+
uintptr_t count = 0;
|
301
|
+
size_t each_ret = 0;
|
302
|
+
TEST_ASSERT(fiobj_each2(o, fiobject_test_task, (void *)&count) == 8,
|
303
|
+
"fiobj_each1 didn't count everything... (%d != %d)", (int)count,
|
304
|
+
(int)each_ret);
|
305
|
+
TEST_ASSERT(count == 8, "Something went wrong with the counter task... (%d)",
|
306
|
+
(int)count)
|
307
|
+
fprintf(stderr, "* passed.\n");
|
308
|
+
fprintf(stderr, "=== Testing fioj_iseq with nested items\n");
|
309
|
+
FIOBJ o2 = fiobj_ary_new2(4);
|
310
|
+
tmp = fiobj_ary_new();
|
311
|
+
fiobj_ary_push(o2, tmp);
|
312
|
+
fiobj_ary_push(o2, fiobj_true());
|
313
|
+
fiobj_ary_push(o2, fiobj_null());
|
314
|
+
fiobj_ary_push(o2, fiobj_num_new(10));
|
315
|
+
fiobj_ary_push(tmp, fiobj_num_new(13));
|
316
|
+
fiobj_ary_push(tmp, fiobj_hash_new());
|
317
|
+
key = fiobj_str_new("my key", 6);
|
318
|
+
fiobj_hash_set(fiobj_ary_entry(tmp, -1), key, fiobj_true());
|
319
|
+
fiobj_free(key);
|
320
|
+
TEST_ASSERT(!fiobj_iseq(o, FIOBJ_INVALID),
|
321
|
+
"Array and FIOBJ_INVALID can't be equal!");
|
322
|
+
TEST_ASSERT(!fiobj_iseq(o, fiobj_null()),
|
323
|
+
"Array and fiobj_null can't be equal!");
|
324
|
+
TEST_ASSERT(fiobj_iseq(o, o2), "Arrays aren't euqal!");
|
325
|
+
fiobj_free(o);
|
326
|
+
fiobj_free(o2);
|
327
|
+
TEST_ASSERT(fiobj_iseq(fiobj_null(), fiobj_null()),
|
328
|
+
"fiobj_null() not equal to self!");
|
329
|
+
TEST_ASSERT(fiobj_iseq(fiobj_false(), fiobj_false()),
|
330
|
+
"fiobj_false() not equal to self!");
|
331
|
+
TEST_ASSERT(fiobj_iseq(fiobj_true(), fiobj_true()),
|
332
|
+
"fiobj_true() not equal to self!");
|
333
|
+
TEST_ASSERT(!fiobj_iseq(fiobj_null(), fiobj_false()),
|
334
|
+
"fiobj_null eqal to fiobj_false!");
|
335
|
+
TEST_ASSERT(!fiobj_iseq(fiobj_null(), fiobj_true()),
|
336
|
+
"fiobj_null eqal to fiobj_true!");
|
337
|
+
fprintf(stderr, "* passed.\n");
|
589
338
|
}
|
339
|
+
|
340
|
+
#endif
|