rage-iodine 1.7.58
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
- data/.github/workflows/ruby.yml +42 -0
- data/.gitignore +20 -0
- data/.rspec +2 -0
- data/.yardopts +8 -0
- data/CHANGELOG.md +1098 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +21 -0
- data/LIMITS.md +41 -0
- data/README.md +782 -0
- data/Rakefile +23 -0
- data/SPEC-PubSub-Draft.md +159 -0
- data/SPEC-WebSocket-Draft.md +239 -0
- data/bin/console +22 -0
- data/bin/info.md +353 -0
- data/bin/mustache_bench.rb +100 -0
- data/bin/poc/Gemfile.lock +23 -0
- data/bin/poc/README.md +37 -0
- data/bin/poc/config.ru +66 -0
- data/bin/poc/gemfile +1 -0
- data/bin/poc/www/index.html +57 -0
- data/examples/async_task.ru +92 -0
- data/examples/bates/README.md +3 -0
- data/examples/bates/config.ru +342 -0
- data/examples/bates/david+bold.pdf +0 -0
- data/examples/bates/public/drop-pdf.png +0 -0
- data/examples/bates/public/index.html +600 -0
- data/examples/config.ru +59 -0
- data/examples/echo.ru +59 -0
- data/examples/etag.ru +16 -0
- data/examples/hello.ru +29 -0
- data/examples/pubsub_engine.ru +81 -0
- data/examples/rack3.ru +12 -0
- data/examples/redis.ru +70 -0
- data/examples/shootout.ru +73 -0
- data/examples/sub-protocols.ru +90 -0
- data/examples/tcp_client.rb +66 -0
- data/examples/x-sendfile.ru +14 -0
- data/exe/iodine +280 -0
- data/ext/iodine/extconf.rb +110 -0
- data/ext/iodine/fio.c +12096 -0
- data/ext/iodine/fio.h +6390 -0
- data/ext/iodine/fio_cli.c +431 -0
- data/ext/iodine/fio_cli.h +189 -0
- data/ext/iodine/fio_json_parser.h +687 -0
- data/ext/iodine/fio_siphash.c +157 -0
- data/ext/iodine/fio_siphash.h +37 -0
- data/ext/iodine/fio_tls.h +129 -0
- data/ext/iodine/fio_tls_missing.c +649 -0
- data/ext/iodine/fio_tls_openssl.c +1056 -0
- data/ext/iodine/fio_tmpfile.h +50 -0
- data/ext/iodine/fiobj.h +44 -0
- data/ext/iodine/fiobj4fio.h +21 -0
- data/ext/iodine/fiobj_ary.c +333 -0
- data/ext/iodine/fiobj_ary.h +139 -0
- data/ext/iodine/fiobj_data.c +1185 -0
- data/ext/iodine/fiobj_data.h +167 -0
- data/ext/iodine/fiobj_hash.c +409 -0
- data/ext/iodine/fiobj_hash.h +176 -0
- data/ext/iodine/fiobj_json.c +622 -0
- data/ext/iodine/fiobj_json.h +68 -0
- data/ext/iodine/fiobj_mem.h +71 -0
- data/ext/iodine/fiobj_mustache.c +317 -0
- data/ext/iodine/fiobj_mustache.h +62 -0
- data/ext/iodine/fiobj_numbers.c +344 -0
- data/ext/iodine/fiobj_numbers.h +127 -0
- data/ext/iodine/fiobj_str.c +433 -0
- data/ext/iodine/fiobj_str.h +172 -0
- data/ext/iodine/fiobject.c +620 -0
- data/ext/iodine/fiobject.h +654 -0
- data/ext/iodine/hpack.h +1923 -0
- data/ext/iodine/http.c +2736 -0
- data/ext/iodine/http.h +1019 -0
- data/ext/iodine/http1.c +825 -0
- data/ext/iodine/http1.h +29 -0
- data/ext/iodine/http1_parser.h +1835 -0
- data/ext/iodine/http_internal.c +1279 -0
- data/ext/iodine/http_internal.h +248 -0
- data/ext/iodine/http_mime_parser.h +350 -0
- data/ext/iodine/iodine.c +1433 -0
- data/ext/iodine/iodine.h +64 -0
- data/ext/iodine/iodine_caller.c +218 -0
- data/ext/iodine/iodine_caller.h +27 -0
- data/ext/iodine/iodine_connection.c +941 -0
- data/ext/iodine/iodine_connection.h +55 -0
- data/ext/iodine/iodine_defer.c +420 -0
- data/ext/iodine/iodine_defer.h +6 -0
- data/ext/iodine/iodine_fiobj2rb.h +120 -0
- data/ext/iodine/iodine_helpers.c +282 -0
- data/ext/iodine/iodine_helpers.h +12 -0
- data/ext/iodine/iodine_http.c +1280 -0
- data/ext/iodine/iodine_http.h +23 -0
- data/ext/iodine/iodine_json.c +302 -0
- data/ext/iodine/iodine_json.h +6 -0
- data/ext/iodine/iodine_mustache.c +567 -0
- data/ext/iodine/iodine_mustache.h +6 -0
- data/ext/iodine/iodine_pubsub.c +580 -0
- data/ext/iodine/iodine_pubsub.h +26 -0
- data/ext/iodine/iodine_rack_io.c +273 -0
- data/ext/iodine/iodine_rack_io.h +20 -0
- data/ext/iodine/iodine_store.c +142 -0
- data/ext/iodine/iodine_store.h +20 -0
- data/ext/iodine/iodine_tcp.c +346 -0
- data/ext/iodine/iodine_tcp.h +13 -0
- data/ext/iodine/iodine_tls.c +261 -0
- data/ext/iodine/iodine_tls.h +13 -0
- data/ext/iodine/mustache_parser.h +1546 -0
- data/ext/iodine/redis_engine.c +957 -0
- data/ext/iodine/redis_engine.h +79 -0
- data/ext/iodine/resp_parser.h +317 -0
- data/ext/iodine/scheduler.c +173 -0
- data/ext/iodine/scheduler.h +6 -0
- data/ext/iodine/websocket_parser.h +506 -0
- data/ext/iodine/websockets.c +752 -0
- data/ext/iodine/websockets.h +185 -0
- data/iodine.gemspec +50 -0
- data/lib/iodine/connection.rb +61 -0
- data/lib/iodine/json.rb +42 -0
- data/lib/iodine/mustache.rb +113 -0
- data/lib/iodine/pubsub.rb +55 -0
- data/lib/iodine/rack_utils.rb +43 -0
- data/lib/iodine/tls.rb +16 -0
- data/lib/iodine/version.rb +3 -0
- data/lib/iodine.rb +274 -0
- data/lib/rack/handler/iodine.rb +33 -0
- data/logo.png +0 -0
- metadata +284 -0
@@ -0,0 +1,167 @@
|
|
1
|
+
/*
|
2
|
+
Copyright: Boaz Segev, 2017-2019
|
3
|
+
License: MIT
|
4
|
+
*/
|
5
|
+
#if !defined(H_FIOBJ_IO_H) && (defined(__unix__) || defined(__APPLE__) || \
|
6
|
+
defined(__linux__) || defined(__CYGWIN__) || \
|
7
|
+
defined(__MINGW32__))
|
8
|
+
|
9
|
+
/**
|
10
|
+
* A dynamic type for reading / writing to a local file, a temporary file or an
|
11
|
+
* in-memory string.
|
12
|
+
*
|
13
|
+
* Supports basic reak, write, seek, puts and gets operations.
|
14
|
+
*
|
15
|
+
* Writing is always performed at the end of the stream / memory buffer,
|
16
|
+
* ignoring the current seek position.
|
17
|
+
*/
|
18
|
+
#define H_FIOBJ_IO_H
|
19
|
+
|
20
|
+
#include <fiobject.h>
|
21
|
+
|
22
|
+
#ifdef __cplusplus
|
23
|
+
extern "C" {
|
24
|
+
#endif
|
25
|
+
|
26
|
+
/* *****************************************************************************
|
27
|
+
Creating the Data Stream object
|
28
|
+
***************************************************************************** */
|
29
|
+
|
30
|
+
/** Creates a new local in-memory Data Stream object */
|
31
|
+
FIOBJ fiobj_data_newstr(void);
|
32
|
+
|
33
|
+
/**
|
34
|
+
* Creates a Data object from an existing buffer. The buffer will be deallocated
|
35
|
+
* using the provided `dealloc` function pointer. Use a NULL `dealloc` function
|
36
|
+
* pointer if the buffer is static and shouldn't be freed.
|
37
|
+
*/
|
38
|
+
FIOBJ fiobj_data_newstr2(void *buffer, uintptr_t length,
|
39
|
+
void (*dealloc)(void *));
|
40
|
+
|
41
|
+
/** Creates a new local tempfile Data Stream object */
|
42
|
+
FIOBJ fiobj_data_newtmpfile(void);
|
43
|
+
|
44
|
+
/** Creates a new local file Data Stream object */
|
45
|
+
FIOBJ fiobj_data_newfd(int fd);
|
46
|
+
|
47
|
+
/** Creates a slice from an existing Data object. */
|
48
|
+
FIOBJ fiobj_data_slice(FIOBJ parent, intptr_t offset, uintptr_t length);
|
49
|
+
|
50
|
+
/* *****************************************************************************
|
51
|
+
Saving the Data Stream object
|
52
|
+
***************************************************************************** */
|
53
|
+
|
54
|
+
/** Creates a new local file Data Stream object */
|
55
|
+
int fiobj_data_save(FIOBJ io, const char *filename);
|
56
|
+
|
57
|
+
/* *****************************************************************************
|
58
|
+
Reading API
|
59
|
+
***************************************************************************** */
|
60
|
+
|
61
|
+
/**
|
62
|
+
* Reads up to `length` bytes and returns a temporary(!) buffer object (not NUL
|
63
|
+
* terminated).
|
64
|
+
*
|
65
|
+
* If `length` is zero or negative, it will be computed from the end of the
|
66
|
+
* input backwards (0 == EOF).
|
67
|
+
*
|
68
|
+
* The C string object will be invalidate the next time a function call to the
|
69
|
+
* Data Stream object is made.
|
70
|
+
*/
|
71
|
+
fio_str_info_s fiobj_data_read(FIOBJ io, intptr_t length);
|
72
|
+
|
73
|
+
/**
|
74
|
+
* Reads until the `token` byte is encountered or until the end of the stream.
|
75
|
+
*
|
76
|
+
* Returns a temporary(!) C string including the end of line marker.
|
77
|
+
*
|
78
|
+
* Careful when using this call on large file streams, as the whole file
|
79
|
+
* stream might be loaded into the memory.
|
80
|
+
*
|
81
|
+
* The C string object will be invalidate the next time a function call to the
|
82
|
+
* Data Stream object is made.
|
83
|
+
*/
|
84
|
+
fio_str_info_s fiobj_data_read2ch(FIOBJ io, uint8_t token);
|
85
|
+
|
86
|
+
/**
|
87
|
+
* Reads a line (until the '\n' byte is encountered) or until the end of the
|
88
|
+
* available data.
|
89
|
+
*
|
90
|
+
* Returns a temporary(!) buffer object (not NUL terminated) including the end
|
91
|
+
* of line marker.
|
92
|
+
*
|
93
|
+
* Careful when using this call on large file streams, as the whole file stream
|
94
|
+
* might be loaded into the memory.
|
95
|
+
*
|
96
|
+
* The C string object will be invalidate the next time a function call to the
|
97
|
+
* Data Stream object is made.
|
98
|
+
*/
|
99
|
+
#define fiobj_data_gets(io) fiobj_data_read2ch((io), '\n');
|
100
|
+
|
101
|
+
/**
|
102
|
+
* Returns the current reading position. Returns -1 on error.
|
103
|
+
*/
|
104
|
+
intptr_t fiobj_data_pos(FIOBJ io);
|
105
|
+
|
106
|
+
/**
|
107
|
+
* Returns the length of the stream.
|
108
|
+
*/
|
109
|
+
intptr_t fiobj_data_len(FIOBJ io);
|
110
|
+
|
111
|
+
/**
|
112
|
+
* Moves the reading position to the requested position.
|
113
|
+
*/
|
114
|
+
void fiobj_data_seek(FIOBJ io, intptr_t position);
|
115
|
+
|
116
|
+
/**
|
117
|
+
* Reads up to `length` bytes starting at `start_at` position and returns a
|
118
|
+
* temporary(!) buffer object (not NUL terminated) string object. The reading
|
119
|
+
* position is ignored and unchanged.
|
120
|
+
*
|
121
|
+
* The C string object will be invalidate the next time a function call to the
|
122
|
+
* Data Stream object is made.
|
123
|
+
*/
|
124
|
+
fio_str_info_s fiobj_data_pread(FIOBJ io, intptr_t start_at, uintptr_t length);
|
125
|
+
|
126
|
+
/* *****************************************************************************
|
127
|
+
Writing API
|
128
|
+
***************************************************************************** */
|
129
|
+
|
130
|
+
/**
|
131
|
+
* Writes `length` bytes at the end of the Data Stream stream, ignoring the
|
132
|
+
* reading position.
|
133
|
+
*
|
134
|
+
* Behaves and returns the same value as the system call `write`.
|
135
|
+
*/
|
136
|
+
intptr_t fiobj_data_write(FIOBJ io, void *buffer, uintptr_t length);
|
137
|
+
|
138
|
+
/**
|
139
|
+
* Writes `length` bytes at the end of the Data Stream stream, ignoring the
|
140
|
+
* reading position, adding an EOL marker ("\r\n") to the end of the stream.
|
141
|
+
*
|
142
|
+
* Behaves and returns the same value as the system call `write`.
|
143
|
+
*/
|
144
|
+
intptr_t fiobj_data_puts(FIOBJ io, void *buffer, uintptr_t length);
|
145
|
+
|
146
|
+
/**
|
147
|
+
* Makes sure the Data Stream object isn't attached to a static or external
|
148
|
+
* string.
|
149
|
+
*
|
150
|
+
* If the Data Stream object is attached to a static or external string, the
|
151
|
+
* data will be copied to a new memory block.
|
152
|
+
*
|
153
|
+
* If the Data Stream object is a slice from another Data Stream object, the
|
154
|
+
* data will be copied and the type of Data Stream object (memory vs. tmpfile)
|
155
|
+
* will be inherited.
|
156
|
+
*/
|
157
|
+
void fiobj_data_assert_dynamic(FIOBJ io);
|
158
|
+
|
159
|
+
#if DEBUG
|
160
|
+
void fiobj_data_test(void);
|
161
|
+
#endif
|
162
|
+
|
163
|
+
#ifdef __cplusplus
|
164
|
+
}
|
165
|
+
#endif
|
166
|
+
|
167
|
+
#endif
|
@@ -0,0 +1,409 @@
|
|
1
|
+
/*
|
2
|
+
Copyright: Boaz Segev, 2017-2019
|
3
|
+
License: MIT
|
4
|
+
*/
|
5
|
+
|
6
|
+
#include <fiobject.h>
|
7
|
+
|
8
|
+
#include <assert.h>
|
9
|
+
#include <fiobj_hash.h>
|
10
|
+
|
11
|
+
#define FIO_SET_CALLOC(size, count) fio_calloc((size), (count))
|
12
|
+
#define FIO_SET_REALLOC(ptr, original_size, size, valid_data_length) \
|
13
|
+
fio_realloc2((ptr), (size), (valid_data_length))
|
14
|
+
#define FIO_SET_FREE(ptr, size) fio_free((ptr))
|
15
|
+
|
16
|
+
#define FIO_SET_NAME fio_hash__
|
17
|
+
#define FIO_SET_KEY_TYPE FIOBJ
|
18
|
+
#define FIO_SET_KEY_COMPARE(o1, o2) \
|
19
|
+
((o2) == ((FIOBJ)-1) || (o1) == ((FIOBJ)-1) || fiobj_iseq((o1), (o2)))
|
20
|
+
#define FIO_SET_KEY_COPY(dest, obj) ((dest) = fiobj_dup((obj)))
|
21
|
+
#define FIO_SET_KEY_DESTROY(obj) \
|
22
|
+
do { \
|
23
|
+
fiobj_free((obj)); \
|
24
|
+
(obj) = FIOBJ_INVALID; \
|
25
|
+
} while (0)
|
26
|
+
#define FIO_SET_OBJ_TYPE FIOBJ
|
27
|
+
#define FIO_SET_OBJ_COMPARE(o1, o2) fiobj_iseq((o1), (o2))
|
28
|
+
#define FIO_SET_OBJ_COPY(dest, obj) ((dest) = fiobj_dup(obj))
|
29
|
+
#define FIO_SET_OBJ_DESTROY(obj) \
|
30
|
+
do { \
|
31
|
+
fiobj_free((obj)); \
|
32
|
+
(obj) = FIOBJ_INVALID; \
|
33
|
+
} while (0)
|
34
|
+
|
35
|
+
#include <fio.h>
|
36
|
+
|
37
|
+
#include <errno.h>
|
38
|
+
|
39
|
+
#include <pthread.h>
|
40
|
+
|
41
|
+
/* *****************************************************************************
|
42
|
+
Hash types
|
43
|
+
***************************************************************************** */
|
44
|
+
typedef struct {
|
45
|
+
fiobj_object_header_s head;
|
46
|
+
fio_hash___s hash;
|
47
|
+
} fiobj_hash_s;
|
48
|
+
|
49
|
+
#define obj2hash(o) ((fiobj_hash_s *)(FIOBJ2PTR(o)))
|
50
|
+
|
51
|
+
void fiobj_hash_rehash(FIOBJ h) {
|
52
|
+
assert(h && FIOBJ_TYPE_IS(h, FIOBJ_T_HASH));
|
53
|
+
fio_hash___rehash(&obj2hash(h)->hash);
|
54
|
+
}
|
55
|
+
|
56
|
+
/* *****************************************************************************
|
57
|
+
Hash alloc + VTable
|
58
|
+
***************************************************************************** */
|
59
|
+
|
60
|
+
static void fiobj_hash_dealloc(FIOBJ o, void (*task)(FIOBJ, void *),
|
61
|
+
void *arg) {
|
62
|
+
FIO_SET_FOR_LOOP(&obj2hash(o)->hash, i) {
|
63
|
+
if (i->obj.key)
|
64
|
+
task((FIOBJ)i->obj.obj, arg);
|
65
|
+
fiobj_free((FIOBJ)i->obj.key);
|
66
|
+
i->obj.key = FIOBJ_INVALID;
|
67
|
+
i->obj.obj = FIOBJ_INVALID;
|
68
|
+
}
|
69
|
+
obj2hash(o)->hash.count = 0;
|
70
|
+
fio_hash___free(&obj2hash(o)->hash);
|
71
|
+
fio_free(FIOBJ2PTR(o));
|
72
|
+
}
|
73
|
+
|
74
|
+
static pthread_key_t each_at_key;
|
75
|
+
static pthread_once_t each_at_key_once = PTHREAD_ONCE_INIT;
|
76
|
+
static void init_each_at_key(void) {
|
77
|
+
pthread_key_create(&each_at_key, free);
|
78
|
+
}
|
79
|
+
static void init_each_at_key_ptr(void) {
|
80
|
+
FIOBJ *eak = malloc(sizeof(FIOBJ));
|
81
|
+
FIO_ASSERT_ALLOC(eak);
|
82
|
+
*eak = FIOBJ_INVALID;
|
83
|
+
pthread_setspecific(each_at_key, eak);
|
84
|
+
}
|
85
|
+
|
86
|
+
static size_t fiobj_hash_each1(FIOBJ o, size_t start_at,
|
87
|
+
int (*task)(FIOBJ obj, void *arg), void *arg) {
|
88
|
+
assert(o && FIOBJ_TYPE_IS(o, FIOBJ_T_HASH));
|
89
|
+
pthread_once(&each_at_key_once, init_each_at_key);
|
90
|
+
FIOBJ *each_at_key_ptr = (FIOBJ *)pthread_getspecific(each_at_key);
|
91
|
+
if (!each_at_key_ptr) {
|
92
|
+
init_each_at_key_ptr();
|
93
|
+
each_at_key_ptr = (FIOBJ *)pthread_getspecific(each_at_key);
|
94
|
+
}
|
95
|
+
FIOBJ old_each_at_key = *each_at_key_ptr;
|
96
|
+
fio_hash___s *hash = &obj2hash(o)->hash;
|
97
|
+
size_t count = 0;
|
98
|
+
if (hash->count == hash->pos) {
|
99
|
+
/* no holes in the hash, we can work as we please. */
|
100
|
+
for (count = start_at; count < hash->count; ++count) {
|
101
|
+
*each_at_key_ptr = hash->ordered[count].obj.key;
|
102
|
+
if (task((FIOBJ)hash->ordered[count].obj.obj, arg) == -1) {
|
103
|
+
++count;
|
104
|
+
goto end;
|
105
|
+
}
|
106
|
+
}
|
107
|
+
} else {
|
108
|
+
size_t pos = 0;
|
109
|
+
for (; pos < start_at && pos < hash->pos; ++pos) {
|
110
|
+
/* counting */
|
111
|
+
if (hash->ordered[pos].obj.key == FIOBJ_INVALID)
|
112
|
+
++start_at;
|
113
|
+
else
|
114
|
+
++count;
|
115
|
+
}
|
116
|
+
for (; pos < hash->pos; ++pos) {
|
117
|
+
/* performing */
|
118
|
+
if (hash->ordered[pos].obj.key == FIOBJ_INVALID)
|
119
|
+
continue;
|
120
|
+
++count;
|
121
|
+
*each_at_key_ptr = hash->ordered[pos].obj.key;
|
122
|
+
if (task((FIOBJ)hash->ordered[pos].obj.obj, arg) == -1)
|
123
|
+
break;
|
124
|
+
}
|
125
|
+
}
|
126
|
+
end:
|
127
|
+
*each_at_key_ptr = old_each_at_key;
|
128
|
+
return count;
|
129
|
+
}
|
130
|
+
|
131
|
+
FIOBJ fiobj_hash_key_in_loop(void) {
|
132
|
+
pthread_once(&each_at_key_once, init_each_at_key);
|
133
|
+
FIOBJ *each_at_key_ptr = (FIOBJ *)pthread_getspecific(each_at_key);
|
134
|
+
if (!each_at_key_ptr) {
|
135
|
+
init_each_at_key_ptr();
|
136
|
+
each_at_key_ptr = (FIOBJ *)pthread_getspecific(each_at_key);
|
137
|
+
}
|
138
|
+
return *each_at_key_ptr;
|
139
|
+
}
|
140
|
+
|
141
|
+
static size_t fiobj_hash_is_eq(const FIOBJ self, const FIOBJ other) {
|
142
|
+
if (fio_hash___count(&obj2hash(self)->hash) !=
|
143
|
+
fio_hash___count(&obj2hash(other)->hash))
|
144
|
+
return 0;
|
145
|
+
return 1;
|
146
|
+
}
|
147
|
+
|
148
|
+
/** Returns the number of elements in the Array. */
|
149
|
+
size_t fiobj_hash_count(const FIOBJ o) {
|
150
|
+
assert(o && FIOBJ_TYPE_IS(o, FIOBJ_T_HASH));
|
151
|
+
return fio_hash___count(&obj2hash(o)->hash);
|
152
|
+
}
|
153
|
+
|
154
|
+
intptr_t fiobj_hash2num(const FIOBJ o) { return (intptr_t)fiobj_hash_count(o); }
|
155
|
+
|
156
|
+
static size_t fiobj_hash_is_true(const FIOBJ o) {
|
157
|
+
return fiobj_hash_count(o) != 0;
|
158
|
+
}
|
159
|
+
|
160
|
+
fio_str_info_s fiobject___noop_to_str(const FIOBJ o);
|
161
|
+
intptr_t fiobject___noop_to_i(const FIOBJ o);
|
162
|
+
double fiobject___noop_to_f(const FIOBJ o);
|
163
|
+
|
164
|
+
const fiobj_object_vtable_s FIOBJECT_VTABLE_HASH = {
|
165
|
+
.class_name = "Hash",
|
166
|
+
.dealloc = fiobj_hash_dealloc,
|
167
|
+
.is_eq = fiobj_hash_is_eq,
|
168
|
+
.count = fiobj_hash_count,
|
169
|
+
.each = fiobj_hash_each1,
|
170
|
+
.is_true = fiobj_hash_is_true,
|
171
|
+
.to_str = fiobject___noop_to_str,
|
172
|
+
.to_i = fiobj_hash2num,
|
173
|
+
.to_f = fiobject___noop_to_f,
|
174
|
+
};
|
175
|
+
|
176
|
+
/* *****************************************************************************
|
177
|
+
Hash API
|
178
|
+
***************************************************************************** */
|
179
|
+
|
180
|
+
/**
|
181
|
+
* Creates a mutable empty Hash object. Use `fiobj_free` when done.
|
182
|
+
*
|
183
|
+
* Notice that these Hash objects are designed for smaller collections and
|
184
|
+
* retain order of object insertion.
|
185
|
+
*/
|
186
|
+
FIOBJ fiobj_hash_new(void) {
|
187
|
+
fiobj_hash_s *h = fio_malloc(sizeof(*h));
|
188
|
+
FIO_ASSERT_ALLOC(h);
|
189
|
+
*h = (fiobj_hash_s){.head = {.ref = 1, .type = FIOBJ_T_HASH},
|
190
|
+
.hash = FIO_SET_INIT};
|
191
|
+
return (FIOBJ)h | FIOBJECT_HASH_FLAG;
|
192
|
+
}
|
193
|
+
|
194
|
+
/**
|
195
|
+
* Creates a mutable empty Hash object with an initial capacity of `capa`. Use
|
196
|
+
* `fiobj_free` when done.
|
197
|
+
*
|
198
|
+
* Notice that these Hash objects are designed for smaller collections and
|
199
|
+
* retain order of object insertion.
|
200
|
+
*/
|
201
|
+
FIOBJ fiobj_hash_new2(size_t capa) {
|
202
|
+
fiobj_hash_s *h = fio_malloc(sizeof(*h));
|
203
|
+
FIO_ASSERT_ALLOC(h);
|
204
|
+
*h = (fiobj_hash_s){.head = {.ref = 1, .type = FIOBJ_T_HASH},
|
205
|
+
.hash = FIO_SET_INIT};
|
206
|
+
fio_hash___capa_require(&h->hash, capa);
|
207
|
+
return (FIOBJ)h | FIOBJECT_HASH_FLAG;
|
208
|
+
}
|
209
|
+
|
210
|
+
/**
|
211
|
+
* Returns a temporary theoretical Hash map capacity.
|
212
|
+
* This could be used for testing performance and memory consumption.
|
213
|
+
*/
|
214
|
+
size_t fiobj_hash_capa(const FIOBJ hash) {
|
215
|
+
assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
|
216
|
+
return fio_hash___capa(&obj2hash(hash)->hash);
|
217
|
+
}
|
218
|
+
|
219
|
+
/**
|
220
|
+
* Sets a key-value pair in the Hash, duplicating the Symbol and **moving**
|
221
|
+
* the ownership of the object to the Hash.
|
222
|
+
*
|
223
|
+
* Returns -1 on error.
|
224
|
+
*/
|
225
|
+
int fiobj_hash_set(FIOBJ hash, FIOBJ key, FIOBJ obj) {
|
226
|
+
assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
|
227
|
+
if (FIOBJ_TYPE_IS(key, FIOBJ_T_STRING))
|
228
|
+
fiobj_str_freeze(key);
|
229
|
+
fio_hash___insert(&obj2hash(hash)->hash, fiobj_obj2hash(key), key, obj, NULL);
|
230
|
+
fiobj_free(obj); /* take ownership - free the user's reference. */
|
231
|
+
return 0;
|
232
|
+
}
|
233
|
+
|
234
|
+
/**
|
235
|
+
* Allows the Hash to be used as a stack.
|
236
|
+
*
|
237
|
+
* If a pointer `key` is provided, it will receive ownership of the key
|
238
|
+
* (remember to free).
|
239
|
+
*
|
240
|
+
* Returns FIOBJ_INVALID on error.
|
241
|
+
*
|
242
|
+
* Returns and object if successful (remember to free).
|
243
|
+
*/
|
244
|
+
FIOBJ fiobj_hash_pop(FIOBJ hash, FIOBJ *key) {
|
245
|
+
assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
|
246
|
+
FIOBJ old;
|
247
|
+
if (fio_hash___count(&obj2hash(hash)->hash))
|
248
|
+
return FIOBJ_INVALID;
|
249
|
+
old = fiobj_dup(fio_hash___last(&obj2hash(hash)->hash).obj);
|
250
|
+
if (key)
|
251
|
+
*key = fiobj_dup(fio_hash___last(&obj2hash(hash)->hash).key);
|
252
|
+
fio_hash___pop(&obj2hash(hash)->hash);
|
253
|
+
return old;
|
254
|
+
}
|
255
|
+
|
256
|
+
/**
|
257
|
+
* Replaces the value in a key-value pair, returning the old value (and it's
|
258
|
+
* ownership) to the caller.
|
259
|
+
*
|
260
|
+
* A return value of NULL indicates that no previous object existed (but a new
|
261
|
+
* key-value pair was created.
|
262
|
+
*
|
263
|
+
* Errors are silently ignored.
|
264
|
+
*/
|
265
|
+
FIOBJ fiobj_hash_replace(FIOBJ hash, FIOBJ key, FIOBJ obj) {
|
266
|
+
assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
|
267
|
+
FIOBJ old = FIOBJ_INVALID;
|
268
|
+
fio_hash___insert(&obj2hash(hash)->hash, fiobj_obj2hash(key), key, obj, &old);
|
269
|
+
fiobj_free(obj); /* take ownership - free the user's reference. */
|
270
|
+
return old;
|
271
|
+
}
|
272
|
+
|
273
|
+
/**
|
274
|
+
* Removes a key-value pair from the Hash, if it exists.
|
275
|
+
*/
|
276
|
+
FIOBJ fiobj_hash_remove(FIOBJ hash, FIOBJ key) {
|
277
|
+
assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
|
278
|
+
FIOBJ old = FIOBJ_INVALID;
|
279
|
+
fio_hash___remove(&obj2hash(hash)->hash, fiobj_obj2hash(key), key, &old);
|
280
|
+
return old;
|
281
|
+
}
|
282
|
+
|
283
|
+
/**
|
284
|
+
* Removes a key-value pair from the Hash, if it exists, returning the old
|
285
|
+
* object (instead of freeing it).
|
286
|
+
*/
|
287
|
+
FIOBJ fiobj_hash_remove2(FIOBJ hash, uint64_t hash_value) {
|
288
|
+
assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
|
289
|
+
FIOBJ old = FIOBJ_INVALID;
|
290
|
+
fio_hash___remove(&obj2hash(hash)->hash, hash_value, -1, &old);
|
291
|
+
return old;
|
292
|
+
}
|
293
|
+
|
294
|
+
/**
|
295
|
+
* Deletes a key-value pair from the Hash, if it exists, freeing the
|
296
|
+
* associated object.
|
297
|
+
*
|
298
|
+
* Returns -1 on type error or if the object never existed.
|
299
|
+
*/
|
300
|
+
int fiobj_hash_delete(FIOBJ hash, FIOBJ key) {
|
301
|
+
return fio_hash___remove(&obj2hash(hash)->hash, fiobj_obj2hash(key), key,
|
302
|
+
NULL);
|
303
|
+
}
|
304
|
+
|
305
|
+
/**
|
306
|
+
* Deletes a key-value pair from the Hash, if it exists, freeing the
|
307
|
+
* associated object.
|
308
|
+
*
|
309
|
+
* This function takes a `uintptr_t` Hash value (see `fio_siphash`) to
|
310
|
+
* perform a lookup in the HashMap, which is slightly faster than the other
|
311
|
+
* variations.
|
312
|
+
*
|
313
|
+
* Returns -1 on type error or if the object never existed.
|
314
|
+
*/
|
315
|
+
int fiobj_hash_delete2(FIOBJ hash, uint64_t key_hash) {
|
316
|
+
return fio_hash___remove(&obj2hash(hash)->hash, key_hash, -1, NULL);
|
317
|
+
}
|
318
|
+
|
319
|
+
/**
|
320
|
+
* Returns a temporary handle to the object associated with the Symbol, NULL
|
321
|
+
* if none.
|
322
|
+
*/
|
323
|
+
FIOBJ fiobj_hash_get(const FIOBJ hash, FIOBJ key) {
|
324
|
+
assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
|
325
|
+
return fio_hash___find(&obj2hash(hash)->hash, fiobj_obj2hash(key), key);
|
326
|
+
;
|
327
|
+
}
|
328
|
+
|
329
|
+
/**
|
330
|
+
* Returns a temporary handle to the object associated hashed key value.
|
331
|
+
*
|
332
|
+
* This function takes a `uintptr_t` Hash value (see `fio_siphash`) to
|
333
|
+
* perform a lookup in the HashMap.
|
334
|
+
*
|
335
|
+
* Returns NULL if no object is associated with this hashed key value.
|
336
|
+
*/
|
337
|
+
FIOBJ fiobj_hash_get2(const FIOBJ hash, uint64_t key_hash) {
|
338
|
+
assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
|
339
|
+
return fio_hash___find(&obj2hash(hash)->hash, key_hash, -1);
|
340
|
+
;
|
341
|
+
}
|
342
|
+
|
343
|
+
/**
|
344
|
+
* Returns 1 if the key (Symbol) exists in the Hash, even if value is NULL.
|
345
|
+
*/
|
346
|
+
int fiobj_hash_haskey(const FIOBJ hash, FIOBJ key) {
|
347
|
+
assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
|
348
|
+
return fio_hash___find(&obj2hash(hash)->hash, fiobj_obj2hash(key), key) !=
|
349
|
+
FIOBJ_INVALID;
|
350
|
+
}
|
351
|
+
|
352
|
+
/**
|
353
|
+
* Empties the Hash.
|
354
|
+
*/
|
355
|
+
void fiobj_hash_clear(const FIOBJ hash) {
|
356
|
+
assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
|
357
|
+
fio_hash___free(&obj2hash(hash)->hash);
|
358
|
+
}
|
359
|
+
|
360
|
+
/* *****************************************************************************
|
361
|
+
Simple Tests
|
362
|
+
***************************************************************************** */
|
363
|
+
|
364
|
+
#if DEBUG
|
365
|
+
void fiobj_test_hash(void) {
|
366
|
+
fprintf(stderr, "=== Testing Hash\n");
|
367
|
+
#define TEST_ASSERT(cond, ...) \
|
368
|
+
if (!(cond)) { \
|
369
|
+
fprintf(stderr, "* " __VA_ARGS__); \
|
370
|
+
fprintf(stderr, "Testing failed.\n"); \
|
371
|
+
exit(-1); \
|
372
|
+
}
|
373
|
+
FIOBJ o = fiobj_hash_new();
|
374
|
+
FIOBJ str_key = fiobj_str_new("Hello World!", 12);
|
375
|
+
TEST_ASSERT(FIOBJ_TYPE_IS(o, FIOBJ_T_HASH), "Type identification error!\n");
|
376
|
+
TEST_ASSERT(fiobj_hash_count(o) == 0, "Hash should be empty!\n");
|
377
|
+
fiobj_hash_set(o, str_key, fiobj_true());
|
378
|
+
TEST_ASSERT(fiobj_str_write(str_key, "should fail...", 13) == 0,
|
379
|
+
"wrote to frozen string?");
|
380
|
+
TEST_ASSERT(fiobj_obj2cstr(str_key).len == 12,
|
381
|
+
"String was mutated (not frozen)!\n");
|
382
|
+
TEST_ASSERT(fiobj_hash_get(o, str_key) == fiobj_true(),
|
383
|
+
"full compare didn't get value back");
|
384
|
+
TEST_ASSERT(fiobj_hash_get2(o, fiobj_obj2hash(str_key)) == fiobj_true(),
|
385
|
+
"hash compare didn't get value back");
|
386
|
+
|
387
|
+
FIOBJ o2 = fiobj_hash_new2(3);
|
388
|
+
TEST_ASSERT(obj2hash(o2)->hash.capa >= 3,
|
389
|
+
"Hash capacity should be larger than 3! %zu != 4\n",
|
390
|
+
(size_t)obj2hash(o2)->hash.capa);
|
391
|
+
fiobj_hash_set(o2, str_key, fiobj_true());
|
392
|
+
TEST_ASSERT(fiobj_hash_is_eq(o, o2), "Hashes not equal at core! %zu != %zu\n",
|
393
|
+
fiobj_hash_count(o), fiobj_hash_count(o2));
|
394
|
+
TEST_ASSERT(fiobj_iseq(o, o2), "Hashes not equal!\n");
|
395
|
+
TEST_ASSERT(obj2hash(o2)->hash.capa > 3,
|
396
|
+
"Hash capacity should be larger than 3! %zu != 4\n",
|
397
|
+
(size_t)obj2hash(o2)->hash.capa);
|
398
|
+
|
399
|
+
fiobj_hash_delete(o, str_key);
|
400
|
+
|
401
|
+
TEST_ASSERT(fiobj_hash_get2(o, fiobj_obj2hash(str_key)) == 0,
|
402
|
+
"item wasn't deleted!");
|
403
|
+
fiobj_free(
|
404
|
+
str_key); /* note that a copy will remain in the Hash until rehashing. */
|
405
|
+
fiobj_free(o);
|
406
|
+
fiobj_free(o2);
|
407
|
+
fprintf(stderr, "* passed.\n");
|
408
|
+
}
|
409
|
+
#endif
|