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
@@ -0,0 +1,164 @@
|
|
1
|
+
/*
|
2
|
+
Copyright: Boaz Segev, 2017-2018
|
3
|
+
License: MIT
|
4
|
+
*/
|
5
|
+
#if !defined(H_FIOBJ_IO_H) && (defined(__unix__) || defined(__APPLE__) || \
|
6
|
+
defined(__linux__) || defined(__CYGWIN__))
|
7
|
+
|
8
|
+
/**
|
9
|
+
* A dynamic type for reading / writing to a local file, a temporary file or an
|
10
|
+
* in-memory string.
|
11
|
+
*
|
12
|
+
* Supports basic reak, write, seek, puts and gets operations.
|
13
|
+
*
|
14
|
+
* Writing is always performed at the end of the stream / memory buffer,
|
15
|
+
* ignoring the current seek position.
|
16
|
+
*/
|
17
|
+
#define H_FIOBJ_IO_H
|
18
|
+
|
19
|
+
#include "fiobject.h"
|
20
|
+
|
21
|
+
#ifdef __cplusplus
|
22
|
+
extern "C" {
|
23
|
+
#endif
|
24
|
+
|
25
|
+
/* *****************************************************************************
|
26
|
+
Creating the IO object
|
27
|
+
***************************************************************************** */
|
28
|
+
|
29
|
+
/** Creates a new local in-memory IO object */
|
30
|
+
FIOBJ fiobj_data_newstr(void);
|
31
|
+
|
32
|
+
/**
|
33
|
+
* Creates a Data object from an existing buffer. The buffer will be deallocated
|
34
|
+
* using the provided `dealloc` function pointer. Use a NULL `dealloc` function
|
35
|
+
* pointer if the buffer is static and shouldn't be freed.
|
36
|
+
*/
|
37
|
+
FIOBJ fiobj_data_newstr2(void *buffer, uintptr_t length,
|
38
|
+
void (*dealloc)(void *));
|
39
|
+
|
40
|
+
/** Creates a new local tempfile IO object */
|
41
|
+
FIOBJ fiobj_data_newtmpfile(void);
|
42
|
+
|
43
|
+
/** Creates a new local file IO object */
|
44
|
+
FIOBJ fiobj_data_newfd(int fd);
|
45
|
+
|
46
|
+
/** Creates a slice from an existing Data object. */
|
47
|
+
FIOBJ fiobj_data_slice(FIOBJ parent, intptr_t offset, uintptr_t length);
|
48
|
+
|
49
|
+
/* *****************************************************************************
|
50
|
+
Saving the IO object
|
51
|
+
***************************************************************************** */
|
52
|
+
|
53
|
+
/** Creates a new local file IO object */
|
54
|
+
int fiobj_data_save(FIOBJ io, const char *filename);
|
55
|
+
|
56
|
+
/* *****************************************************************************
|
57
|
+
Reading API
|
58
|
+
***************************************************************************** */
|
59
|
+
|
60
|
+
/**
|
61
|
+
* Reads up to `length` bytes and returns a temporary(!) buffer object (not NUL
|
62
|
+
* terminated).
|
63
|
+
*
|
64
|
+
* If `length` is zero or negative, it will be computed from the end of the
|
65
|
+
* input backwards (0 == EOF).
|
66
|
+
*
|
67
|
+
* The C string object will be invalidate the next time a function call to the
|
68
|
+
* IO object is made.
|
69
|
+
*/
|
70
|
+
fio_cstr_s fiobj_data_read(FIOBJ io, intptr_t length);
|
71
|
+
|
72
|
+
/**
|
73
|
+
* Reads until the `token` byte is encountered or until the end of the stream.
|
74
|
+
*
|
75
|
+
* Returns a temporary(!) C string including the end of line marker.
|
76
|
+
*
|
77
|
+
* Careful when using this call on large file streams, as the whole file
|
78
|
+
* stream might be loaded into the memory.
|
79
|
+
*
|
80
|
+
* The C string object will be invalidate the next time a function call to the
|
81
|
+
* IO object is made.
|
82
|
+
*/
|
83
|
+
fio_cstr_s fiobj_data_read2ch(FIOBJ io, uint8_t token);
|
84
|
+
|
85
|
+
/**
|
86
|
+
* Reads a line (until the '\n' byte is encountered) or until the end of the
|
87
|
+
* available data.
|
88
|
+
*
|
89
|
+
* Returns a temporary(!) buffer object (not NUL terminated) including the end
|
90
|
+
* of line marker.
|
91
|
+
*
|
92
|
+
* Careful when using this call on large file streams, as the whole file stream
|
93
|
+
* might be loaded into the memory.
|
94
|
+
*
|
95
|
+
* The C string object will be invalidate the next time a function call to the
|
96
|
+
* IO object is made.
|
97
|
+
*/
|
98
|
+
#define fiobj_data_gets(io) fiobj_data_read2ch((io), '\n');
|
99
|
+
|
100
|
+
/**
|
101
|
+
* Returns the current reading position. Returns -1 on error.
|
102
|
+
*/
|
103
|
+
intptr_t fiobj_data_pos(FIOBJ io);
|
104
|
+
|
105
|
+
/**
|
106
|
+
* Returns the length of the stream.
|
107
|
+
*/
|
108
|
+
intptr_t fiobj_data_len(FIOBJ io);
|
109
|
+
|
110
|
+
/**
|
111
|
+
* Moves the reading position to the requested position.
|
112
|
+
*/
|
113
|
+
void fiobj_data_seek(FIOBJ io, intptr_t position);
|
114
|
+
|
115
|
+
/**
|
116
|
+
* Reads up to `length` bytes starting at `start_at` position and returns a
|
117
|
+
* temporary(!) buffer object (not NUL terminated) string object. The reading
|
118
|
+
* position is ignored and unchanged.
|
119
|
+
*
|
120
|
+
* The C string object will be invalidate the next time a function call to the
|
121
|
+
* IO object is made.
|
122
|
+
*/
|
123
|
+
fio_cstr_s fiobj_data_pread(FIOBJ io, intptr_t start_at, uintptr_t length);
|
124
|
+
|
125
|
+
/* *****************************************************************************
|
126
|
+
Writing API
|
127
|
+
***************************************************************************** */
|
128
|
+
|
129
|
+
/**
|
130
|
+
* Writes `length` bytes at the end of the IO stream, ignoring the reading
|
131
|
+
* position.
|
132
|
+
*
|
133
|
+
* Behaves and returns the same value as the system call `write`.
|
134
|
+
*/
|
135
|
+
intptr_t fiobj_data_write(FIOBJ io, void *buffer, uintptr_t length);
|
136
|
+
|
137
|
+
/**
|
138
|
+
* Writes `length` bytes at the end of the IO stream, ignoring the reading
|
139
|
+
* position, adding an EOL marker ("\r\n") to the end of the stream.
|
140
|
+
*
|
141
|
+
* Behaves and returns the same value as the system call `write`.
|
142
|
+
*/
|
143
|
+
intptr_t fiobj_data_puts(FIOBJ io, void *buffer, uintptr_t length);
|
144
|
+
|
145
|
+
/**
|
146
|
+
* Makes sure the IO object isn't attached to a static or external string.
|
147
|
+
*
|
148
|
+
* If the IO object is attached to a static or external string, the data will be
|
149
|
+
* copied to a new memory block.
|
150
|
+
*
|
151
|
+
* If the IO object is a slice from another IO object, the data will be copied
|
152
|
+
* and the type of IO object (memory vs. tmpfile) will be inherited.
|
153
|
+
*/
|
154
|
+
void fiobj_data_assert_dynamic(FIOBJ io);
|
155
|
+
|
156
|
+
#if DEBUG
|
157
|
+
void fiobj_data_test(void);
|
158
|
+
#endif
|
159
|
+
|
160
|
+
#ifdef __cplusplus
|
161
|
+
}
|
162
|
+
#endif
|
163
|
+
|
164
|
+
#endif
|
data/ext/iodine/fiobj_hash.c
CHANGED
@@ -1,425 +1,275 @@
|
|
1
1
|
/*
|
2
|
-
Copyright: Boaz Segev, 2017
|
2
|
+
Copyright: Boaz Segev, 2017-2018
|
3
3
|
License: MIT
|
4
4
|
*/
|
5
5
|
|
6
|
-
#include "
|
7
|
-
#include "fiobj_internal.h"
|
6
|
+
#include "fiobject.h"
|
8
7
|
|
9
|
-
#
|
8
|
+
#define FIO_OVERRIDE_MALLOC 1
|
9
|
+
#include "fio_mem.h"
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
#define FIOBJ_HASH_MAX_MAP_SEEK (256)
|
11
|
+
#if !FIO_FORCE_MALLOC
|
12
|
+
#define FIO_HASH_REALLOC(ptr, original_size, size, valid_data_length) \
|
13
|
+
fio_realloc2((ptr), (size), (valid_data_length))
|
14
|
+
#endif
|
16
15
|
|
17
|
-
|
18
|
-
uintptr_t hash;
|
19
|
-
fio_ls_s *container;
|
20
|
-
} map_info_s;
|
16
|
+
#include "fiobj_hash.h"
|
21
17
|
|
22
|
-
|
23
|
-
uintptr_t capa;
|
24
|
-
map_info_s *data;
|
25
|
-
} fio_map_s;
|
18
|
+
#include <assert.h>
|
26
19
|
|
27
20
|
typedef struct {
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
21
|
+
uint64_t hash;
|
22
|
+
FIOBJ key;
|
23
|
+
} hash_key_s;
|
24
|
+
|
25
|
+
static hash_key_s hash_key_copy(hash_key_s key) {
|
26
|
+
fiobj_dup(key.key);
|
27
|
+
fiobj_str_freeze(key.key);
|
28
|
+
return key;
|
29
|
+
}
|
30
|
+
static void hash_key_free(hash_key_s key) { fiobj_free(key.key); }
|
34
31
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
32
|
+
#define FIO_HASH_KEY_TYPE hash_key_s
|
33
|
+
#define FIO_HASH_KEY_INVALID ((hash_key_s){.hash = 0})
|
34
|
+
#define FIO_HASH_KEY2UINT(k) ((k).hash)
|
35
|
+
#define FIO_HASH_COMPARE_KEYS(k1, k2) \
|
36
|
+
(!(k2).key || fiobj_iseq((k1).key, (k2).key))
|
37
|
+
#define FIO_HASH_KEY_ISINVALID(k) ((k).hash == 0 && (k).key == 0)
|
38
|
+
#define FIO_HASH_KEY_COPY(k) hash_key_copy(k)
|
39
|
+
#define FIO_HASH_KEY_DESTROY(k) hash_key_free(k)
|
41
40
|
|
42
|
-
|
41
|
+
#include "fio_hashmap.h"
|
43
42
|
|
44
|
-
#
|
45
|
-
#define obj2couplet(o) ((fiobj_couplet_s *)(o))
|
43
|
+
#include <errno.h>
|
46
44
|
|
47
45
|
/* *****************************************************************************
|
48
|
-
|
49
|
-
We avoid the fiobj_ary_s to prevent code entanglement
|
46
|
+
Hash types
|
50
47
|
***************************************************************************** */
|
48
|
+
typedef struct {
|
49
|
+
fiobj_object_header_s head;
|
50
|
+
fio_hash_s hash;
|
51
|
+
} fiobj_hash_s;
|
51
52
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
map->data = calloc(sizeof(*map->data), map->capa);
|
58
|
-
if (!map->data)
|
59
|
-
perror("HashMap Allocation Failed"), exit(errno);
|
53
|
+
#define obj2hash(o) ((fiobj_hash_s *)(FIOBJ2PTR(o)))
|
54
|
+
|
55
|
+
void fiobj_hash_rehash(FIOBJ h) {
|
56
|
+
assert(h && FIOBJ_TYPE_IS(h, FIOBJ_T_HASH));
|
57
|
+
fio_hash_rehash(&obj2hash(h)->hash);
|
60
58
|
}
|
61
59
|
|
62
60
|
/* *****************************************************************************
|
63
|
-
|
61
|
+
Hash alloc + VTable
|
64
62
|
***************************************************************************** */
|
65
|
-
static inline uintptr_t fio_map_cuckoo_steps(uintptr_t step) {
|
66
|
-
// return ((step * (step + 1)) >> 1);
|
67
|
-
return (step * 3);
|
68
|
-
}
|
69
63
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
uintptr_t i = 0;
|
75
|
-
const uintptr_t limit = h->map.capa > FIOBJ_HASH_MAX_MAP_SEEK
|
76
|
-
? FIOBJ_HASH_MAX_MAP_SEEK
|
77
|
-
: (h->map.capa >> 1);
|
78
|
-
while (i < limit) {
|
79
|
-
if (!pos->hash || pos->hash == hash)
|
80
|
-
return pos;
|
81
|
-
pos = h->map.data +
|
82
|
-
(((hash & h->mask) + fio_map_cuckoo_steps(i++)) & h->mask);
|
83
|
-
}
|
84
|
-
return NULL;
|
85
|
-
}
|
86
|
-
|
87
|
-
/* seeks the hash's position in the map while actually comparing key data */
|
88
|
-
// static map_info_s *fio_hash_seek_secure(fiobj_hash_s *h, uintptr_t hash,
|
89
|
-
// fiobj_s *key) {
|
90
|
-
// /* TODO: consider implementing Robing Hood reordering during seek? */
|
91
|
-
// map_info_s *pos = h->map.data + (hash & h->mask);
|
92
|
-
// uintptr_t i = 0;
|
93
|
-
// const uintptr_t limit = h->map.capa > FIOBJ_HASH_MAX_MAP_SEEK
|
94
|
-
// ? FIOBJ_HASH_MAX_MAP_SEEK
|
95
|
-
// : (h->map.capa >> 1);
|
96
|
-
// while (i < limit) {
|
97
|
-
// if (!pos->hash ||
|
98
|
-
// (pos->hash == hash &&
|
99
|
-
// (!pos->container || !pos->container->obj ||
|
100
|
-
// obj2couplet(pos->container->obj)->name == key ||
|
101
|
-
// fiobj_iseq(obj2couplet(pos->container->obj)->name, key))))
|
102
|
-
// return pos;
|
103
|
-
// pos = h->map.data +
|
104
|
-
// (((hash & h->mask) + fio_map_cuckoo_steps(i++)) & h->mask);
|
105
|
-
// }
|
106
|
-
// return NULL;
|
107
|
-
// }
|
108
|
-
|
109
|
-
/* finds an object in the map */
|
110
|
-
static void *fio_hash_find(fiobj_hash_s *h, uintptr_t hash) {
|
111
|
-
map_info_s *info = fio_hash_seek(h, hash);
|
112
|
-
if (!info || !info->container)
|
113
|
-
return NULL;
|
114
|
-
return (void *)info->container->obj;
|
115
|
-
}
|
116
|
-
|
117
|
-
/* inserts an object to the map, rehashing if required, returning old object.
|
118
|
-
* set obj to NULL to remove existing data.
|
119
|
-
*/
|
120
|
-
static void *fio_hash_insert(fiobj_hash_s *h, uintptr_t hash, void *obj) {
|
121
|
-
map_info_s *info = fio_hash_seek(h, hash);
|
122
|
-
if (!info)
|
123
|
-
return (void *)(-1);
|
124
|
-
if (!info->container) {
|
125
|
-
/* a fresh object */
|
126
|
-
if (obj == NULL)
|
127
|
-
return NULL; /* nothing to delete */
|
128
|
-
/* create container and set hash */
|
129
|
-
fio_ls_unshift(&h->items, obj);
|
130
|
-
*info = (map_info_s){.hash = hash, .container = h->items.prev};
|
131
|
-
h->count++;
|
132
|
-
return NULL;
|
133
|
-
}
|
134
|
-
/* a container object exists, this is a "replace/delete" operation */
|
135
|
-
if (!obj) {
|
136
|
-
/* delete */
|
137
|
-
h->count--;
|
138
|
-
obj = fio_ls_remove(info->container);
|
139
|
-
*info = (map_info_s){.hash = hash}; /* hash is set to seek over position */
|
140
|
-
return obj;
|
141
|
-
}
|
142
|
-
/* replace */
|
143
|
-
void *old = (void *)info->container->obj;
|
144
|
-
info->container->obj = obj;
|
145
|
-
return old;
|
64
|
+
static void fiobj_hash_dealloc(FIOBJ o, void (*task)(FIOBJ, void *),
|
65
|
+
void *arg) {
|
66
|
+
FIO_HASH_FOR_FREE(&obj2hash(o)->hash, i) { task((FIOBJ)i->obj, arg); }
|
67
|
+
free(FIOBJ2PTR(o));
|
146
68
|
}
|
147
69
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
70
|
+
static __thread FIOBJ each_at_key = FIOBJ_INVALID;
|
71
|
+
|
72
|
+
static size_t fiobj_hash_each1(FIOBJ o, const size_t start_at,
|
73
|
+
int (*task)(FIOBJ obj, void *arg), void *arg) {
|
74
|
+
assert(o && FIOBJ_TYPE_IS(o, FIOBJ_T_HASH));
|
75
|
+
FIOBJ old_each_at_key = each_at_key;
|
76
|
+
fio_hash_s *hash = &obj2hash(o)->hash;
|
77
|
+
size_t count = 0;
|
78
|
+
if (hash->count == hash->pos) {
|
79
|
+
/* no holes in the hash, we can work as we please. */
|
80
|
+
for (count = start_at; count < hash->count; ++count) {
|
81
|
+
each_at_key = hash->ordered[count].key.key;
|
82
|
+
// fprintf(stderr, "each_at_key: %p, hash %p, org %p, obj %p\n",
|
83
|
+
// (void *)each_at_key, (void *)hash->ordered[count].key.hash,
|
84
|
+
// (void *)hash->ordered[count].key.key,
|
85
|
+
// (void *)hash->ordered[count].obj);
|
86
|
+
if (task((FIOBJ)hash->ordered[count].obj, arg) == -1) {
|
87
|
+
++count;
|
88
|
+
goto end;
|
89
|
+
}
|
90
|
+
}
|
91
|
+
} else {
|
92
|
+
fio_hash_data_ordered_s *i;
|
93
|
+
const fio_hash_data_ordered_s *stop = hash->ordered + hash->pos;
|
94
|
+
for (i = hash->ordered; count < start_at && i && i < stop; ++i) {
|
95
|
+
/* counting */
|
96
|
+
if (!i->obj)
|
97
|
+
continue;
|
98
|
+
++count;
|
99
|
+
}
|
100
|
+
for (; i && i < stop; ++i) {
|
101
|
+
/* performing */
|
102
|
+
if (!i->obj)
|
103
|
+
continue;
|
104
|
+
++count;
|
105
|
+
each_at_key = i->key.key;
|
106
|
+
if (task((FIOBJ)i->obj, arg) == -1)
|
107
|
+
break;
|
165
108
|
}
|
166
|
-
*info = (map_info_s){.hash = pos_hash, .container = pos};
|
167
|
-
pos = pos->next;
|
168
109
|
}
|
110
|
+
end:
|
111
|
+
each_at_key = old_each_at_key;
|
112
|
+
return count;
|
169
113
|
}
|
170
114
|
|
171
|
-
|
172
|
-
Couplet alloc + Couplet VTable
|
173
|
-
***************************************************************************** */
|
115
|
+
FIOBJ fiobj_hash_key_in_loop(void) { return each_at_key; }
|
174
116
|
|
175
|
-
const
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
fiobj_dealloc(o);
|
117
|
+
static size_t fiobj_hash_is_eq(const FIOBJ self, const FIOBJ other) {
|
118
|
+
if (fio_hash_count(&obj2hash(self)->hash) !=
|
119
|
+
fio_hash_count(&obj2hash(other)->hash))
|
120
|
+
return 0;
|
121
|
+
return 1;
|
181
122
|
}
|
182
123
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
return 0;
|
188
|
-
return OBJVTBL(obj2couplet(o)->obj)
|
189
|
-
->each1(obj2couplet(o)->obj, start_at, task, arg);
|
124
|
+
/** Returns the number of elements in the Array. */
|
125
|
+
size_t fiobj_hash_count(const FIOBJ o) {
|
126
|
+
assert(o && FIOBJ_TYPE_IS(o, FIOBJ_T_HASH));
|
127
|
+
return fio_hash_count(&obj2hash(o)->hash);
|
190
128
|
}
|
191
129
|
|
192
|
-
|
130
|
+
intptr_t fiobj_hash2num(const FIOBJ o) { return (intptr_t)fiobj_hash_count(o); }
|
193
131
|
|
194
|
-
|
195
|
-
|
196
|
-
if (obj2couplet(self)->name != obj2couplet(other)->name &&
|
197
|
-
(!obj2couplet(other)->name || !obj2couplet(self)->name ||
|
198
|
-
!OBJVTBL(obj2couplet(self)->name)
|
199
|
-
->is_eq(obj2couplet(self)->name, obj2couplet(other)->name)))
|
200
|
-
return 0;
|
201
|
-
return fiobj_iseq(obj2couplet(self)->obj, obj2couplet(other)->obj);
|
132
|
+
static size_t fiobj_hash_is_true(const FIOBJ o) {
|
133
|
+
return fiobj_hash_count(o) != 0;
|
202
134
|
}
|
203
135
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
return 0;
|
208
|
-
return OBJVTBL(obj2couplet(o)->obj)->count(obj2couplet(o)->obj);
|
209
|
-
}
|
136
|
+
fio_cstr_s fiobject___noop_to_str(const FIOBJ o);
|
137
|
+
intptr_t fiobject___noop_to_i(const FIOBJ o);
|
138
|
+
double fiobject___noop_to_f(const FIOBJ o);
|
210
139
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
.
|
215
|
-
.
|
216
|
-
.
|
217
|
-
.
|
218
|
-
.
|
219
|
-
.
|
220
|
-
.
|
221
|
-
.each1 = fiobj_couplet_each1,
|
140
|
+
const fiobj_object_vtable_s FIOBJECT_VTABLE_HASH = {
|
141
|
+
.class_name = "Hash",
|
142
|
+
.dealloc = fiobj_hash_dealloc,
|
143
|
+
.is_eq = fiobj_hash_is_eq,
|
144
|
+
.count = fiobj_hash_count,
|
145
|
+
.each = fiobj_hash_each1,
|
146
|
+
.is_true = fiobj_hash_is_true,
|
147
|
+
.to_str = fiobject___noop_to_str,
|
148
|
+
.to_i = fiobj_hash2num,
|
149
|
+
.to_f = fiobject___noop_to_f,
|
222
150
|
};
|
223
151
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
fiobj_s *o = fiobj_alloc(sizeof(fiobj_couplet_s));
|
228
|
-
if (!o)
|
229
|
-
perror("ERROR: fiobj hash couldn't allocate couplet"), exit(errno);
|
230
|
-
*(obj2couplet(o)) = (fiobj_couplet_s){
|
231
|
-
.vtable = &FIOBJ_VTABLE_COUPLET, .name = fiobj_dup(sym), .obj = obj};
|
232
|
-
return o;
|
233
|
-
}
|
152
|
+
/* *****************************************************************************
|
153
|
+
Hash API
|
154
|
+
***************************************************************************** */
|
234
155
|
|
235
156
|
/**
|
236
|
-
*
|
237
|
-
* (Symbol) from the key-value pair.
|
157
|
+
* Creates a mutable empty Hash object. Use `fiobj_free` when done.
|
238
158
|
*
|
239
|
-
*
|
159
|
+
* Notice that these Hash objects are designed for smaller collections and
|
160
|
+
* retain order of object insertion.
|
240
161
|
*/
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
162
|
+
FIOBJ fiobj_hash_new(void) {
|
163
|
+
fiobj_hash_s *h = malloc(sizeof(*h));
|
164
|
+
if (!h) {
|
165
|
+
perror("ERROR: fiobj hash couldn't allocate memory");
|
166
|
+
exit(errno);
|
167
|
+
}
|
168
|
+
*h = (fiobj_hash_s){.head = {.ref = 1, .type = FIOBJ_T_HASH}};
|
169
|
+
fio_hash_new(&h->hash);
|
170
|
+
return (FIOBJ)h | FIOBJECT_HASH_FLAG;
|
245
171
|
}
|
246
172
|
|
247
173
|
/**
|
248
|
-
*
|
249
|
-
*
|
174
|
+
* Creates a mutable empty Hash object with an initial capacity of `capa`. Use
|
175
|
+
* `fiobj_free` when done.
|
250
176
|
*
|
251
|
-
*
|
177
|
+
* Notice that these Hash objects are designed for smaller collections and
|
178
|
+
* retain order of object insertion.
|
252
179
|
*/
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
/* *****************************************************************************
|
260
|
-
Hash alloc + VTable
|
261
|
-
***************************************************************************** */
|
262
|
-
|
263
|
-
const uintptr_t FIOBJ_T_HASH;
|
264
|
-
|
265
|
-
static void fiobj_hash_dealloc(fiobj_s *h) {
|
266
|
-
while (fio_ls_pop(&obj2hash(h)->items))
|
267
|
-
;
|
268
|
-
free(obj2hash(h)->map.data);
|
269
|
-
obj2hash(h)->map.data = NULL;
|
270
|
-
obj2hash(h)->map.capa = 0;
|
271
|
-
fiobj_dealloc(h);
|
272
|
-
}
|
273
|
-
|
274
|
-
static size_t fiobj_hash_each1(fiobj_s *o, const size_t start_at,
|
275
|
-
int (*task)(fiobj_s *obj, void *arg),
|
276
|
-
void *arg) {
|
277
|
-
if (start_at >= obj2hash(o)->count)
|
278
|
-
return obj2hash(o)->count;
|
279
|
-
size_t i = 0;
|
280
|
-
fio_ls_s *pos = obj2hash(o)->items.next;
|
281
|
-
while (pos != &obj2hash(o)->items && start_at > i) {
|
282
|
-
pos = pos->next;
|
283
|
-
++i;
|
284
|
-
}
|
285
|
-
while (pos != &obj2hash(o)->items) {
|
286
|
-
++i;
|
287
|
-
if (task((fiobj_s *)pos->obj, arg) == -1)
|
288
|
-
return i;
|
289
|
-
pos = pos->next;
|
180
|
+
FIOBJ fiobj_hash_new2(size_t capa) {
|
181
|
+
fiobj_hash_s *h = malloc(sizeof(*h));
|
182
|
+
if (!h) {
|
183
|
+
perror("ERROR: fiobj hash couldn't allocate memory");
|
184
|
+
exit(errno);
|
290
185
|
}
|
291
|
-
|
186
|
+
*h = (fiobj_hash_s){.head = {.ref = 1, .type = FIOBJ_T_HASH}};
|
187
|
+
fio_hash_new2(&h->hash, capa);
|
188
|
+
return (FIOBJ)h | FIOBJECT_HASH_FLAG;
|
292
189
|
}
|
293
190
|
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
// if (!fio_hash_find((fiobj_hash_s *)other,
|
302
|
-
// fiobj_sym_id(obj2couplet(pos->obj)->name)))
|
303
|
-
// return 0;
|
304
|
-
// pos = pos->next;
|
305
|
-
// }
|
306
|
-
return 1;
|
307
|
-
}
|
308
|
-
|
309
|
-
/** Returns the number of elements in the Array. */
|
310
|
-
static size_t fiobj_hash_count_items(const fiobj_s *o) {
|
311
|
-
return obj2hash(o)->count;
|
191
|
+
/**
|
192
|
+
* Returns a temporary theoretical Hash map capacity.
|
193
|
+
* This could be used for testig performance and memory consumption.
|
194
|
+
*/
|
195
|
+
size_t fiobj_hash_capa(const FIOBJ hash) {
|
196
|
+
assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
|
197
|
+
return fio_hash_capa(&obj2hash(hash)->hash);
|
312
198
|
}
|
313
199
|
|
314
|
-
static struct fiobj_vtable_s FIOBJ_VTABLE_HASH = {
|
315
|
-
.free = fiobj_hash_dealloc,
|
316
|
-
.to_i = fiobj_noop_i,
|
317
|
-
.to_f = fiobj_noop_f,
|
318
|
-
.to_str = fiobj_noop_str,
|
319
|
-
.is_eq = fiobj_hash_is_eq,
|
320
|
-
.count = fiobj_hash_count_items,
|
321
|
-
.unwrap = fiobj_noop_unwrap,
|
322
|
-
.each1 = fiobj_hash_each1,
|
323
|
-
};
|
324
|
-
|
325
|
-
const uintptr_t FIOBJ_T_HASH = (uintptr_t)(&FIOBJ_VTABLE_HASH);
|
326
|
-
|
327
|
-
/* *****************************************************************************
|
328
|
-
Hash API
|
329
|
-
***************************************************************************** */
|
330
|
-
|
331
200
|
/**
|
332
|
-
*
|
201
|
+
* Sets a key-value pair in the Hash, duplicating the Symbol and **moving**
|
202
|
+
* the ownership of the object to the Hash.
|
333
203
|
*
|
334
|
-
*
|
335
|
-
* retain order of object insertion.
|
204
|
+
* Returns -1 on error.
|
336
205
|
*/
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
.mask = (HASH_INITIAL_CAPACITY - 1),
|
344
|
-
.items = FIO_LS_INIT((obj2hash(o)->items)),
|
345
|
-
.map.data = calloc(sizeof(map_info_s), HASH_INITIAL_CAPACITY),
|
346
|
-
.map.capa = HASH_INITIAL_CAPACITY,
|
347
|
-
};
|
348
|
-
if (!obj2hash(o)->map.data)
|
349
|
-
perror("ERROR: fiobj hash couldn't allocate memory"), exit(errno);
|
350
|
-
return o;
|
206
|
+
int fiobj_hash_set(FIOBJ hash, FIOBJ key, FIOBJ obj) {
|
207
|
+
assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
|
208
|
+
hash_key_s k = {.hash = fiobj_obj2hash(key), .key = key};
|
209
|
+
FIOBJ old = (FIOBJ)fio_hash_insert(&obj2hash(hash)->hash, k, (void *)obj);
|
210
|
+
fiobj_free(old);
|
211
|
+
return 0;
|
351
212
|
}
|
352
213
|
|
353
|
-
/**
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
214
|
+
/**
|
215
|
+
* Allows the Hash to be used as a stack.
|
216
|
+
*
|
217
|
+
* If a pointer `key` is provided, it will receive ownership of the key
|
218
|
+
* (remember to free).
|
219
|
+
*
|
220
|
+
* Returns FIOBJ_INVALID on error.
|
221
|
+
*
|
222
|
+
* Returns and object if successful (remember to free).
|
223
|
+
*/
|
224
|
+
FIOBJ fiobj_hash_pop(FIOBJ hash, FIOBJ *key) {
|
225
|
+
assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
|
226
|
+
hash_key_s k = {.hash = 0, .key = FIOBJ_INVALID};
|
227
|
+
FIOBJ old = (FIOBJ)fio_hash_pop(&obj2hash(hash)->hash, &k);
|
228
|
+
if (!old)
|
229
|
+
return FIOBJ_INVALID;
|
230
|
+
if (key)
|
231
|
+
*key = k.key;
|
232
|
+
else
|
233
|
+
fiobj_free(k.key);
|
234
|
+
return old;
|
358
235
|
}
|
359
236
|
|
360
237
|
/**
|
361
|
-
*
|
362
|
-
*
|
238
|
+
* Replaces the value in a key-value pair, returning the old value (and it's
|
239
|
+
* ownership) to the caller.
|
363
240
|
*
|
364
|
-
*
|
241
|
+
* A return value of NULL indicates that no previous object existed (but a new
|
242
|
+
* key-value pair was created.
|
243
|
+
*
|
244
|
+
* Errors are silently ignored.
|
365
245
|
*/
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
if (sym->type == FIOBJ_T_SYMBOL) {
|
373
|
-
hash_value = fiobj_sym_id(sym);
|
374
|
-
} else if (FIOBJ_IS_STRING(sym)) {
|
375
|
-
fio_cstr_s str = fiobj_obj2cstr(sym);
|
376
|
-
hash_value = fiobj_sym_hash(str.value, str.len);
|
377
|
-
} else {
|
378
|
-
fiobj_free((fiobj_s *)obj);
|
379
|
-
return -1;
|
380
|
-
}
|
246
|
+
FIOBJ fiobj_hash_replace(FIOBJ hash, FIOBJ key, FIOBJ obj) {
|
247
|
+
assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
|
248
|
+
hash_key_s k = {.hash = fiobj_obj2hash(key), .key = key};
|
249
|
+
FIOBJ old = (FIOBJ)fio_hash_insert(&obj2hash(hash)->hash, k, (void *)obj);
|
250
|
+
return old;
|
251
|
+
}
|
381
252
|
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
obj2couplet(old)->obj = NULL;
|
393
|
-
fiobj_couplet_dealloc(old);
|
394
|
-
}
|
395
|
-
return 0;
|
253
|
+
/**
|
254
|
+
* Removes a key-value pair from the Hash, if it exists, returning the old
|
255
|
+
* object (instead of freeing it).
|
256
|
+
*/
|
257
|
+
FIOBJ fiobj_hash_remove(FIOBJ hash, FIOBJ key) {
|
258
|
+
assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
|
259
|
+
assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
|
260
|
+
return (FIOBJ)fio_hash_insert(
|
261
|
+
&obj2hash(hash)->hash,
|
262
|
+
(hash_key_s){.hash = fiobj_obj2hash(key), .key = key}, NULL);
|
396
263
|
}
|
397
264
|
|
398
265
|
/**
|
399
266
|
* Removes a key-value pair from the Hash, if it exists, returning the old
|
400
267
|
* object (instead of freeing it).
|
401
268
|
*/
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
uintptr_t hash_value = 0;
|
407
|
-
if (sym->type == FIOBJ_T_SYMBOL) {
|
408
|
-
hash_value = fiobj_sym_id(sym);
|
409
|
-
} else if (FIOBJ_IS_STRING(sym)) {
|
410
|
-
fio_cstr_s str = fiobj_obj2cstr(sym);
|
411
|
-
hash_value = fiobj_sym_hash(str.value, str.len);
|
412
|
-
} else {
|
413
|
-
return NULL;
|
414
|
-
}
|
415
|
-
fiobj_s *coup = fio_hash_insert(obj2hash(hash), hash_value, NULL);
|
416
|
-
if (!coup)
|
417
|
-
return NULL;
|
418
|
-
fiobj_s *ret = fiobj_couplet2obj(coup);
|
419
|
-
obj2couplet(coup)->obj = NULL;
|
420
|
-
|
421
|
-
fiobj_couplet_dealloc((fiobj_s *)coup);
|
422
|
-
return ret;
|
269
|
+
FIOBJ fiobj_hash_remove2(FIOBJ hash, uint64_t hash_value) {
|
270
|
+
assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
|
271
|
+
return (FIOBJ)fio_hash_insert(&obj2hash(hash)->hash,
|
272
|
+
(hash_key_s){.hash = hash_value}, NULL);
|
423
273
|
}
|
424
274
|
|
425
275
|
/**
|
@@ -428,11 +278,29 @@ fiobj_s *fiobj_hash_remove(fiobj_s *hash, fiobj_s *sym) {
|
|
428
278
|
*
|
429
279
|
* Returns -1 on type error or if the object never existed.
|
430
280
|
*/
|
431
|
-
int fiobj_hash_delete(
|
432
|
-
|
433
|
-
if (!
|
281
|
+
int fiobj_hash_delete(FIOBJ hash, FIOBJ key) {
|
282
|
+
FIOBJ old = fiobj_hash_remove(hash, key);
|
283
|
+
if (!old)
|
434
284
|
return -1;
|
435
|
-
fiobj_free(
|
285
|
+
fiobj_free(old);
|
286
|
+
return 0;
|
287
|
+
}
|
288
|
+
|
289
|
+
/**
|
290
|
+
* Deletes a key-value pair from the Hash, if it exists, freeing the
|
291
|
+
* associated object.
|
292
|
+
*
|
293
|
+
* This function takes a `uintptr_t` Hash value (see `fio_siphash`) to
|
294
|
+
* perform a lookup in the HashMap, which is slightly faster than the other
|
295
|
+
* variations.
|
296
|
+
*
|
297
|
+
* Returns -1 on type error or if the object never existed.
|
298
|
+
*/
|
299
|
+
int fiobj_hash_delete2(FIOBJ hash, uint64_t key_hash) {
|
300
|
+
FIOBJ old = fiobj_hash_remove2(hash, key_hash);
|
301
|
+
if (!old)
|
302
|
+
return -1;
|
303
|
+
fiobj_free(old);
|
436
304
|
return 0;
|
437
305
|
}
|
438
306
|
|
@@ -440,68 +308,92 @@ int fiobj_hash_delete(fiobj_s *hash, fiobj_s *sym) {
|
|
440
308
|
* Returns a temporary handle to the object associated with the Symbol, NULL
|
441
309
|
* if none.
|
442
310
|
*/
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
if (sym->type == FIOBJ_T_SYMBOL) {
|
449
|
-
hash_value = fiobj_sym_id(sym);
|
450
|
-
} else if (FIOBJ_IS_STRING(sym)) {
|
451
|
-
fio_cstr_s str = fiobj_obj2cstr(sym);
|
452
|
-
hash_value = fiobj_sym_hash(str.value, str.len);
|
453
|
-
} else {
|
454
|
-
return 0;
|
455
|
-
}
|
456
|
-
fiobj_s *coup = fio_hash_find(obj2hash(hash), hash_value);
|
457
|
-
if (!coup)
|
458
|
-
return NULL;
|
459
|
-
return fiobj_couplet2obj(coup);
|
311
|
+
FIOBJ fiobj_hash_get(const FIOBJ hash, FIOBJ key) {
|
312
|
+
assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
|
313
|
+
return (FIOBJ)fio_hash_find(
|
314
|
+
&obj2hash(hash)->hash,
|
315
|
+
(hash_key_s){.hash = fiobj_obj2hash(key), .key = key});
|
460
316
|
}
|
461
317
|
|
462
318
|
/**
|
463
|
-
* Returns a temporary handle to the object associated
|
319
|
+
* Returns a temporary handle to the object associated hashed key value.
|
464
320
|
*
|
465
|
-
* This function takes a
|
466
|
-
*
|
321
|
+
* This function takes a `uintptr_t` Hash value (see `fio_siphash`) to
|
322
|
+
* perform a lookup in the HashMap.
|
467
323
|
*
|
468
|
-
* Returns NULL if no object is asociated with this
|
324
|
+
* Returns NULL if no object is asociated with this hashed key value.
|
469
325
|
*/
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
fiobj_s *coup = fio_hash_find(obj2hash(hash), hashed_sym);
|
476
|
-
if (!coup)
|
477
|
-
return NULL;
|
478
|
-
return fiobj_couplet2obj(coup);
|
326
|
+
FIOBJ fiobj_hash_get2(const FIOBJ hash, uint64_t key_hash) {
|
327
|
+
assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
|
328
|
+
return (FIOBJ)fio_hash_find(&obj2hash(hash)->hash, (hash_key_s){
|
329
|
+
.hash = key_hash,
|
330
|
+
});
|
479
331
|
}
|
480
332
|
|
481
333
|
/**
|
482
334
|
* Returns 1 if the key (Symbol) exists in the Hash, even if value is NULL.
|
483
335
|
*/
|
484
|
-
int fiobj_hash_haskey(const
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
uintptr_t hash_value = 0;
|
489
|
-
if (sym->type == FIOBJ_T_SYMBOL) {
|
490
|
-
hash_value = fiobj_sym_id(sym);
|
491
|
-
} else if (FIOBJ_IS_STRING(sym)) {
|
492
|
-
fio_cstr_s str = fiobj_obj2cstr(sym);
|
493
|
-
hash_value = fiobj_sym_hash(str.value, str.len);
|
494
|
-
} else {
|
495
|
-
return 0;
|
496
|
-
}
|
497
|
-
fiobj_s *coup = fio_hash_find(obj2hash(hash), hash_value);
|
498
|
-
if (!coup)
|
499
|
-
return 0;
|
500
|
-
return 1;
|
336
|
+
int fiobj_hash_haskey(const FIOBJ hash, FIOBJ key) {
|
337
|
+
assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
|
338
|
+
hash_key_s k = {.hash = fiobj_obj2hash(key), .key = key};
|
339
|
+
return (FIOBJ)fio_hash_find(&obj2hash(hash)->hash, k) != 0;
|
501
340
|
}
|
502
341
|
|
503
342
|
/**
|
504
|
-
*
|
505
|
-
* This could be used for testig performance and memory consumption.
|
343
|
+
* Empties the Hash.
|
506
344
|
*/
|
507
|
-
|
345
|
+
void fiobj_hash_clear(const FIOBJ hash) {
|
346
|
+
assert(hash && FIOBJ_TYPE_IS(hash, FIOBJ_T_HASH));
|
347
|
+
FIO_HASH_FOR_EMPTY(&obj2hash(hash)->hash, i) { fiobj_free((FIOBJ)i->obj); }
|
348
|
+
}
|
349
|
+
|
350
|
+
/* *****************************************************************************
|
351
|
+
Simple Tests
|
352
|
+
***************************************************************************** */
|
353
|
+
|
354
|
+
#if DEBUG
|
355
|
+
void fiobj_test_hash(void) {
|
356
|
+
fprintf(stderr, "=== Testing Hash\n");
|
357
|
+
#define TEST_ASSERT(cond, ...) \
|
358
|
+
if (!(cond)) { \
|
359
|
+
fprintf(stderr, "* " __VA_ARGS__); \
|
360
|
+
fprintf(stderr, "Testing failed.\n"); \
|
361
|
+
exit(-1); \
|
362
|
+
}
|
363
|
+
FIOBJ o = fiobj_hash_new();
|
364
|
+
FIOBJ str_key = fiobj_str_new("Hello World!", 12);
|
365
|
+
TEST_ASSERT(FIOBJ_TYPE_IS(o, FIOBJ_T_HASH), "Type identification error!\n");
|
366
|
+
TEST_ASSERT(fiobj_hash_count(o) == 0, "Hash should be empty!\n");
|
367
|
+
fiobj_hash_set(o, str_key, fiobj_true());
|
368
|
+
TEST_ASSERT(fiobj_str_write(str_key, "should fail...", 13) == 0,
|
369
|
+
"wrote to frozen string?");
|
370
|
+
TEST_ASSERT(fiobj_obj2cstr(str_key).len == 12,
|
371
|
+
"String was mutated (not frozen)!\n");
|
372
|
+
TEST_ASSERT(fiobj_hash_get(o, str_key) == fiobj_true(),
|
373
|
+
"full compare didn't get value back");
|
374
|
+
TEST_ASSERT(fiobj_hash_get2(o, fiobj_obj2hash(str_key)) == fiobj_true(),
|
375
|
+
"hash compare didn't get value back");
|
376
|
+
|
377
|
+
FIOBJ o2 = fiobj_hash_new2(1);
|
378
|
+
TEST_ASSERT(obj2hash(o2)->hash.capa == 1,
|
379
|
+
"Hash capacity should be initialized to 1! %zu != 1\n",
|
380
|
+
(size_t)obj2hash(o2)->hash.capa);
|
381
|
+
fiobj_hash_set(o2, str_key, fiobj_true());
|
382
|
+
TEST_ASSERT(fiobj_hash_is_eq(o, o2), "Hashes not equal at core! %zu != %zu\n",
|
383
|
+
fiobj_hash_count(o), fiobj_hash_count(o2));
|
384
|
+
TEST_ASSERT(fiobj_iseq(o, o2), "Hashes not equal!\n");
|
385
|
+
TEST_ASSERT(obj2hash(o2)->hash.capa == 1,
|
386
|
+
"Hash capacity should be 1! %zu != 1\n",
|
387
|
+
(size_t)obj2hash(o2)->hash.capa);
|
388
|
+
|
389
|
+
fiobj_hash_delete(o, str_key);
|
390
|
+
|
391
|
+
TEST_ASSERT(fiobj_hash_get2(o, fiobj_obj2hash(str_key)) == 0,
|
392
|
+
"item wasn't deleted!");
|
393
|
+
fiobj_free(
|
394
|
+
str_key); /* note that a copy will remain in the Hash until rehashing. */
|
395
|
+
fiobj_free(o);
|
396
|
+
fiobj_free(o2);
|
397
|
+
fprintf(stderr, "* passed.\n");
|
398
|
+
}
|
399
|
+
#endif
|