iodine 0.7.1 → 0.7.2

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.

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