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.

Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -2
  3. data/CHANGELOG.md +22 -0
  4. data/LIMITS.md +19 -9
  5. data/README.md +92 -77
  6. data/SPEC-PubSub-Draft.md +113 -0
  7. data/SPEC-Websocket-Draft.md +127 -143
  8. data/bin/http-hello +0 -1
  9. data/bin/raw-rbhttp +1 -1
  10. data/bin/raw_broadcast +8 -10
  11. data/bin/updated api +2 -2
  12. data/bin/ws-broadcast +2 -4
  13. data/bin/ws-echo +2 -2
  14. data/examples/config.ru +13 -13
  15. data/examples/echo.ru +5 -6
  16. data/examples/hello.ru +2 -3
  17. data/examples/info.md +316 -0
  18. data/examples/pubsub_engine.ru +81 -0
  19. data/examples/redis.ru +9 -9
  20. data/examples/shootout.ru +45 -11
  21. data/ext/iodine/defer.c +194 -297
  22. data/ext/iodine/defer.h +61 -53
  23. data/ext/iodine/evio.c +0 -260
  24. data/ext/iodine/evio.h +50 -22
  25. data/ext/iodine/evio_callbacks.c +26 -0
  26. data/ext/iodine/evio_epoll.c +251 -0
  27. data/ext/iodine/evio_kqueue.c +193 -0
  28. data/ext/iodine/extconf.rb +1 -1
  29. data/ext/iodine/facil.c +1420 -542
  30. data/ext/iodine/facil.h +151 -64
  31. data/ext/iodine/fio_ary.h +418 -0
  32. data/ext/iodine/{base64.c → fio_base64.c} +33 -24
  33. data/ext/iodine/{base64.h → fio_base64.h} +6 -7
  34. data/ext/iodine/{fio_cli_helper.c → fio_cli.c} +77 -58
  35. data/ext/iodine/{fio_cli_helper.h → fio_cli.h} +9 -4
  36. data/ext/iodine/fio_hashmap.h +759 -0
  37. data/ext/iodine/fio_json_parser.h +651 -0
  38. data/ext/iodine/fio_llist.h +257 -0
  39. data/ext/iodine/fio_mem.c +672 -0
  40. data/ext/iodine/fio_mem.h +140 -0
  41. data/ext/iodine/fio_random.c +248 -0
  42. data/ext/iodine/{random.h → fio_random.h} +11 -14
  43. data/ext/iodine/{sha1.c → fio_sha1.c} +28 -24
  44. data/ext/iodine/{sha1.h → fio_sha1.h} +38 -16
  45. data/ext/iodine/{sha2.c → fio_sha2.c} +66 -49
  46. data/ext/iodine/{sha2.h → fio_sha2.h} +57 -26
  47. data/ext/iodine/{fiobj_internal.c → fio_siphash.c} +9 -90
  48. data/ext/iodine/fio_siphash.h +18 -0
  49. data/ext/iodine/fio_tmpfile.h +38 -0
  50. data/ext/iodine/fiobj.h +24 -7
  51. data/ext/iodine/fiobj4sock.h +23 -0
  52. data/ext/iodine/fiobj_ary.c +143 -226
  53. data/ext/iodine/fiobj_ary.h +17 -16
  54. data/ext/iodine/fiobj_data.c +1160 -0
  55. data/ext/iodine/fiobj_data.h +164 -0
  56. data/ext/iodine/fiobj_hash.c +298 -406
  57. data/ext/iodine/fiobj_hash.h +101 -54
  58. data/ext/iodine/fiobj_json.c +478 -601
  59. data/ext/iodine/fiobj_json.h +34 -9
  60. data/ext/iodine/fiobj_numbers.c +383 -51
  61. data/ext/iodine/fiobj_numbers.h +87 -11
  62. data/ext/iodine/fiobj_str.c +423 -184
  63. data/ext/iodine/fiobj_str.h +81 -32
  64. data/ext/iodine/fiobject.c +273 -522
  65. data/ext/iodine/fiobject.h +477 -112
  66. data/ext/iodine/http.c +2243 -83
  67. data/ext/iodine/http.h +842 -121
  68. data/ext/iodine/http1.c +810 -385
  69. data/ext/iodine/http1.h +16 -39
  70. data/ext/iodine/http1_parser.c +146 -74
  71. data/ext/iodine/http1_parser.h +15 -4
  72. data/ext/iodine/http_internal.c +1258 -0
  73. data/ext/iodine/http_internal.h +226 -0
  74. data/ext/iodine/http_mime_parser.h +341 -0
  75. data/ext/iodine/iodine.c +86 -68
  76. data/ext/iodine/iodine.h +26 -11
  77. data/ext/iodine/iodine_helpers.c +8 -7
  78. data/ext/iodine/iodine_http.c +487 -324
  79. data/ext/iodine/iodine_json.c +304 -0
  80. data/ext/iodine/iodine_json.h +6 -0
  81. data/ext/iodine/iodine_protocol.c +107 -45
  82. data/ext/iodine/iodine_pubsub.c +526 -225
  83. data/ext/iodine/iodine_pubsub.h +10 -0
  84. data/ext/iodine/iodine_websockets.c +268 -510
  85. data/ext/iodine/iodine_websockets.h +2 -4
  86. data/ext/iodine/pubsub.c +726 -432
  87. data/ext/iodine/pubsub.h +85 -103
  88. data/ext/iodine/rb-call.c +4 -4
  89. data/ext/iodine/rb-defer.c +46 -22
  90. data/ext/iodine/rb-fiobj2rb.h +117 -0
  91. data/ext/iodine/rb-rack-io.c +73 -238
  92. data/ext/iodine/rb-rack-io.h +2 -2
  93. data/ext/iodine/rb-registry.c +35 -93
  94. data/ext/iodine/rb-registry.h +1 -0
  95. data/ext/iodine/redis_engine.c +742 -304
  96. data/ext/iodine/redis_engine.h +42 -39
  97. data/ext/iodine/resp_parser.h +311 -0
  98. data/ext/iodine/sock.c +627 -490
  99. data/ext/iodine/sock.h +345 -297
  100. data/ext/iodine/spnlock.inc +15 -4
  101. data/ext/iodine/websocket_parser.h +16 -20
  102. data/ext/iodine/websockets.c +188 -257
  103. data/ext/iodine/websockets.h +24 -133
  104. data/lib/iodine.rb +52 -7
  105. data/lib/iodine/cli.rb +6 -24
  106. data/lib/iodine/json.rb +40 -0
  107. data/lib/iodine/version.rb +1 -1
  108. data/lib/iodine/websocket.rb +5 -3
  109. data/lib/rack/handler/iodine.rb +58 -13
  110. metadata +38 -48
  111. data/bin/ws-shootout +0 -107
  112. data/examples/broadcast.ru +0 -56
  113. data/ext/iodine/bscrypt-common.h +0 -116
  114. data/ext/iodine/bscrypt.h +0 -49
  115. data/ext/iodine/fio2resp.c +0 -60
  116. data/ext/iodine/fio2resp.h +0 -51
  117. data/ext/iodine/fio_dict.c +0 -446
  118. data/ext/iodine/fio_dict.h +0 -99
  119. data/ext/iodine/fio_hash_table.h +0 -370
  120. data/ext/iodine/fio_list.h +0 -111
  121. data/ext/iodine/fiobj_internal.h +0 -280
  122. data/ext/iodine/fiobj_primitives.c +0 -131
  123. data/ext/iodine/fiobj_primitives.h +0 -55
  124. data/ext/iodine/fiobj_sym.c +0 -135
  125. data/ext/iodine/fiobj_sym.h +0 -60
  126. data/ext/iodine/hex.c +0 -124
  127. data/ext/iodine/hex.h +0 -70
  128. data/ext/iodine/http1_request.c +0 -81
  129. data/ext/iodine/http1_request.h +0 -58
  130. data/ext/iodine/http1_response.c +0 -417
  131. data/ext/iodine/http1_response.h +0 -95
  132. data/ext/iodine/http_request.c +0 -111
  133. data/ext/iodine/http_request.h +0 -102
  134. data/ext/iodine/http_response.c +0 -1703
  135. data/ext/iodine/http_response.h +0 -250
  136. data/ext/iodine/misc.c +0 -182
  137. data/ext/iodine/misc.h +0 -74
  138. data/ext/iodine/random.c +0 -208
  139. data/ext/iodine/redis_connection.c +0 -278
  140. data/ext/iodine/redis_connection.h +0 -86
  141. data/ext/iodine/resp.c +0 -842
  142. data/ext/iodine/resp.h +0 -261
  143. data/ext/iodine/siphash.c +0 -154
  144. data/ext/iodine/siphash.h +0 -22
  145. data/ext/iodine/xor-crypt.c +0 -193
  146. 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
@@ -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 "fiobj_hash.h"
7
- #include "fiobj_internal.h"
6
+ #include "fiobject.h"
8
7
 
9
- #include <errno.h>
8
+ #define FIO_OVERRIDE_MALLOC 1
9
+ #include "fio_mem.h"
10
10
 
11
- /* *****************************************************************************
12
- Hash types
13
- ***************************************************************************** */
14
- /* MUST be a power of 2 */
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
- typedef struct {
18
- uintptr_t hash;
19
- fio_ls_s *container;
20
- } map_info_s;
16
+ #include "fiobj_hash.h"
21
17
 
22
- typedef struct {
23
- uintptr_t capa;
24
- map_info_s *data;
25
- } fio_map_s;
18
+ #include <assert.h>
26
19
 
27
20
  typedef struct {
28
- struct fiobj_vtable_s *vtable;
29
- uintptr_t count;
30
- uintptr_t mask;
31
- fio_ls_s items;
32
- fio_map_s map;
33
- } fiobj_hash_s;
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
- /* Hash node */
36
- typedef struct {
37
- struct fiobj_vtable_s *vtable;
38
- fiobj_s *name;
39
- fiobj_s *obj;
40
- } fiobj_couplet_s;
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
- void fiobj_hash_rehash(fiobj_s *h);
41
+ #include "fio_hashmap.h"
43
42
 
44
- #define obj2hash(o) ((fiobj_hash_s *)(o))
45
- #define obj2couplet(o) ((fiobj_couplet_s *)(o))
43
+ #include <errno.h>
46
44
 
47
45
  /* *****************************************************************************
48
- Internal Map Array
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
- static inline void fio_map_reset(fio_map_s *map, uintptr_t capa) {
53
- /* It's better to reallocate using calloc than manually zero out memory */
54
- /* Maybe there's enough zeroed out pages available in the system */
55
- map->capa = capa;
56
- free(map->data);
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
- Internal HashMap
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
- /* seeks the hash's position in the map */
71
- static map_info_s *fio_hash_seek(fiobj_hash_s *h, uintptr_t hash) {
72
- /* TODO: consider implementing Robing Hood reordering during seek? */
73
- map_info_s *pos = h->map.data + (hash & h->mask);
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
- /* attempts to rehash the hashmap. */
149
- void fiobj_hash_rehash(fiobj_s *h_) {
150
- fiobj_hash_s *h = obj2hash(h_);
151
- // fprintf(stderr,
152
- // "- Rehash with "
153
- // "length/capacity == %lu/%lu\n",
154
- // h->count, h->map.capa);
155
- retry_rehashing:
156
- h->mask = ((h->mask) << 1) | 1;
157
- fio_map_reset(&h->map, h->mask + 1);
158
- fio_ls_s *pos = h->items.next;
159
- while (pos != &h->items) {
160
- /* can't use fio_hash_insert, because we're recycling containers */
161
- uintptr_t pos_hash = fiobj_sym_id(obj2couplet(pos->obj)->name);
162
- map_info_s *info = fio_hash_seek(h, pos_hash);
163
- if (!info) {
164
- goto retry_rehashing;
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 uintptr_t FIOBJ_T_COUPLET;
176
-
177
- static void fiobj_couplet_dealloc(fiobj_s *o) {
178
- if (OBJREF_REM(obj2couplet(o)->name) == 0)
179
- OBJVTBL(obj2couplet(o)->name)->free(obj2couplet(o)->name);
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
- static size_t fiobj_couplet_each1(fiobj_s *o, size_t start_at,
184
- int (*task)(fiobj_s *obj, void *arg),
185
- void *arg) {
186
- if (obj2couplet(o)->obj == NULL)
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
- static int fiobj_coup_is_eq(const fiobj_s *self, const fiobj_s *other) {
130
+ intptr_t fiobj_hash2num(const FIOBJ o) { return (intptr_t)fiobj_hash_count(o); }
193
131
 
194
- if (other->type != FIOBJ_T_COUPLET)
195
- return 0;
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
- /** Returns the number of elements in the Array. */
205
- static size_t fiobj_couplet_count_items(const fiobj_s *o) {
206
- if (obj2couplet(o)->obj == NULL)
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
- fiobj_s *fiobj_couplet2obj(const fiobj_s *obj);
212
-
213
- static struct fiobj_vtable_s FIOBJ_VTABLE_COUPLET = {
214
- .free = fiobj_couplet_dealloc,
215
- .to_i = fiobj_noop_i,
216
- .to_f = fiobj_noop_f,
217
- .to_str = fiobj_noop_str,
218
- .is_eq = fiobj_coup_is_eq,
219
- .count = fiobj_couplet_count_items,
220
- .unwrap = fiobj_couplet2obj,
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
- const uintptr_t FIOBJ_T_COUPLET = (uintptr_t)(&FIOBJ_VTABLE_COUPLET);
225
-
226
- static inline fiobj_s *fiobj_couplet_alloc(void *sym, void *obj) {
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
- * If object is a Hash couplet (occurs in `fiobj_each2`), returns the key
237
- * (Symbol) from the key-value pair.
157
+ * Creates a mutable empty Hash object. Use `fiobj_free` when done.
238
158
  *
239
- * Otherwise returns NULL.
159
+ * Notice that these Hash objects are designed for smaller collections and
160
+ * retain order of object insertion.
240
161
  */
241
- fiobj_s *fiobj_couplet2key(const fiobj_s *obj) {
242
- if (!obj || obj->type != FIOBJ_T_COUPLET)
243
- return NULL;
244
- return obj2couplet(obj)->name;
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
- * If object is a Hash couplet (occurs in `fiobj_each2`), returns the object
249
- * (the value) from the key-value pair.
174
+ * Creates a mutable empty Hash object with an initial capacity of `capa`. Use
175
+ * `fiobj_free` when done.
250
176
  *
251
- * Otherwise returns NULL.
177
+ * Notice that these Hash objects are designed for smaller collections and
178
+ * retain order of object insertion.
252
179
  */
253
- fiobj_s *fiobj_couplet2obj(const fiobj_s *obj) {
254
- if (!obj || obj->type != FIOBJ_T_COUPLET)
255
- return (fiobj_s *)obj;
256
- return obj2couplet(obj)->obj;
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
- return i;
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
- static int fiobj_hash_is_eq(const fiobj_s *self, const fiobj_s *other) {
295
- if (other->type != FIOBJ_T_HASH)
296
- return 0;
297
- if (obj2hash(self)->count != obj2hash(other)->count)
298
- return 0;
299
- // fio_ls_s *pos = obj2hash(self)->items.next;
300
- // while (pos != &obj2hash(self)->items) {
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
- * Creates a mutable empty Hash object. Use `fiobj_free` when done.
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
- * Notice that these Hash objects are designed for smaller collections and
335
- * retain order of object insertion.
204
+ * Returns -1 on error.
336
205
  */
337
- fiobj_s *fiobj_hash_new(void) {
338
- fiobj_s *o = fiobj_alloc(sizeof(fiobj_hash_s));
339
- if (!o)
340
- perror("ERROR: fiobj hash couldn't allocate memory"), exit(errno);
341
- *obj2hash(o) = (fiobj_hash_s){
342
- .vtable = &FIOBJ_VTABLE_HASH,
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
- /** Returns the number of elements in the Hash. */
354
- size_t fiobj_hash_count(const fiobj_s *hash) {
355
- if (!hash || hash->type != FIOBJ_T_HASH)
356
- return 0;
357
- return obj2hash(hash)->count;
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
- * Sets a key-value pair in the Hash, duplicating the Symbol and **moving**
362
- * the ownership of the object to the Hash.
238
+ * Replaces the value in a key-value pair, returning the old value (and it's
239
+ * ownership) to the caller.
363
240
  *
364
- * Returns -1 on error.
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
- int fiobj_hash_set(fiobj_s *hash, fiobj_s *sym, fiobj_s *obj) {
367
- if (hash->type != FIOBJ_T_HASH) {
368
- fiobj_free(obj);
369
- return -1;
370
- }
371
- uintptr_t hash_value = 0;
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
- fiobj_s *coup = fiobj_couplet_alloc(sym, obj);
383
- fiobj_s *old = fio_hash_insert(obj2hash(hash), hash_value, coup);
384
- while (old == (void *)-1) {
385
- fiobj_hash_rehash(hash);
386
- old = fio_hash_insert(obj2hash(hash), hash_value, coup);
387
- // fprintf(stderr, "WARN: (fiobj Hash) collision limit reached"
388
- // " - forced rehashing\n");
389
- }
390
- if (old) {
391
- fiobj_free(obj2couplet(old)->obj);
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
- fiobj_s *fiobj_hash_remove(fiobj_s *hash, fiobj_s *sym) {
403
- if (hash->type != FIOBJ_T_HASH) {
404
- return 0;
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(fiobj_s *hash, fiobj_s *sym) {
432
- fiobj_s *obj = fiobj_hash_remove(hash, sym);
433
- if (!obj)
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(obj);
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
- fiobj_s *fiobj_hash_get(const fiobj_s *hash, fiobj_s *sym) {
444
- if (hash->type != FIOBJ_T_HASH) {
445
- return 0;
446
- }
447
- uintptr_t hash_value = 0;
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 with the Symbol C string.
319
+ * Returns a temporary handle to the object associated hashed key value.
464
320
  *
465
- * This function takes a C string instead of a Symbol, which is slower if a
466
- * Symbol can be cached but faster if a Symbol must be created.
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 String data.
324
+ * Returns NULL if no object is asociated with this hashed key value.
469
325
  */
470
- fiobj_s *fiobj_hash_get2(const fiobj_s *hash, const char *str, size_t len) {
471
- if (hash->type != FIOBJ_T_HASH || str == NULL) {
472
- return NULL;
473
- }
474
- uintptr_t hashed_sym = fiobj_sym_hash(str, len);
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 fiobj_s *hash, fiobj_s *sym) {
485
- if (hash->type != FIOBJ_T_HASH) {
486
- return 0;
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
- * Returns a temporary theoretical Hash map capacity.
505
- * This could be used for testig performance and memory consumption.
343
+ * Empties the Hash.
506
344
  */
507
- size_t fiobj_hash_capa(const fiobj_s *hash) { return obj2hash(hash)->map.capa; }
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