isomorfeus-iodine 0.7.44

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