iodine 0.3.6 → 0.4.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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -0
  3. data/LIMITS.md +25 -0
  4. data/README.md +39 -80
  5. data/SPEC-Websocket-Draft.md +129 -4
  6. data/bin/echo +2 -2
  7. data/bin/http-hello +1 -0
  8. data/bin/updated api +113 -0
  9. data/bin/ws-echo +0 -1
  10. data/examples/broadcast.ru +56 -0
  11. data/examples/echo.ru +57 -0
  12. data/examples/hello.ru +30 -0
  13. data/examples/redis.ru +69 -0
  14. data/examples/shootout.ru +53 -0
  15. data/exe/iodine +2 -80
  16. data/ext/iodine/defer.c +11 -5
  17. data/ext/iodine/empty.h +26 -0
  18. data/ext/iodine/evio.h +1 -1
  19. data/ext/iodine/facil.c +103 -61
  20. data/ext/iodine/facil.h +20 -12
  21. data/ext/iodine/fio_dict.c +446 -0
  22. data/ext/iodine/fio_dict.h +90 -0
  23. data/ext/iodine/fio_hash_table.h +370 -0
  24. data/ext/iodine/fio_list.h +30 -3
  25. data/ext/iodine/http.c +169 -37
  26. data/ext/iodine/http.h +33 -10
  27. data/ext/iodine/http1.c +78 -42
  28. data/ext/iodine/http_request.c +6 -0
  29. data/ext/iodine/http_request.h +3 -0
  30. data/ext/iodine/http_response.c +43 -11
  31. data/ext/iodine/iodine.c +380 -0
  32. data/ext/iodine/iodine.h +62 -0
  33. data/ext/iodine/iodine_helpers.c +235 -0
  34. data/ext/iodine/iodine_helpers.h +13 -0
  35. data/ext/iodine/iodine_http.c +409 -241
  36. data/ext/iodine/iodine_http.h +7 -14
  37. data/ext/iodine/iodine_protocol.c +626 -0
  38. data/ext/iodine/iodine_protocol.h +13 -0
  39. data/ext/iodine/iodine_pubsub.c +646 -0
  40. data/ext/iodine/iodine_pubsub.h +27 -0
  41. data/ext/iodine/iodine_websockets.c +796 -0
  42. data/ext/iodine/iodine_websockets.h +19 -0
  43. data/ext/iodine/pubsub.c +544 -0
  44. data/ext/iodine/pubsub.h +215 -0
  45. data/ext/iodine/random.c +4 -4
  46. data/ext/iodine/rb-call.c +1 -5
  47. data/ext/iodine/rb-defer.c +3 -20
  48. data/ext/iodine/rb-rack-io.c +22 -22
  49. data/ext/iodine/rb-rack-io.h +3 -4
  50. data/ext/iodine/rb-registry.c +111 -118
  51. data/ext/iodine/redis_connection.c +277 -0
  52. data/ext/iodine/redis_connection.h +77 -0
  53. data/ext/iodine/redis_engine.c +398 -0
  54. data/ext/iodine/redis_engine.h +68 -0
  55. data/ext/iodine/resp.c +842 -0
  56. data/ext/iodine/resp.h +253 -0
  57. data/ext/iodine/sock.c +26 -12
  58. data/ext/iodine/sock.h +14 -3
  59. data/ext/iodine/spnlock.inc +19 -2
  60. data/ext/iodine/websockets.c +299 -11
  61. data/ext/iodine/websockets.h +159 -6
  62. data/lib/iodine.rb +104 -1
  63. data/lib/iodine/cli.rb +106 -0
  64. data/lib/iodine/monkeypatch.rb +40 -0
  65. data/lib/iodine/pubsub.rb +70 -0
  66. data/lib/iodine/version.rb +1 -1
  67. data/lib/iodine/websocket.rb +12 -0
  68. data/lib/rack/handler/iodine.rb +33 -7
  69. metadata +35 -7
  70. data/ext/iodine/iodine_core.c +0 -760
  71. data/ext/iodine/iodine_core.h +0 -79
  72. data/ext/iodine/iodine_websocket.c +0 -551
  73. data/ext/iodine/iodine_websocket.h +0 -22
  74. data/lib/iodine/http.rb +0 -4
@@ -0,0 +1,90 @@
1
+ /*
2
+ Copyright: Boaz segev, 2016-2017
3
+ License: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+ #ifndef H_FIO_DICT_H
8
+ /**
9
+ `fio_dict_s` Is based on a 4-bit trie structure, allowing for fast no-collisions
10
+ key-value matching while avoiding any hashing.
11
+
12
+ It's memory intensive... very memory intensive... but it has 0 collision risk
13
+ and offers fairly high performance.
14
+
15
+ Just to offer some insight, a single key-value pair for the key "hello" will
16
+ require ~1,360 bytes. Add the key "bye!" ad you'll add ~1,088 bytes more... but
17
+ the key "hello1" will cost only 272 bytes... brrr.
18
+ */
19
+ #define H_FIO_DICT_H
20
+
21
+ #include <stdint.h>
22
+ #include <stdlib.h>
23
+
24
+ typedef struct fio_dict_s {
25
+ struct fio_dict_s *parent;
26
+ struct fio_dict_s *trie[16];
27
+ unsigned used : 1;
28
+ unsigned trie_val : 4;
29
+ } fio_dict_s;
30
+
31
+ #define FIO_DICT_INIT \
32
+ (fio_dict_s) { .parent = NULL }
33
+ #define FIO_DICT_INIT_STATIC \
34
+ { .parent = NULL }
35
+
36
+ #ifndef fio_node2obj
37
+ #define fio_node2obj(type, member, ptr) \
38
+ ((type *)((uintptr_t)(ptr) - (uintptr_t)(&(((type *)0)->member))))
39
+ #endif
40
+
41
+ /* *****************************************************************************
42
+ API
43
+ ***************************************************************************** */
44
+
45
+ /** Returns the `fio_dict_s *` object associated with the key, NULL if none. */
46
+ fio_dict_s *fio_dict_get(fio_dict_s *dict, void *key, size_t key_len);
47
+
48
+ /** Returns the old `fio_dict_s *` object associated with the key, if any.*/
49
+ fio_dict_s *fio_dict_set(fio_dict_s *dict, void *key, size_t key_len,
50
+ fio_dict_s *node);
51
+
52
+ /** Removes and returns the specified `fio_dict_s *` object.*/
53
+ fio_dict_s *fio_dict_remove(fio_dict_s *node);
54
+
55
+ /** Returns a `fio_dict_s *` dictionary (or NULL) of all `prefix` children. */
56
+ fio_dict_s *fio_dict_step(fio_dict_s *dict, uint8_t prefix);
57
+
58
+ /** Returns a `fio_dict_s *` dictionary (or NULL) of all `prefix` children. */
59
+ fio_dict_s *fio_dict_prefix(fio_dict_s *dict, void *prefix, size_t len);
60
+
61
+ /**
62
+ * Creates a `fio_dict_s *` dictionary (if missing) for the `prefix`...
63
+ *
64
+ * After calling this function a node MUST be added to this dictionary, or
65
+ * memory leaks will occure.
66
+ */
67
+ fio_dict_s *fio_dict_ensure_prefix(fio_dict_s *dict, void *prefix, size_t len);
68
+
69
+ /** Traverses a dictionary, performing an action for each item. */
70
+ void fio_dict_each(fio_dict_s *dict,
71
+ void (*action)(fio_dict_s *node, void *arg), void *arg);
72
+
73
+ /** Performing an action for each item matching the glob pattern. */
74
+ void fio_dict_each_match_glob(fio_dict_s *dict, void *pattern, size_t len,
75
+ void (*action)(fio_dict_s *node, void *arg),
76
+ void *arg);
77
+
78
+ /** A binary glob matching helper. Returns 1 on match, otherwise returns 0. */
79
+ int fio_glob_match(uint8_t *data, size_t data_len, uint8_t *pattern,
80
+ size_t pat_len);
81
+
82
+ #define fio_dict_isempty(dict) \
83
+ (!((dict)->trie[0] || (dict)->trie[1] || (dict)->trie[2] || \
84
+ (dict)->trie[3] || (dict)->trie[4] || (dict)->trie[5] || \
85
+ (dict)->trie[6] || (dict)->trie[7] || (dict)->trie[8] || \
86
+ (dict)->trie[9] || (dict)->trie[10] || (dict)->trie[11] || \
87
+ (dict)->trie[12] || (dict)->trie[13] || (dict)->trie[14] || \
88
+ (dict)->trie[15]))
89
+
90
+ #endif
@@ -0,0 +1,370 @@
1
+ #ifndef H_FIO_HASH_TABLE_H
2
+ /**
3
+ The facil.io hash table imeplementation tries to balance performance with memory
4
+ footprint and collision risks.
5
+
6
+ It might be a good implementation and it might suck. Test it out and descide for
7
+ yourself.
8
+
9
+ */
10
+ #define H_FIO_HASH_TABLE_H
11
+ #include "fio_list.h"
12
+
13
+ #include <stdint.h>
14
+ #include <stdlib.h>
15
+
16
+ /* *****************************************************************************
17
+ Data Types and API
18
+ ***************************************************************************** */
19
+
20
+ #define FIO_FUNC static __attribute__((unused))
21
+
22
+ typedef struct {
23
+ fio_list_s items;
24
+ uint64_t count;
25
+ uint64_t bin_count;
26
+ uint64_t mask;
27
+ struct fio_list_s *bins;
28
+ } fio_ht_s;
29
+
30
+ /** Used to initialize an empty `fio_ht_s`. */
31
+ #define FIO_HASH_TABLE_INIT(name) \
32
+ (fio_ht_s) { .items = FIO_LIST_INIT_STATIC(name.items) }
33
+
34
+ /** Used to initialize an empty `fio_ht_s`. */
35
+ #define FIO_HASH_TABLE_STATIC(name) \
36
+ { .items = FIO_LIST_INIT_STATIC(name.items) }
37
+
38
+ typedef struct {
39
+ fio_list_s items;
40
+ fio_list_s siblings;
41
+ fio_ht_s *parent;
42
+ uint64_t hash;
43
+ } fio_ht_node_s;
44
+
45
+ #ifndef fio_node2obj
46
+ /** Takes a node pointer (list/hash/dict, etc') and returns it's container. */
47
+ #define fio_node2obj(type, member, ptr) \
48
+ ((type *)((uintptr_t)(ptr) - (uintptr_t)(&(((type *)0)->member))))
49
+ #endif
50
+
51
+ /** Takes a list pointer and returns a pointer to it's container. */
52
+ #define fio_ht_object(type, member, pht) fio_node2obj(type, member, (pht))
53
+
54
+ /** A simple SipHash2/4 function implementation that can be used for hashing. */
55
+ FIO_FUNC uint64_t fio_ht_hash(const void *data, size_t len);
56
+
57
+ /** A simple SipHash2/4 function for when a string's length is unknown. */
58
+ FIO_FUNC uint64_t fio_ht_hash_cstr(const void *data);
59
+
60
+ /**
61
+ * Adds a new item to the hash table.
62
+ *
63
+ * If a hash collision occurs, the old item is replaced and returned.
64
+ */
65
+ inline FIO_FUNC fio_ht_node_s *fio_ht_add(fio_ht_s *table, fio_ht_node_s *item,
66
+ uint64_t hash_value);
67
+
68
+ /** Finds an item in the hash table. */
69
+ inline FIO_FUNC fio_ht_node_s *fio_ht_find(fio_ht_s *table,
70
+ uint64_t hash_value);
71
+ /** Removes the item from the hash table, returning the removed item. */
72
+ inline FIO_FUNC fio_ht_node_s *fio_ht_remove(fio_ht_node_s *item);
73
+
74
+ /** Finds, removes and returns the matching item from the hash table. */
75
+ inline FIO_FUNC fio_ht_node_s *fio_ht_pop(fio_ht_s *table, uint64_t hash);
76
+
77
+ /**
78
+ * Re-hashes the hash table. This is automatically when growth is required.
79
+ *
80
+ * `bin_count` MUST be a power of 2 or 0 (0,1,2,4,8,16,32,64,128...).
81
+ *
82
+ * If `bin_count`, the hash table's memory is released and it will behave like a
83
+ * list (until an item is added).
84
+ */
85
+ inline FIO_FUNC void fio_ht_rehash(fio_ht_s *table, uint64_t bin_count);
86
+
87
+ /** iterates through all Hash Table members. */
88
+ #define fio_ht_for_each(type, member, var, table) \
89
+ fio_list_for_each(type, member.items, var, (table).items)
90
+
91
+ /**
92
+ * Frees any internal memory used by the hash table.
93
+ *
94
+ * This soes **NOT** free any items in the table or the table object itself.
95
+ */
96
+ #define fio_ht_free(table) fio_ht_rehash(table, 0)
97
+
98
+ /* *****************************************************************************
99
+ Implementations
100
+ ***************************************************************************** */
101
+
102
+ /** Adds a new item to the hash table. If a hash collision occurs, the old item
103
+ * is removed and replaced. */
104
+ inline FIO_FUNC fio_ht_node_s *fio_ht_add(fio_ht_s *table, fio_ht_node_s *item,
105
+ uint64_t hash) {
106
+ fio_ht_node_s *old = fio_ht_find(table, hash);
107
+ if (old == item)
108
+ return NULL;
109
+ /* initialize item */
110
+ item->parent = table;
111
+ item->hash = hash;
112
+ if (old)
113
+ goto found_old;
114
+ fio_list_add(table->items.prev, &item->items);
115
+ table->count++;
116
+ if (table->count >= ((table->bin_count >> 2) * 3)) {
117
+ fio_ht_rehash(table, (table->bin_count ? (table->bin_count << 1) : 32));
118
+ return NULL;
119
+ }
120
+ fio_list_add(table->bins[item->hash & table->mask].prev, &item->siblings);
121
+ return NULL;
122
+ found_old:
123
+ /* replace inplace. */
124
+ *item = *old;
125
+ old->parent = NULL;
126
+ old->items = FIO_LIST_INIT(old->items);
127
+ old->siblings = FIO_LIST_INIT(old->siblings);
128
+ item->items.prev->next = &item->items;
129
+ item->items.next->prev = &item->items;
130
+ item->siblings.prev->next = &item->siblings;
131
+ item->siblings.next->prev = &item->siblings;
132
+ return old;
133
+ }
134
+
135
+ /** Finds an item in the hash table. */
136
+ inline FIO_FUNC fio_ht_node_s *fio_ht_find(fio_ht_s *table,
137
+ uint64_t hash_value) {
138
+ fio_ht_node_s *item;
139
+ if (!table || !table->bins)
140
+ return NULL;
141
+ fio_list_for_each(fio_ht_node_s, siblings, item,
142
+ table->bins[hash_value & (table->mask)]) {
143
+ if (hash_value == item->hash) {
144
+ return item;
145
+ }
146
+ }
147
+ return NULL;
148
+ }
149
+
150
+ /** Removes an item to the hash table. */
151
+ inline FIO_FUNC fio_ht_node_s *fio_ht_remove(fio_ht_node_s *item) {
152
+ if (!item || !item->parent)
153
+ return item;
154
+ fio_list_remove(&item->siblings);
155
+ fio_list_remove(&item->items);
156
+ item->parent->count--;
157
+ /* ** memory shrinkage? not really... ** */
158
+ /*
159
+ if (item->parent->bin_count > 1024 &&
160
+ (item->parent->bin_count >> 3) >= item->parent->count)
161
+ fio_ht_rehash(item->parent, item->parent->bin_count >> 2);
162
+ */
163
+ item->parent = NULL;
164
+ return item;
165
+ }
166
+
167
+ /** Finds, removes and returns the matching item from the hash table. */
168
+ inline FIO_FUNC fio_ht_node_s *fio_ht_pop(fio_ht_s *table, uint64_t hash) {
169
+ fio_ht_node_s *item = fio_ht_find(table, hash);
170
+ if (!item)
171
+ return NULL;
172
+ fio_ht_remove(item);
173
+ return item;
174
+ }
175
+
176
+ /** Re-hashes the hash table. This is usually automatically called. */
177
+ inline FIO_FUNC void fio_ht_rehash(fio_ht_s *table, uint64_t bin_count) {
178
+ if (!table)
179
+ return;
180
+ if (!bin_count) {
181
+ if (table->bins) {
182
+ free(table->bins);
183
+ table->bins = NULL;
184
+ }
185
+ return;
186
+ }
187
+ void *mem = realloc(table->bins, bin_count * sizeof(*table->bins));
188
+ if (!mem)
189
+ return;
190
+ table->bin_count = bin_count;
191
+ table->bins = mem;
192
+ table->mask = bin_count - 1;
193
+ while (bin_count) {
194
+ bin_count--;
195
+ table->bins[bin_count] = FIO_LIST_INIT(table->bins[bin_count]);
196
+ }
197
+ fio_ht_node_s *item;
198
+ fio_list_for_each(fio_ht_node_s, items, item, table->items) {
199
+ fio_list_add(table->bins[item->hash & table->mask].prev, &item->siblings);
200
+ }
201
+ }
202
+
203
+ /* *****************************************************************************
204
+ The Hash Function
205
+ ***************************************************************************** */
206
+
207
+ #if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__) && \
208
+ __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
209
+ /* the algorithm was designed as little endian... so, byte swap 64 bit. */
210
+ #define sip_local64(i) \
211
+ (((i)&0xFFULL) << 56) | (((i)&0xFF00ULL) << 40) | \
212
+ (((i)&0xFF0000ULL) << 24) | (((i)&0xFF000000ULL) << 8) | \
213
+ (((i)&0xFF00000000ULL) >> 8) | (((i)&0xFF0000000000ULL) >> 24) | \
214
+ (((i)&0xFF000000000000ULL) >> 40) | (((i)&0xFF00000000000000ULL) >> 56)
215
+ #else
216
+ /* no need */
217
+ #define sip_local64(i) (i)
218
+ #endif
219
+
220
+ /* 64Bit left rotation, inlined. */
221
+ #define lrot64(i, bits) \
222
+ (((uint64_t)(i) << (bits)) | ((uint64_t)(i) >> (64 - (bits))))
223
+
224
+ FIO_FUNC uint64_t fio_ht_hash(const void *data, size_t len) {
225
+ /* initialize the 4 words */
226
+ uint64_t v0 = (0x0706050403020100ULL ^ 0x736f6d6570736575ULL);
227
+ uint64_t v1 = (0x0f0e0d0c0b0a0908ULL ^ 0x646f72616e646f6dULL);
228
+ uint64_t v2 = (0x0706050403020100ULL ^ 0x6c7967656e657261ULL);
229
+ uint64_t v3 = (0x0f0e0d0c0b0a0908ULL ^ 0x7465646279746573ULL);
230
+ const uint64_t *w64 = data;
231
+ uint8_t len_mod = len & 255;
232
+ union {
233
+ uint64_t i;
234
+ uint8_t str[8];
235
+ } word;
236
+
237
+ #define hash_map_SipRound \
238
+ do { \
239
+ v2 += v3; \
240
+ v3 = lrot64(v3, 16) ^ v2; \
241
+ v0 += v1; \
242
+ v1 = lrot64(v1, 13) ^ v0; \
243
+ v0 = lrot64(v0, 32); \
244
+ v2 += v1; \
245
+ v0 += v3; \
246
+ v1 = lrot64(v1, 17) ^ v2; \
247
+ v3 = lrot64(v3, 21) ^ v0; \
248
+ v2 = lrot64(v2, 32); \
249
+ } while (0);
250
+
251
+ while (len >= 8) {
252
+ word.i = sip_local64(*w64);
253
+ v3 ^= word.i;
254
+ /* Sip Rounds */
255
+ hash_map_SipRound;
256
+ hash_map_SipRound;
257
+ v0 ^= word.i;
258
+ w64 += 1;
259
+ len -= 8;
260
+ }
261
+ word.i = 0;
262
+ uint8_t *pos = word.str;
263
+ uint8_t *w8 = (void *)w64;
264
+ switch (len) { /* fallthrough is intentional */
265
+ case 7:
266
+ pos[6] = w8[6];
267
+ case 6:
268
+ pos[5] = w8[5];
269
+ case 5:
270
+ pos[4] = w8[4];
271
+ case 4:
272
+ pos[3] = w8[3];
273
+ case 3:
274
+ pos[2] = w8[2];
275
+ case 2:
276
+ pos[1] = w8[1];
277
+ case 1:
278
+ pos[0] = w8[0];
279
+ }
280
+ word.str[7] = len_mod;
281
+
282
+ /* last round */
283
+ v3 ^= word.i;
284
+ hash_map_SipRound;
285
+ hash_map_SipRound;
286
+ v0 ^= word.i;
287
+ /* Finalization */
288
+ v2 ^= 0xff;
289
+ /* d iterations of SipRound */
290
+ hash_map_SipRound;
291
+ hash_map_SipRound;
292
+ hash_map_SipRound;
293
+ hash_map_SipRound;
294
+ /* XOR it all together */
295
+ v0 ^= v1 ^ v2 ^ v3;
296
+ #undef hash_map_SipRound
297
+ return v0;
298
+ }
299
+
300
+ FIO_FUNC uint64_t fio_ht_hash_cstr(const void *data) {
301
+ /* initialize the 4 words */
302
+ uint64_t v0 = (0x0706050403020100ULL ^ 0x736f6d6570736575ULL);
303
+ uint64_t v1 = (0x0f0e0d0c0b0a0908ULL ^ 0x646f72616e646f6dULL);
304
+ uint64_t v2 = (0x0706050403020100ULL ^ 0x6c7967656e657261ULL);
305
+ uint64_t v3 = (0x0f0e0d0c0b0a0908ULL ^ 0x7465646279746573ULL);
306
+ const uint64_t *w64 = data;
307
+ uint8_t len = 0;
308
+ union {
309
+ uint64_t i;
310
+ uint8_t str[8];
311
+ } word;
312
+
313
+ #define hash_map_SipRound \
314
+ do { \
315
+ v2 += v3; \
316
+ v3 = lrot64(v3, 16) ^ v2; \
317
+ v0 += v1; \
318
+ v1 = lrot64(v1, 13) ^ v0; \
319
+ v0 = lrot64(v0, 32); \
320
+ v2 += v1; \
321
+ v0 += v3; \
322
+ v1 = lrot64(v1, 17) ^ v2; \
323
+ v3 = lrot64(v3, 21) ^ v0; \
324
+ v2 = lrot64(v2, 32); \
325
+ } while (0);
326
+
327
+ while ((*w64 & 0xFFULL) && (*w64 & 0xFF00ULL) && (*w64 & 0xFF0000ULL) &&
328
+ (*w64 & 0xFF000000ULL) && (*w64 & 0xFF00000000ULL) &&
329
+ (*w64 & 0xFF0000000000ULL) && (*w64 & 0xFF000000000000ULL) &&
330
+ (*w64 & 0xFF00000000000000ULL)) {
331
+ word.i = sip_local64(*w64);
332
+ v3 ^= word.i;
333
+ /* Sip Rounds */
334
+ hash_map_SipRound;
335
+ hash_map_SipRound;
336
+ v0 ^= word.i;
337
+ w64 += 1;
338
+ len += 8;
339
+ }
340
+ word.i = 0;
341
+ uint8_t *pos = word.str;
342
+ uint8_t *w8 = (void *)w64;
343
+ while (*w8) {
344
+ *(pos++) = *(w8++);
345
+ len++;
346
+ }
347
+ word.str[7] = len & 255;
348
+
349
+ /* last round */
350
+ v3 ^= word.i;
351
+ hash_map_SipRound;
352
+ hash_map_SipRound;
353
+ v0 ^= word.i;
354
+ /* Finalization */
355
+ v2 ^= 0xff;
356
+ /* d iterations of SipRound */
357
+ hash_map_SipRound;
358
+ hash_map_SipRound;
359
+ hash_map_SipRound;
360
+ hash_map_SipRound;
361
+ /* XOR it all together */
362
+ v0 ^= v1 ^ v2 ^ v3;
363
+ #undef hash_map_SipRound
364
+ return v0;
365
+ }
366
+
367
+ #undef sip_local64
368
+ #undef lrot64
369
+ #undef FIO_FUNC
370
+ #endif