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
@@ -8,7 +8,7 @@ Feel free to copy, use and enjoy according to the license provided.
|
|
8
8
|
/**
|
9
9
|
|
10
10
|
This is a customized version for command line interface (CLI) arguments. All
|
11
|
-
arguments are converted
|
11
|
+
arguments are converted into a key-value paired Hash.
|
12
12
|
|
13
13
|
The CLI helper automatically provides `-?`, `-h` and `-help` support that
|
14
14
|
prints a short explanation for every option and exits.
|
@@ -39,8 +39,10 @@ EXAMPLE:
|
|
39
39
|
const char *port = fio_cli_get_str("port");
|
40
40
|
if (!port)
|
41
41
|
port = "3000";
|
42
|
+
// use parsed information.
|
43
|
+
// ...
|
44
|
+
// cleanup
|
42
45
|
fio_cli_end();
|
43
|
-
// .. use parsed information.
|
44
46
|
|
45
47
|
|
46
48
|
*/
|
@@ -51,10 +53,13 @@ EXAMPLE:
|
|
51
53
|
extern "C" {
|
52
54
|
#endif
|
53
55
|
|
54
|
-
/** Initialize the CLI helper and adds the `info` string to the help section */
|
56
|
+
/** Initialize the CLI helper and adds the `info` string to the help section. */
|
55
57
|
void fio_cli_start(int argc, const char **argv, const char *info);
|
56
58
|
|
57
|
-
/**
|
59
|
+
/** Tells the CLI helper to ignore unrecognized command line arguments. */
|
60
|
+
void fio_cli_ignore_unknown(void);
|
61
|
+
|
62
|
+
/** Clears the memory and resources related to the CLI helper. */
|
58
63
|
void fio_cli_end(void);
|
59
64
|
|
60
65
|
/**
|
@@ -0,0 +1,759 @@
|
|
1
|
+
#ifndef H_FIO_SIMPLE_HASH_H
|
2
|
+
/*
|
3
|
+
Copyright: Boaz Segev, 2017-2018
|
4
|
+
License: MIT
|
5
|
+
*/
|
6
|
+
|
7
|
+
/**
|
8
|
+
* A simple ordered Hash Table implementation, with a minimal API and zero hash
|
9
|
+
* collision protection.
|
10
|
+
*
|
11
|
+
* Unique keys are required. Full key collisions aren't handled, instead the old
|
12
|
+
* value is replaced and returned.
|
13
|
+
*
|
14
|
+
* Partial key collisions are handled by seeking forward and attempting to find
|
15
|
+
* a close enough spot. If a close enough spot isn't found, rehashing is
|
16
|
+
* initiated and memory consumption increases.
|
17
|
+
*
|
18
|
+
* The Hash Table is ordered using an internal ordered array of data containers
|
19
|
+
* with duplicates of the key data (to improve cache locality).
|
20
|
+
*
|
21
|
+
* The file was written to be compatible with C++ as well as C, hence some
|
22
|
+
* pointer casting.
|
23
|
+
*/
|
24
|
+
#define H_FIO_SIMPLE_HASH_H
|
25
|
+
|
26
|
+
#ifndef _GNU_SOURCE
|
27
|
+
#define _GNU_SOURCE
|
28
|
+
#endif
|
29
|
+
|
30
|
+
#ifndef FIO_FUNC
|
31
|
+
#define FIO_FUNC static __attribute__((unused))
|
32
|
+
#endif
|
33
|
+
|
34
|
+
#include <errno.h>
|
35
|
+
#include <stdint.h>
|
36
|
+
#include <stdio.h>
|
37
|
+
#include <stdlib.h>
|
38
|
+
#include <string.h>
|
39
|
+
|
40
|
+
/**
|
41
|
+
* extra collision protection can be obtained by defining ALL of the following:
|
42
|
+
* * FIO_HASH_KEY_TYPE - the type used for keys.
|
43
|
+
* * FIO_HASH_KEY_INVALID - an invalid key with it's bytes set to zero.
|
44
|
+
* * FIO_HASH_KEY2UINT(key) - converts a key to a hash value number.
|
45
|
+
* * FIO_HASH_COMPARE_KEYS(k1, k2) - compares two key.
|
46
|
+
* * FIO_HASH_KEY_ISINVALID(key) - tests for an invalid key.
|
47
|
+
* * FIO_HASH_KEY_COPY(key) - creates a persistent copy of the key.
|
48
|
+
* * FIO_HASH_KEY_DESTROY(key) - destroys (or frees) the key's copy.
|
49
|
+
*
|
50
|
+
* Note: FIO_HASH_COMPARE_KEYS will be used to compare against
|
51
|
+
* FIO_HASH_KEY_INVALID as well as valid keys.
|
52
|
+
*
|
53
|
+
* Note: Before freeing the Hash, FIO_HASH_KEY_DESTROY should be called for
|
54
|
+
* every key. This is NOT automatic. see the FIO_HASH_FOR_EMPTY(h) macro.
|
55
|
+
*/
|
56
|
+
#if !defined(FIO_HASH_COMPARE_KEYS) || !defined(FIO_HASH_KEY_TYPE) || \
|
57
|
+
!defined(FIO_HASH_KEY2UINT) || !defined(FIO_HASH_KEY_INVALID) || \
|
58
|
+
!defined(FIO_HASH_KEY_ISINVALID) || !defined(FIO_HASH_KEY_COPY) || \
|
59
|
+
!defined(FIO_HASH_KEY_DESTROY)
|
60
|
+
#define FIO_HASH_KEY_TYPE uint64_t
|
61
|
+
#define FIO_HASH_KEY_INVALID 0
|
62
|
+
#define FIO_HASH_KEY2UINT(key) (key)
|
63
|
+
#define FIO_HASH_COMPARE_KEYS(k1, k2) ((k1) == (k2))
|
64
|
+
#define FIO_HASH_KEY_ISINVALID(key) ((key) == 0)
|
65
|
+
#define FIO_HASH_KEY_COPY(key) (key)
|
66
|
+
#define FIO_HASH_KEY_DESTROY(key) ((void)0)
|
67
|
+
#elif !defined(FIO_HASH_NO_TEST)
|
68
|
+
#define FIO_HASH_NO_TEST 1
|
69
|
+
#endif
|
70
|
+
|
71
|
+
#ifndef FIO_HASH_INITIAL_CAPACITY
|
72
|
+
/* MUST be a power of 2 */
|
73
|
+
#define FIO_HASH_INITIAL_CAPACITY 4
|
74
|
+
#endif
|
75
|
+
|
76
|
+
#ifndef FIO_HASH_MAX_MAP_SEEK
|
77
|
+
/* MUST be a power of 2 */
|
78
|
+
#define FIO_HASH_MAX_MAP_SEEK (256)
|
79
|
+
#endif
|
80
|
+
|
81
|
+
#ifndef FIO_HASH_REALLOC /* NULL ptr indicates new allocation */
|
82
|
+
#define FIO_HASH_REALLOC(ptr, original_size, new_size, valid_data_length) \
|
83
|
+
realloc((ptr), (new_size))
|
84
|
+
#endif
|
85
|
+
#ifndef FIO_HASH_CALLOC
|
86
|
+
#define FIO_HASH_CALLOC(size, count) calloc((size), (count))
|
87
|
+
#endif
|
88
|
+
#ifndef FIO_HASH_FREE
|
89
|
+
#define FIO_HASH_FREE(ptr, size) free((ptr))
|
90
|
+
#endif
|
91
|
+
|
92
|
+
/* *****************************************************************************
|
93
|
+
Hash API
|
94
|
+
***************************************************************************** */
|
95
|
+
|
96
|
+
/** The Hash Table container type. */
|
97
|
+
typedef struct fio_hash_s fio_hash_s;
|
98
|
+
|
99
|
+
/** Allocates and initializes internal data and resources. */
|
100
|
+
FIO_FUNC void fio_hash_new(fio_hash_s *hash);
|
101
|
+
|
102
|
+
/** Allocates and initializes internal data and resources with the requested
|
103
|
+
* capacity. */
|
104
|
+
FIO_FUNC void fio_hash_new2(fio_hash_s *hash, size_t capa);
|
105
|
+
|
106
|
+
/** Deallocates any internal resources. */
|
107
|
+
FIO_FUNC void fio_hash_free(fio_hash_s *hash);
|
108
|
+
|
109
|
+
/** Locates an object in the Hash Map Table according to the hash key value. */
|
110
|
+
FIO_FUNC inline void *fio_hash_find(fio_hash_s *hash, FIO_HASH_KEY_TYPE key);
|
111
|
+
|
112
|
+
/**
|
113
|
+
* Inserts an object to the Hash Map Table, rehashing if required, returning the
|
114
|
+
* old object if it exists.
|
115
|
+
*
|
116
|
+
* Set obj to NULL to remove an existing data (the existing object will be
|
117
|
+
* returned).
|
118
|
+
*/
|
119
|
+
FIO_FUNC void *fio_hash_insert(fio_hash_s *hash, FIO_HASH_KEY_TYPE key,
|
120
|
+
void *obj);
|
121
|
+
|
122
|
+
/**
|
123
|
+
* Allows the Hash to be momenterally used as a stack, poping the last element
|
124
|
+
* entered.
|
125
|
+
*
|
126
|
+
* If a pointer to `key` is provided, the element's key will be placed in it's
|
127
|
+
* place.
|
128
|
+
*
|
129
|
+
* Remember that keys are likely to be freed as well (`FIO_HASH_KEY_DESTROY`).
|
130
|
+
*/
|
131
|
+
FIO_FUNC void *fio_hash_pop(fio_hash_s *hash, FIO_HASH_KEY_TYPE *key);
|
132
|
+
|
133
|
+
/**
|
134
|
+
* Allows a peak at the Hash's last element.
|
135
|
+
*
|
136
|
+
* If a pointer to `key` is provided, the element's key will be placed in it's
|
137
|
+
* place.
|
138
|
+
*
|
139
|
+
* Remember that keys might be destroyed if the Hash is altered
|
140
|
+
* (`FIO_HASH_KEY_DESTROY`).
|
141
|
+
*/
|
142
|
+
FIO_FUNC void *fio_hash_last(fio_hash_s *hash, FIO_HASH_KEY_TYPE *key);
|
143
|
+
|
144
|
+
/** Returns the number of elements currently in the Hash Table. */
|
145
|
+
FIO_FUNC inline size_t fio_hash_count(const fio_hash_s *hash);
|
146
|
+
|
147
|
+
/**
|
148
|
+
* Returns a temporary theoretical Hash map capacity.
|
149
|
+
* This could be used for testing performance and memory consumption.
|
150
|
+
*/
|
151
|
+
FIO_FUNC inline size_t fio_hash_capa(const fio_hash_s *hash);
|
152
|
+
|
153
|
+
/**
|
154
|
+
* Attempts to minimize memory usage by removing empty spaces caused by deleted
|
155
|
+
* items and rehashing the Hash Map.
|
156
|
+
*
|
157
|
+
* Returns the updated hash map capacity.
|
158
|
+
*/
|
159
|
+
FIO_FUNC inline size_t fio_hash_compact(fio_hash_s *hash);
|
160
|
+
|
161
|
+
/** Forces a rehashing of the hash. */
|
162
|
+
FIO_FUNC void fio_hash_rehash(fio_hash_s *hash);
|
163
|
+
|
164
|
+
/**
|
165
|
+
* Iteration using a callback for each entry in the Hash Table.
|
166
|
+
*
|
167
|
+
* The callback task function must accept the hash key, the entry data and an
|
168
|
+
* opaque user pointer:
|
169
|
+
*
|
170
|
+
* int example_task(FIO_HASH_KEY_TYPE key, void *obj, void *arg) {return 0;}
|
171
|
+
*
|
172
|
+
* If the callback returns -1, the loop is broken. Any other value is ignored.
|
173
|
+
*
|
174
|
+
* Returns the relative "stop" position, i.e., the number of items processed +
|
175
|
+
* the starting point.
|
176
|
+
*/
|
177
|
+
FIO_FUNC inline size_t fio_hash_each(fio_hash_s *hash, const size_t start_at,
|
178
|
+
int (*task)(FIO_HASH_KEY_TYPE key,
|
179
|
+
void *obj, void *arg),
|
180
|
+
void *arg);
|
181
|
+
|
182
|
+
/**
|
183
|
+
* A macro for a `for` loop that iterates over all the hashed objects (in
|
184
|
+
* order).
|
185
|
+
*
|
186
|
+
* `hash` a pointer to the hash table variable and `i` is a temporary variable
|
187
|
+
* name to be created for iteration.
|
188
|
+
*
|
189
|
+
* `i->key` is the key and `i->obj` is the hashed data.
|
190
|
+
*/
|
191
|
+
#define FIO_HASH_FOR_LOOP(hash, i)
|
192
|
+
|
193
|
+
/**
|
194
|
+
* A macro for a `for` loop that will iterate over all the hashed objects (in
|
195
|
+
* order) and empties the hash, later calling `fio_hash_free` to free the hash
|
196
|
+
* (but not the container).
|
197
|
+
*
|
198
|
+
* `hash` a pointer to the hash table variable and `i` is a temporary variable
|
199
|
+
* name to be created for iteration.
|
200
|
+
*
|
201
|
+
* `i->key` is the key and `i->obj` is the hashed data.
|
202
|
+
*
|
203
|
+
* Free the objects and the Hash Map container manually (if required). Custom
|
204
|
+
* keys will be freed automatically when using this macro.
|
205
|
+
*
|
206
|
+
*/
|
207
|
+
#define FIO_HASH_FOR_FREE(hash, i)
|
208
|
+
|
209
|
+
/**
|
210
|
+
* A macro for a `for` loop that iterates over all the hashed objects (in
|
211
|
+
* order) and empties the hash.
|
212
|
+
*
|
213
|
+
* This will also reallocate the map's memory (to zero out the data), so if this
|
214
|
+
* is performed before calling `fio_hash_free`, use FIO_HASH_FOR_FREE instead.
|
215
|
+
*
|
216
|
+
* `hash` a pointer to the hash table variable and `i` is a temporary variable
|
217
|
+
* name to be created for iteration.
|
218
|
+
*
|
219
|
+
* `i->key` is the key and `i->obj` is the hashed data.
|
220
|
+
*
|
221
|
+
* Free the objects and the Hash Map container manually (if required). Custom
|
222
|
+
* keys will be freed automatically when using this macro.
|
223
|
+
*
|
224
|
+
*/
|
225
|
+
#define FIO_HASH_FOR_EMPTY(hash, i)
|
226
|
+
|
227
|
+
/* *****************************************************************************
|
228
|
+
Hash Table Internal Data Structures
|
229
|
+
***************************************************************************** */
|
230
|
+
|
231
|
+
typedef struct fio_hash_data_ordered_s {
|
232
|
+
FIO_HASH_KEY_TYPE key; /* another copy for memory cache locality */
|
233
|
+
void *obj;
|
234
|
+
} fio_hash_data_ordered_s;
|
235
|
+
|
236
|
+
typedef struct fio_hash_data_s {
|
237
|
+
FIO_HASH_KEY_TYPE key; /* another copy for memory cache locality */
|
238
|
+
struct fio_hash_data_ordered_s *obj;
|
239
|
+
} fio_hash_data_s;
|
240
|
+
|
241
|
+
/* the information in the Hash Map structure should be considered READ ONLY. */
|
242
|
+
struct fio_hash_s {
|
243
|
+
uintptr_t count;
|
244
|
+
uintptr_t capa;
|
245
|
+
uintptr_t pos;
|
246
|
+
uintptr_t mask;
|
247
|
+
fio_hash_data_ordered_s *ordered;
|
248
|
+
fio_hash_data_s *map;
|
249
|
+
};
|
250
|
+
|
251
|
+
#undef FIO_HASH_FOR_LOOP
|
252
|
+
#define FIO_HASH_FOR_LOOP(hash, container) \
|
253
|
+
for (fio_hash_data_ordered_s *container = (hash)->ordered; \
|
254
|
+
container && (container < (hash)->ordered + (hash)->pos); ++container)
|
255
|
+
|
256
|
+
#undef FIO_HASH_FOR_FREE
|
257
|
+
#define FIO_HASH_FOR_FREE(hash, container) \
|
258
|
+
for (fio_hash_data_ordered_s *container = (hash)->ordered; \
|
259
|
+
(container && container >= (hash)->ordered && \
|
260
|
+
(container < (hash)->ordered + (hash)->pos)) || \
|
261
|
+
((fio_hash_free(hash), (hash)->ordered) != NULL); \
|
262
|
+
FIO_HASH_KEY_DESTROY(container->key), (++container))
|
263
|
+
|
264
|
+
#undef FIO_HASH_FOR_EMPTY
|
265
|
+
#define FIO_HASH_FOR_EMPTY(hash, container) \
|
266
|
+
for (fio_hash_data_ordered_s *container = (hash)->ordered; \
|
267
|
+
(container && (container < (hash)->ordered + (hash)->pos)) || \
|
268
|
+
(memset((hash)->map, 0, (hash)->capa * sizeof(*(hash)->map)), \
|
269
|
+
((hash)->pos = (hash)->count = 0)); \
|
270
|
+
(FIO_HASH_KEY_DESTROY(container->key), \
|
271
|
+
container->key = FIO_HASH_KEY_INVALID, container->obj = NULL), \
|
272
|
+
(++container))
|
273
|
+
#define FIO_HASH_INIT \
|
274
|
+
{ .capa = 0 }
|
275
|
+
|
276
|
+
/* *****************************************************************************
|
277
|
+
Hash allocation / deallocation.
|
278
|
+
***************************************************************************** */
|
279
|
+
|
280
|
+
/** Allocates and initializes internal data and resources with the requested
|
281
|
+
* capacity. */
|
282
|
+
FIO_FUNC void fio_hash__new__internal__safe_capa(fio_hash_s *h, size_t capa) {
|
283
|
+
*h = (fio_hash_s){
|
284
|
+
.mask = (capa - 1),
|
285
|
+
.map = (fio_hash_data_s *)FIO_HASH_CALLOC(sizeof(*h->map), capa),
|
286
|
+
.ordered =
|
287
|
+
(fio_hash_data_ordered_s *)FIO_HASH_CALLOC(sizeof(*h->ordered), capa),
|
288
|
+
.capa = capa,
|
289
|
+
};
|
290
|
+
if (!h->map || !h->ordered) {
|
291
|
+
perror("ERROR: Hash Table couldn't allocate memory");
|
292
|
+
exit(errno);
|
293
|
+
}
|
294
|
+
h->ordered[0] =
|
295
|
+
(fio_hash_data_ordered_s){.key = FIO_HASH_KEY_INVALID, .obj = NULL};
|
296
|
+
}
|
297
|
+
|
298
|
+
/** Allocates and initializes internal data and resources with the requested
|
299
|
+
* capacity. */
|
300
|
+
FIO_FUNC void fio_hash_new2(fio_hash_s *h, size_t capa) {
|
301
|
+
size_t act_capa = 1;
|
302
|
+
while (act_capa < capa)
|
303
|
+
act_capa = act_capa << 1;
|
304
|
+
fio_hash__new__internal__safe_capa(h, act_capa);
|
305
|
+
}
|
306
|
+
|
307
|
+
FIO_FUNC void fio_hash_new(fio_hash_s *h) {
|
308
|
+
fio_hash__new__internal__safe_capa(h, FIO_HASH_INITIAL_CAPACITY);
|
309
|
+
}
|
310
|
+
|
311
|
+
FIO_FUNC void fio_hash_free(fio_hash_s *h) {
|
312
|
+
FIO_HASH_FREE(h->map, h->capa);
|
313
|
+
FIO_HASH_FREE(h->ordered, h->capa);
|
314
|
+
*h = (fio_hash_s){.map = NULL};
|
315
|
+
}
|
316
|
+
|
317
|
+
/* *****************************************************************************
|
318
|
+
Internal HashMap Functions
|
319
|
+
***************************************************************************** */
|
320
|
+
FIO_FUNC inline uintptr_t fio_hash_map_cuckoo_steps(uintptr_t step) {
|
321
|
+
return (step * 3);
|
322
|
+
}
|
323
|
+
|
324
|
+
/* seeks the hash's position in the map */
|
325
|
+
FIO_FUNC fio_hash_data_s *fio_hash_seek_pos_(fio_hash_s *hash,
|
326
|
+
FIO_HASH_KEY_TYPE key) {
|
327
|
+
/* TODO: consider implementing Robing Hood reordering during seek? */
|
328
|
+
fio_hash_data_s *pos = hash->map + (FIO_HASH_KEY2UINT(key) & hash->mask);
|
329
|
+
uintptr_t i = 0;
|
330
|
+
const uintptr_t limit = hash->capa > FIO_HASH_MAX_MAP_SEEK
|
331
|
+
? FIO_HASH_MAX_MAP_SEEK
|
332
|
+
: ((hash->capa >> 1) | 1);
|
333
|
+
while (i < limit) {
|
334
|
+
if (FIO_HASH_KEY_ISINVALID(pos->key) ||
|
335
|
+
(FIO_HASH_KEY2UINT(pos->key) == FIO_HASH_KEY2UINT(key) &&
|
336
|
+
FIO_HASH_COMPARE_KEYS(pos->key, key)))
|
337
|
+
return pos;
|
338
|
+
pos = hash->map + (((FIO_HASH_KEY2UINT(key) & hash->mask) +
|
339
|
+
fio_hash_map_cuckoo_steps(i++)) &
|
340
|
+
hash->mask);
|
341
|
+
}
|
342
|
+
return NULL;
|
343
|
+
}
|
344
|
+
|
345
|
+
/* finds an object in the map */
|
346
|
+
FIO_FUNC inline void *fio_hash_find(fio_hash_s *hash, FIO_HASH_KEY_TYPE key) {
|
347
|
+
if (!hash->map)
|
348
|
+
return NULL;
|
349
|
+
fio_hash_data_s *info = fio_hash_seek_pos_(hash, key);
|
350
|
+
if (!info || !info->obj)
|
351
|
+
return NULL;
|
352
|
+
return (void *)info->obj->obj;
|
353
|
+
}
|
354
|
+
|
355
|
+
/* inserts an object to the map, rehashing if required, returning old object.
|
356
|
+
* set obj to NULL to remove existing data.
|
357
|
+
*/
|
358
|
+
FIO_FUNC void *fio_hash_insert(fio_hash_s *hash, FIO_HASH_KEY_TYPE key,
|
359
|
+
void *obj) {
|
360
|
+
/* ensure some space */
|
361
|
+
if (obj && hash->pos >= hash->capa)
|
362
|
+
fio_hash_rehash(hash);
|
363
|
+
|
364
|
+
/* find where the object belongs in the map */
|
365
|
+
fio_hash_data_s *info = fio_hash_seek_pos_(hash, key);
|
366
|
+
if (!info && !obj)
|
367
|
+
return NULL;
|
368
|
+
while (!info) {
|
369
|
+
fio_hash_rehash(hash);
|
370
|
+
info = fio_hash_seek_pos_(hash, key);
|
371
|
+
}
|
372
|
+
|
373
|
+
if (!info->obj) {
|
374
|
+
/* a fresh object */
|
375
|
+
|
376
|
+
if (obj == NULL) {
|
377
|
+
/* nothing to delete */
|
378
|
+
return NULL;
|
379
|
+
}
|
380
|
+
|
381
|
+
/* add object to ordered hash */
|
382
|
+
hash->ordered[hash->pos] =
|
383
|
+
(fio_hash_data_ordered_s){.key = FIO_HASH_KEY_COPY(key), .obj = obj};
|
384
|
+
|
385
|
+
/* add object to map */
|
386
|
+
*info = (fio_hash_data_s){.key = hash->ordered[hash->pos].key,
|
387
|
+
.obj = hash->ordered + hash->pos};
|
388
|
+
|
389
|
+
/* manage counters and mark end position */
|
390
|
+
hash->count++;
|
391
|
+
hash->pos++;
|
392
|
+
return NULL;
|
393
|
+
}
|
394
|
+
|
395
|
+
if (!obj && !info->obj->obj) {
|
396
|
+
/* a delete operation for an empty element */
|
397
|
+
return NULL;
|
398
|
+
}
|
399
|
+
|
400
|
+
/* an object exists, this is a "replace/delete" operation */
|
401
|
+
const void *old = (void *)info->obj->obj;
|
402
|
+
|
403
|
+
if (!obj) {
|
404
|
+
/* it was a delete operation */
|
405
|
+
if (info->obj == hash->ordered + hash->pos - 1) {
|
406
|
+
/* we removed the last ordered element, no need to keep any holes. */
|
407
|
+
--hash->pos;
|
408
|
+
FIO_HASH_KEY_DESTROY(hash->ordered[hash->pos].key);
|
409
|
+
hash->ordered[hash->pos] =
|
410
|
+
(fio_hash_data_ordered_s){.obj = NULL, .key = FIO_HASH_KEY_INVALID};
|
411
|
+
*info = (fio_hash_data_s){.obj = NULL, .key = FIO_HASH_KEY_INVALID};
|
412
|
+
if (hash->pos && !hash->ordered[hash->pos - 1].obj) {
|
413
|
+
fio_hash_pop(hash, NULL);
|
414
|
+
} else {
|
415
|
+
--hash->count;
|
416
|
+
}
|
417
|
+
|
418
|
+
return (void *)old;
|
419
|
+
}
|
420
|
+
--hash->count;
|
421
|
+
} else if (!old) {
|
422
|
+
/* inserted an item after a previous one was removed. */
|
423
|
+
++hash->count;
|
424
|
+
}
|
425
|
+
info->obj->obj = obj;
|
426
|
+
|
427
|
+
return (void *)old;
|
428
|
+
}
|
429
|
+
|
430
|
+
/**
|
431
|
+
* Allows the Hash to be momenterally used as a stack, poping the last element
|
432
|
+
* entered.
|
433
|
+
* Remember that keys might have to be freed as well (`FIO_HASH_KEY_DESTROY`).
|
434
|
+
*/
|
435
|
+
FIO_FUNC void *fio_hash_pop(fio_hash_s *hash, FIO_HASH_KEY_TYPE *key) {
|
436
|
+
if (!hash->pos)
|
437
|
+
return NULL;
|
438
|
+
--(hash->pos);
|
439
|
+
--(hash->count);
|
440
|
+
void *old = hash->ordered[hash->pos].obj;
|
441
|
+
/* removing hole from hashtable is possible because it's the last element */
|
442
|
+
fio_hash_data_s *info =
|
443
|
+
fio_hash_seek_pos_(hash, hash->ordered[hash->pos].key);
|
444
|
+
if (!info) {
|
445
|
+
/* no info is a data corruption error. */
|
446
|
+
fprintf(stderr, "FATAL ERROR: (fio_hash) unexpected missing container.\n");
|
447
|
+
exit(-1);
|
448
|
+
}
|
449
|
+
*info = (fio_hash_data_s){.obj = NULL};
|
450
|
+
/* cleanup key (or copy to target) and reset the ordered position. */
|
451
|
+
if (key)
|
452
|
+
*key = hash->ordered[hash->pos].key;
|
453
|
+
else
|
454
|
+
FIO_HASH_KEY_DESTROY(hash->ordered[hash->pos].key);
|
455
|
+
hash->ordered[hash->pos] =
|
456
|
+
(fio_hash_data_ordered_s){.obj = NULL, .key = FIO_HASH_KEY_INVALID};
|
457
|
+
/* remove any holes from the top (top is kept tight) */
|
458
|
+
while (hash->pos && hash->ordered[hash->pos - 1].obj == NULL) {
|
459
|
+
--(hash->pos);
|
460
|
+
info = fio_hash_seek_pos_(hash, hash->ordered[hash->pos].key);
|
461
|
+
if (!info) {
|
462
|
+
/* no info is a data corruption error. */
|
463
|
+
fprintf(stderr,
|
464
|
+
"FATAL ERROR: (fio_hash) unexpected missing container (2).\n");
|
465
|
+
exit(-1);
|
466
|
+
}
|
467
|
+
*info = (fio_hash_data_s){.obj = NULL};
|
468
|
+
FIO_HASH_KEY_DESTROY(hash->ordered[hash->pos].key);
|
469
|
+
hash->ordered[hash->pos] =
|
470
|
+
(fio_hash_data_ordered_s){.obj = NULL, .key = FIO_HASH_KEY_INVALID};
|
471
|
+
}
|
472
|
+
return old;
|
473
|
+
}
|
474
|
+
|
475
|
+
/**
|
476
|
+
* Allows a peak at the Hash's last element.
|
477
|
+
*
|
478
|
+
* If a pointer to `key` is provided, the element's key will be placed in it's
|
479
|
+
* place.
|
480
|
+
*
|
481
|
+
* Remember that keys might be destroyed if the Hash is altered
|
482
|
+
* (`FIO_HASH_KEY_DESTROY`).
|
483
|
+
*/
|
484
|
+
FIO_FUNC void *fio_hash_last(fio_hash_s *hash, FIO_HASH_KEY_TYPE *key) {
|
485
|
+
if (key)
|
486
|
+
*key = hash->ordered[hash->pos - 1].key;
|
487
|
+
return hash->ordered[hash->pos - 1].obj;
|
488
|
+
}
|
489
|
+
|
490
|
+
/* attempts to rehash the hashmap. */
|
491
|
+
FIO_FUNC void fio_hash_rehash(fio_hash_s *h) {
|
492
|
+
if (!h->capa) /* lazy initialization */
|
493
|
+
h->mask = FIO_HASH_INITIAL_CAPACITY - 1;
|
494
|
+
retry_rehashing:
|
495
|
+
h->mask = ((h->mask) << 1) | 1;
|
496
|
+
{
|
497
|
+
/* It's better to reallocate using calloc than manually zero out memory */
|
498
|
+
/* Maybe there's enough zeroed out pages available in the system */
|
499
|
+
FIO_HASH_FREE(h->map, h->capa);
|
500
|
+
h->capa = h->mask + 1;
|
501
|
+
h->map = (fio_hash_data_s *)FIO_HASH_CALLOC(sizeof(*h->map), h->capa);
|
502
|
+
if (!h->map) {
|
503
|
+
perror("HashMap Allocation Failed");
|
504
|
+
exit(errno);
|
505
|
+
}
|
506
|
+
/* the ordered list doesn't care about initialized memory, so realloc */
|
507
|
+
/* will be faster. */
|
508
|
+
h->ordered = (fio_hash_data_ordered_s *)(FIO_HASH_REALLOC(
|
509
|
+
h->ordered, ((h->capa >> 1) * sizeof(*h->ordered)),
|
510
|
+
((h->capa) * sizeof(*h->ordered)), ((h->pos) * sizeof(*h->ordered))));
|
511
|
+
if (!h->ordered) {
|
512
|
+
perror("HashMap Reallocation Failed");
|
513
|
+
exit(errno);
|
514
|
+
}
|
515
|
+
}
|
516
|
+
if (!h->count) {
|
517
|
+
/* empty hash */
|
518
|
+
return;
|
519
|
+
|
520
|
+
} else if (h->pos == h->count) {
|
521
|
+
/* the ordered list is fully occupied, no need to rearange. */
|
522
|
+
FIO_HASH_FOR_LOOP(h, i) {
|
523
|
+
/* can't use fio_hash_insert, because we're recycling containers */
|
524
|
+
fio_hash_data_s *place = fio_hash_seek_pos_(h, i->key);
|
525
|
+
if (!place) {
|
526
|
+
goto retry_rehashing;
|
527
|
+
}
|
528
|
+
*place = (fio_hash_data_s){.key = i->key, .obj = i};
|
529
|
+
}
|
530
|
+
|
531
|
+
} else {
|
532
|
+
/* the ordered list has holes, fill 'em up.*/
|
533
|
+
size_t reader = 0;
|
534
|
+
size_t writer = 0;
|
535
|
+
while (reader < h->pos) {
|
536
|
+
if (h->ordered[reader].obj) {
|
537
|
+
fio_hash_data_s *place = fio_hash_seek_pos_(h, h->ordered[reader].key);
|
538
|
+
if (!place) {
|
539
|
+
goto retry_rehashing;
|
540
|
+
}
|
541
|
+
*place = (fio_hash_data_s){.key = h->ordered[reader].key,
|
542
|
+
.obj = h->ordered + writer};
|
543
|
+
fio_hash_data_ordered_s old = h->ordered[reader];
|
544
|
+
h->ordered[reader] =
|
545
|
+
(fio_hash_data_ordered_s){.key = FIO_HASH_KEY_INVALID, .obj = NULL};
|
546
|
+
h->ordered[writer] = old;
|
547
|
+
++writer;
|
548
|
+
} else {
|
549
|
+
FIO_HASH_KEY_DESTROY(h->ordered[reader].key);
|
550
|
+
h->ordered[reader].key = FIO_HASH_KEY_INVALID;
|
551
|
+
}
|
552
|
+
++reader;
|
553
|
+
}
|
554
|
+
h->pos = writer;
|
555
|
+
// h->ordered[h->pos] =
|
556
|
+
// (fio_hash_data_ordered_s){.key = FIO_HASH_KEY_INVALID, .obj = NULL};
|
557
|
+
}
|
558
|
+
}
|
559
|
+
|
560
|
+
FIO_FUNC inline size_t fio_hash_each(fio_hash_s *hash, size_t start_at,
|
561
|
+
int (*task)(FIO_HASH_KEY_TYPE key,
|
562
|
+
void *obj, void *arg),
|
563
|
+
void *arg) {
|
564
|
+
if (start_at >= hash->count)
|
565
|
+
return hash->count;
|
566
|
+
size_t count = 0;
|
567
|
+
if (hash->pos == hash->count) {
|
568
|
+
count = start_at;
|
569
|
+
while (count < hash->pos) {
|
570
|
+
/* no "holes" in the hash. */
|
571
|
+
++count;
|
572
|
+
if (task(hash->ordered[count - 1].key,
|
573
|
+
(void *)hash->ordered[count - 1].obj, arg) == -1)
|
574
|
+
return count;
|
575
|
+
}
|
576
|
+
} else {
|
577
|
+
size_t pos = 0;
|
578
|
+
while (count < start_at && pos < hash->pos) {
|
579
|
+
if (hash->ordered[pos].obj) {
|
580
|
+
++count;
|
581
|
+
}
|
582
|
+
++pos;
|
583
|
+
}
|
584
|
+
while (pos < hash->pos) {
|
585
|
+
if (hash->ordered[pos].obj) {
|
586
|
+
++count;
|
587
|
+
if (task(hash->ordered[pos].key, (void *)hash->ordered[pos].obj, arg) ==
|
588
|
+
-1)
|
589
|
+
return count;
|
590
|
+
}
|
591
|
+
++pos;
|
592
|
+
}
|
593
|
+
}
|
594
|
+
return count;
|
595
|
+
}
|
596
|
+
|
597
|
+
/** Returns the number of elements in the Hash. */
|
598
|
+
FIO_FUNC inline size_t fio_hash_count(const fio_hash_s *hash) {
|
599
|
+
if (!hash)
|
600
|
+
return 0;
|
601
|
+
return hash->count;
|
602
|
+
}
|
603
|
+
|
604
|
+
/**
|
605
|
+
* Returns a temporary theoretical Hash map capacity.
|
606
|
+
* This could be used for testig performance and memory consumption.
|
607
|
+
*/
|
608
|
+
FIO_FUNC inline size_t fio_hash_capa(const fio_hash_s *hash) {
|
609
|
+
if (!hash)
|
610
|
+
return 0;
|
611
|
+
return hash->capa;
|
612
|
+
}
|
613
|
+
|
614
|
+
/**
|
615
|
+
* Attempts to minimize memory usage by removing empty spaces caused by deleted
|
616
|
+
* items and rehashing the Hash Map.
|
617
|
+
*
|
618
|
+
* Returns the updated hash map capacity.
|
619
|
+
*/
|
620
|
+
FIO_FUNC inline size_t fio_hash_compact(fio_hash_s *hash) {
|
621
|
+
if (!hash)
|
622
|
+
return 0;
|
623
|
+
if (hash->count == hash->pos && (hash->count << 1) >= hash->capa)
|
624
|
+
return hash->capa;
|
625
|
+
/* compact ordered list */
|
626
|
+
{
|
627
|
+
size_t reader = 0;
|
628
|
+
size_t writer = 0;
|
629
|
+
while (reader < hash->pos) {
|
630
|
+
if (hash->ordered[reader].obj) {
|
631
|
+
hash->ordered[writer] = hash->ordered[reader];
|
632
|
+
++writer;
|
633
|
+
} else {
|
634
|
+
FIO_HASH_KEY_DESTROY(hash->ordered[reader].key);
|
635
|
+
}
|
636
|
+
++reader;
|
637
|
+
}
|
638
|
+
hash->pos = writer;
|
639
|
+
}
|
640
|
+
/* recalculate minimal length and rehash */
|
641
|
+
while (hash->mask && hash->mask >= hash->count)
|
642
|
+
hash->mask = hash->mask >> 1;
|
643
|
+
if (hash->mask + 1 < FIO_HASH_INITIAL_CAPACITY)
|
644
|
+
hash->mask = (FIO_HASH_INITIAL_CAPACITY - 1);
|
645
|
+
while (hash->count >= hash->mask)
|
646
|
+
hash->mask = (hash->mask << 1) | 1;
|
647
|
+
fio_hash_rehash(hash);
|
648
|
+
|
649
|
+
return hash->capa;
|
650
|
+
}
|
651
|
+
|
652
|
+
#if DEBUG && !FIO_HASH_NO_TEST
|
653
|
+
#define FIO_HASHMAP_TEXT_COUNT 524288UL
|
654
|
+
#include <stdio.h>
|
655
|
+
FIO_FUNC void fio_hash_test(void) {
|
656
|
+
#define TEST_ASSERT(cond, ...) \
|
657
|
+
if (!(cond)) { \
|
658
|
+
fprintf(stderr, "* " __VA_ARGS__); \
|
659
|
+
fprintf(stderr, "Testing failed.\n"); \
|
660
|
+
exit(-1); \
|
661
|
+
}
|
662
|
+
fio_hash_s h = {.capa = 0};
|
663
|
+
fprintf(stderr, "=== Testing Core HashMap (fio_hashmap.h)\n");
|
664
|
+
fprintf(stderr, "* Inserting %lu items\n", FIO_HASHMAP_TEXT_COUNT);
|
665
|
+
for (unsigned long i = 1; i < FIO_HASHMAP_TEXT_COUNT; ++i) {
|
666
|
+
fio_hash_insert(&h, i, (void *)i);
|
667
|
+
TEST_ASSERT((i == (uintptr_t)fio_hash_find(&h, i)), "insertion != find");
|
668
|
+
}
|
669
|
+
fprintf(stderr, "* Seeking %lu items\n", FIO_HASHMAP_TEXT_COUNT);
|
670
|
+
for (unsigned long i = 1; i < FIO_HASHMAP_TEXT_COUNT; ++i) {
|
671
|
+
TEST_ASSERT((i == (uintptr_t)fio_hash_find(&h, i)), "insertion != find");
|
672
|
+
}
|
673
|
+
{
|
674
|
+
fprintf(stderr, "* Testing order for %lu items\n", FIO_HASHMAP_TEXT_COUNT);
|
675
|
+
uintptr_t i = 1;
|
676
|
+
FIO_HASH_FOR_LOOP(&h, pos) {
|
677
|
+
TEST_ASSERT(pos->key == (uintptr_t)pos->obj, "Key and value mismatch.");
|
678
|
+
TEST_ASSERT(pos->key == i, "Key out of order %lu != %lu.",
|
679
|
+
(unsigned long)i, (unsigned long)pos->key);
|
680
|
+
++i;
|
681
|
+
}
|
682
|
+
}
|
683
|
+
fprintf(stderr, "* Removing odd items from %lu items\n",
|
684
|
+
FIO_HASHMAP_TEXT_COUNT);
|
685
|
+
for (unsigned long i = 1; i < FIO_HASHMAP_TEXT_COUNT; i += 2) {
|
686
|
+
uintptr_t old = (uintptr_t)fio_hash_insert(&h, i, NULL);
|
687
|
+
TEST_ASSERT(old == i, "Removal didn't return old value.");
|
688
|
+
TEST_ASSERT(!(fio_hash_find(&h, i)), "Removal failed (still exists).");
|
689
|
+
}
|
690
|
+
if (1) {
|
691
|
+
size_t count = h.count;
|
692
|
+
size_t pos = h.pos;
|
693
|
+
fio_hash_insert(&h, 1, (void *)1);
|
694
|
+
TEST_ASSERT(
|
695
|
+
count + 1 == h.count,
|
696
|
+
"Readding a removed item should increase count by 1 (%zu + 1 != %zu).",
|
697
|
+
count, (size_t)h.count);
|
698
|
+
TEST_ASSERT(
|
699
|
+
pos == h.pos,
|
700
|
+
"Readding a removed item shouldn't change the position marker!");
|
701
|
+
TEST_ASSERT(fio_hash_find(&h, 1) == (void *)1,
|
702
|
+
"Readding a removed item should update the item (%p != 1)!",
|
703
|
+
fio_hash_find(&h, 1));
|
704
|
+
fio_hash_insert(&h, 1, NULL);
|
705
|
+
TEST_ASSERT(count == h.count,
|
706
|
+
"Re-removing an item should decrease count (%zu != %zu).",
|
707
|
+
count, (size_t)h.count);
|
708
|
+
TEST_ASSERT(pos == h.pos,
|
709
|
+
"Re-removing an item shouldn't effect the position marker!");
|
710
|
+
TEST_ASSERT(!fio_hash_find(&h, 1),
|
711
|
+
"Re-removing a re-added item should update the item!");
|
712
|
+
}
|
713
|
+
{
|
714
|
+
fprintf(stderr, "* Testing for %lu / 2 holes\n", FIO_HASHMAP_TEXT_COUNT);
|
715
|
+
uintptr_t i = 1;
|
716
|
+
FIO_HASH_FOR_LOOP(&h, pos) {
|
717
|
+
if (pos->obj) {
|
718
|
+
TEST_ASSERT(pos->key == (uintptr_t)pos->obj, "Key and value mismatch.");
|
719
|
+
TEST_ASSERT(pos->key == i, "Key out of order %lu != %lu.",
|
720
|
+
(unsigned long)i, (unsigned long)pos->key);
|
721
|
+
} else {
|
722
|
+
TEST_ASSERT(pos->obj == NULL, "old value detected.");
|
723
|
+
TEST_ASSERT(pos->key == i, "Key out of order.");
|
724
|
+
}
|
725
|
+
++i;
|
726
|
+
}
|
727
|
+
}
|
728
|
+
{
|
729
|
+
fprintf(stderr, "* Poping two elements (testing pop through holes)\n");
|
730
|
+
FIO_HASH_KEY_TYPE k;
|
731
|
+
TEST_ASSERT(fio_hash_pop(&h, &k), "Pop 1 failed to collect object");
|
732
|
+
TEST_ASSERT(k, "Pop 1 failed to collect key");
|
733
|
+
FIO_HASH_KEY_DESTROY(k);
|
734
|
+
TEST_ASSERT(fio_hash_pop(&h, &k), "Pop 2 failed to collect object");
|
735
|
+
TEST_ASSERT(k, "Pop 2 failed to collect key");
|
736
|
+
FIO_HASH_KEY_DESTROY(k);
|
737
|
+
}
|
738
|
+
fprintf(stderr, "* Compacting Hash to %lu\n", FIO_HASHMAP_TEXT_COUNT >> 1);
|
739
|
+
fio_hash_compact(&h);
|
740
|
+
{
|
741
|
+
fprintf(stderr, "* Testing that %lu items are continues\n",
|
742
|
+
FIO_HASHMAP_TEXT_COUNT >> 1);
|
743
|
+
uintptr_t i = 0;
|
744
|
+
FIO_HASH_FOR_LOOP(&h, pos) {
|
745
|
+
TEST_ASSERT(pos->obj, "Found a hole after compact.");
|
746
|
+
TEST_ASSERT(pos->key == (uintptr_t)pos->obj, "Key and value mismatch.");
|
747
|
+
++i;
|
748
|
+
}
|
749
|
+
TEST_ASSERT(i == h.count, "count error (%lu != %lu).", i, h.count);
|
750
|
+
}
|
751
|
+
fio_hash_free(&h);
|
752
|
+
fprintf(stderr, "* passed... without testing that FIO_HASH_KEY_DESTROY is "
|
753
|
+
"called only once.\n");
|
754
|
+
}
|
755
|
+
#endif /* DEBUG Testing */
|
756
|
+
|
757
|
+
#undef FIO_FUNC
|
758
|
+
|
759
|
+
#endif /* H_FIO_SIMPLE_HASH_H */
|