iodine 0.7.60 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. checksums.yaml +4 -4
  2. data/.clang-format +15 -0
  3. data/.rspec +1 -0
  4. data/.yardopts +1 -3
  5. data/CHANGELOG.md +132 -12
  6. data/Gemfile +5 -7
  7. data/LICENSE.txt +1 -1
  8. data/README.md +147 -671
  9. data/Rakefile +9 -16
  10. data/examples/chatroom.nru +9 -0
  11. data/examples/client +74 -0
  12. data/examples/config-hello.nru +9 -0
  13. data/examples/config-hello.ru +11 -0
  14. data/examples/config-hijack.ru +13 -0
  15. data/examples/config-shootout.ru +43 -0
  16. data/examples/config.nru +120 -0
  17. data/examples/config.rb +37 -0
  18. data/examples/config.ru +42 -53
  19. data/examples/fake +24 -0
  20. data/examples/hello_neorack +137 -0
  21. data/examples/routes.nru +29 -0
  22. data/examples/routes.ru +42 -0
  23. data/exe/iodine +218 -258
  24. data/ext/iodine/extconf.rb +27 -100
  25. data/ext/iodine/fio-stl.h +128336 -0
  26. data/ext/iodine/fio-stl.md +26396 -0
  27. data/ext/iodine/iodine.c +213 -1393
  28. data/ext/iodine/iodine.h +250 -55
  29. data/ext/iodine/iodine_arg_helper.h +329 -0
  30. data/ext/iodine/iodine_caller.h +234 -25
  31. data/ext/iodine/iodine_cli.h +434 -0
  32. data/ext/iodine/iodine_compression.h +312 -0
  33. data/ext/iodine/iodine_connection.h +3484 -34
  34. data/ext/iodine/iodine_core.h +363 -0
  35. data/ext/iodine/iodine_crypto.h +1441 -0
  36. data/ext/iodine/iodine_defer.h +225 -4
  37. data/ext/iodine/iodine_json.h +738 -4
  38. data/ext/iodine/iodine_listener.h +263 -0
  39. data/ext/iodine/iodine_log.h +297 -0
  40. data/ext/iodine/iodine_minimap.h +543 -0
  41. data/ext/iodine/iodine_mustache.h +461 -4
  42. data/ext/iodine/iodine_pubsub_eng.h +729 -0
  43. data/ext/iodine/iodine_pubsub_history.h +547 -0
  44. data/ext/iodine/iodine_pubsub_msg.h +228 -0
  45. data/ext/iodine/iodine_redis.h +322 -0
  46. data/ext/iodine/iodine_store.h +418 -16
  47. data/ext/iodine/iodine_threads.h +279 -0
  48. data/ext/iodine/iodine_tls.h +515 -6
  49. data/ext/iodine/iodine_utils.h +923 -0
  50. data/iodine.gemspec +37 -42
  51. data/lib/iodine/benchmark.rb +1690 -0
  52. data/lib/iodine/documentation.rb +3085 -0
  53. data/lib/iodine/version.rb +17 -1
  54. data/lib/iodine.rb +54 -255
  55. data/logo.svg +36 -0
  56. data/mustache.md +55 -0
  57. data/sig/iodine.rbs +4 -0
  58. metadata +58 -184
  59. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
  60. data/.github/workflows/ruby.yml +0 -42
  61. data/.gitignore +0 -21
  62. data/LIMITS.md +0 -41
  63. data/SPEC-PubSub-Draft.md +0 -159
  64. data/SPEC-WebSocket-Draft.md +0 -239
  65. data/bin/console +0 -22
  66. data/bin/info.md +0 -353
  67. data/bin/mustache_bench.rb +0 -100
  68. data/bin/poc/Gemfile.lock +0 -23
  69. data/bin/poc/README.md +0 -37
  70. data/bin/poc/config.ru +0 -66
  71. data/bin/poc/gemfile +0 -1
  72. data/bin/poc/www/index.html +0 -57
  73. data/examples/async_task.ru +0 -92
  74. data/examples/bates/README.md +0 -3
  75. data/examples/bates/config.ru +0 -342
  76. data/examples/bates/david+bold.pdf +0 -0
  77. data/examples/bates/public/drop-pdf.png +0 -0
  78. data/examples/bates/public/index.html +0 -600
  79. data/examples/echo.ru +0 -59
  80. data/examples/etag.ru +0 -16
  81. data/examples/hello.ru +0 -29
  82. data/examples/pubsub_engine.ru +0 -81
  83. data/examples/rack3.ru +0 -12
  84. data/examples/redis.ru +0 -70
  85. data/examples/shootout.ru +0 -73
  86. data/examples/sub-protocols.ru +0 -90
  87. data/examples/tcp_client.rb +0 -66
  88. data/examples/x-sendfile.ru +0 -14
  89. data/ext/iodine/fio.c +0 -11995
  90. data/ext/iodine/fio.h +0 -6371
  91. data/ext/iodine/fio_cli.c +0 -431
  92. data/ext/iodine/fio_cli.h +0 -189
  93. data/ext/iodine/fio_json_parser.h +0 -688
  94. data/ext/iodine/fio_siphash.c +0 -157
  95. data/ext/iodine/fio_siphash.h +0 -37
  96. data/ext/iodine/fio_tls.h +0 -129
  97. data/ext/iodine/fio_tls_missing.c +0 -649
  98. data/ext/iodine/fio_tls_openssl.c +0 -1106
  99. data/ext/iodine/fio_tmpfile.h +0 -50
  100. data/ext/iodine/fiobj.h +0 -44
  101. data/ext/iodine/fiobj4fio.h +0 -21
  102. data/ext/iodine/fiobj_ary.c +0 -333
  103. data/ext/iodine/fiobj_ary.h +0 -139
  104. data/ext/iodine/fiobj_data.c +0 -1187
  105. data/ext/iodine/fiobj_data.h +0 -167
  106. data/ext/iodine/fiobj_hash.c +0 -409
  107. data/ext/iodine/fiobj_hash.h +0 -176
  108. data/ext/iodine/fiobj_json.c +0 -622
  109. data/ext/iodine/fiobj_json.h +0 -68
  110. data/ext/iodine/fiobj_mem.h +0 -71
  111. data/ext/iodine/fiobj_mustache.c +0 -317
  112. data/ext/iodine/fiobj_mustache.h +0 -62
  113. data/ext/iodine/fiobj_numbers.c +0 -344
  114. data/ext/iodine/fiobj_numbers.h +0 -127
  115. data/ext/iodine/fiobj_str.c +0 -433
  116. data/ext/iodine/fiobj_str.h +0 -172
  117. data/ext/iodine/fiobject.c +0 -620
  118. data/ext/iodine/fiobject.h +0 -655
  119. data/ext/iodine/hpack.h +0 -1923
  120. data/ext/iodine/http.c +0 -2743
  121. data/ext/iodine/http.h +0 -1002
  122. data/ext/iodine/http1.c +0 -910
  123. data/ext/iodine/http1.h +0 -29
  124. data/ext/iodine/http1_parser.h +0 -878
  125. data/ext/iodine/http_internal.c +0 -1279
  126. data/ext/iodine/http_internal.h +0 -237
  127. data/ext/iodine/http_mime_parser.h +0 -350
  128. data/ext/iodine/iodine_caller.c +0 -230
  129. data/ext/iodine/iodine_connection.c +0 -941
  130. data/ext/iodine/iodine_defer.c +0 -420
  131. data/ext/iodine/iodine_fiobj2rb.h +0 -120
  132. data/ext/iodine/iodine_helpers.c +0 -282
  133. data/ext/iodine/iodine_helpers.h +0 -12
  134. data/ext/iodine/iodine_http.c +0 -1181
  135. data/ext/iodine/iodine_http.h +0 -23
  136. data/ext/iodine/iodine_json.c +0 -302
  137. data/ext/iodine/iodine_mustache.c +0 -567
  138. data/ext/iodine/iodine_pubsub.c +0 -580
  139. data/ext/iodine/iodine_pubsub.h +0 -26
  140. data/ext/iodine/iodine_rack_io.c +0 -273
  141. data/ext/iodine/iodine_rack_io.h +0 -20
  142. data/ext/iodine/iodine_store.c +0 -148
  143. data/ext/iodine/iodine_tcp.c +0 -346
  144. data/ext/iodine/iodine_tcp.h +0 -13
  145. data/ext/iodine/iodine_tls.c +0 -261
  146. data/ext/iodine/mustache_parser.h +0 -1546
  147. data/ext/iodine/redis_engine.c +0 -957
  148. data/ext/iodine/redis_engine.h +0 -79
  149. data/ext/iodine/resp_parser.h +0 -317
  150. data/ext/iodine/websocket_parser.h +0 -506
  151. data/ext/iodine/websockets.c +0 -743
  152. data/ext/iodine/websockets.h +0 -185
  153. data/lib/iodine/connection.rb +0 -61
  154. data/lib/iodine/json.rb +0 -42
  155. data/lib/iodine/mustache.rb +0 -113
  156. data/lib/iodine/pubsub.rb +0 -55
  157. data/lib/iodine/rack_utils.rb +0 -43
  158. data/lib/iodine/tls.rb +0 -16
  159. data/lib/rack/handler/iodine.rb +0 -33
  160. data/logo.png +0 -0
@@ -0,0 +1,543 @@
1
+ #ifndef H___IODINE_MINIMAP___H
2
+ #define H___IODINE_MINIMAP___H
3
+ #include "iodine.h"
4
+
5
+ /* *****************************************************************************
6
+ Iodine MiniMap - Lightweight Hash Map for Ruby
7
+
8
+ This module provides the Iodine::Base::MiniMap Ruby class, a lightweight
9
+ hash map implementation backed by facil.io's hash map. It's designed for
10
+ internal use where a simple key-value store is needed with:
11
+
12
+ - String or Symbol keys only
13
+ - Fast lookup and insertion
14
+ - JSON serialization support
15
+ - Iteration via each block
16
+
17
+ The MiniMap is simpler and potentially faster than Ruby's Hash for
18
+ specific use cases, but has fewer features.
19
+
20
+ Ruby API (Iodine::Base::MiniMap):
21
+ - map[key] - Get value for key
22
+ - map[key] = value - Set value (nil deletes key)
23
+ - map.count - Number of entries
24
+ - map.capa - Current capacity
25
+ - map.each { } - Iterate key-value pairs
26
+ - map.clear - Remove all entries
27
+ - map.reserve(n) - Pre-allocate capacity
28
+ - map.to_s / to_json - JSON string representation
29
+
30
+ Benchmark:
31
+ require 'iodine/benchmark'
32
+ Iodine::Benchmark.minimap(100)
33
+ ***************************************************************************** */
34
+
35
+ /** The Iodine::Base::MiniMap Ruby class */
36
+ static VALUE iodine_rb_IODINE_BASE_MINIMAP;
37
+
38
+ /* *****************************************************************************
39
+ Mini Ruby Hash Map - Internal Implementation
40
+ ***************************************************************************** */
41
+
42
+ static uint64_t iodinde_hmap_hash(VALUE k) {
43
+ if (RB_TYPE_P(k, RUBY_T_STRING)) {
44
+ fio_buf_info_s buf = (fio_buf_info_s)IODINE_RSTR_INFO(k);
45
+ return fio_risky_hash(buf.buf, buf.len, (uint64_t)&iodinde_hmap_hash);
46
+ }
47
+ if (RB_SYMBOL_P(k) || RB_FIXNUM_P(k) || k == RUBY_T_TRUE ||
48
+ k == RUBY_T_FALSE || k == RUBY_T_NIL)
49
+ return fio_risky_num((uint64_t)k, (uint64_t)&iodinde_hmap_hash);
50
+ rb_raise(rb_eTypeError, "key MUST be either a String or a Symbol.");
51
+ return 0;
52
+ }
53
+
54
+ static _Bool iodinde_hmap_object_cmp(VALUE a, VALUE b) {
55
+ if (a == b)
56
+ return 1;
57
+ if ((unsigned)(!RB_TYPE_P(b, RUBY_T_STRING)) | (!RB_TYPE_P(a, RUBY_T_STRING)))
58
+ return 0;
59
+ fio_buf_info_s aa = IODINE_RSTR_INFO(a);
60
+ fio_buf_info_s bb = IODINE_RSTR_INFO(b);
61
+ return FIO_BUF_INFO_IS_EQ(aa, bb);
62
+ }
63
+
64
+ #define FIO_MAP_NAME iodine_hmap
65
+ #define FIO_MAP_KEY VALUE
66
+ #define FIO_MAP_VALUE VALUE
67
+ #define FIO_MAP_HASH_FN iodinde_hmap_hash
68
+ #define FIO_MAP_KEY_CMP(a, b) iodinde_hmap_object_cmp(a, b)
69
+ #define FIO_MAP_KEY_COPY(dest, src) ((dest) = (src))
70
+ #include FIO_INCLUDE_FILE
71
+
72
+ /**
73
+ * Internal structure for the MiniMap Ruby object.
74
+ *
75
+ * Wraps a facil.io hash map with a temporary VALUE holder
76
+ * used during key duplication to prevent GC issues.
77
+ */
78
+ typedef struct {
79
+ iodine_hmap_s map; /**< The underlying facil.io hash map */
80
+ VALUE tmp; /**< Temporary holder during key operations */
81
+ } iodine_minimap_s;
82
+
83
+ static int iodine_minimap_gc_mark_task(iodine_hmap_each_s *e) {
84
+ rb_gc_mark(e->key);
85
+ rb_gc_mark(e->value);
86
+ return 0;
87
+ }
88
+
89
+ FIO_IFUNC void iodine_minimap_gc_mark(void *m_) {
90
+ iodine_minimap_s *m = (iodine_minimap_s *)m_;
91
+ if (m->tmp)
92
+ rb_gc_mark(m->tmp);
93
+ iodine_hmap_each(&m->map, iodine_minimap_gc_mark_task, NULL, 0);
94
+ }
95
+
96
+ static VALUE iodine_minimap_store(iodine_minimap_s *m, VALUE key, VALUE value) {
97
+ if (value == Qnil)
98
+ goto on_delete;
99
+ if (RB_TYPE_P(key, T_STRING) && !rb_obj_frozen_p(key)) {
100
+ m->tmp = value;
101
+ key = rb_str_dup_frozen(key);
102
+ m->tmp = 0;
103
+ }
104
+ return iodine_hmap_set(&m->map, key, value, NULL);
105
+ on_delete:
106
+ iodine_hmap_remove(&m->map, key, &value);
107
+ return value;
108
+ }
109
+
110
+ FIO_IFUNC VALUE iodine_minimap_rb_get(iodine_minimap_s *m, VALUE key) {
111
+ return iodine_hmap_get(&m->map, key);
112
+ }
113
+
114
+ typedef struct iodine_hmap_rb_each_task_s {
115
+ size_t r;
116
+ iodine_hmap_s *m;
117
+ } iodine_hmap_rb_each_task_s;
118
+
119
+ static int iodine_minimap_c_each_task(iodine_hmap_each_s *e) {
120
+ iodine_hmap_rb_each_task_s *info = (iodine_hmap_rb_each_task_s *)e->udata;
121
+ VALUE args[] = {e->key, e->value};
122
+ info->r++;
123
+ rb_yield_values2(2, args);
124
+ return 0;
125
+ }
126
+ static VALUE iodine_minimap_rb_each_task(VALUE info_) {
127
+ iodine_hmap_rb_each_task_s *info = (iodine_hmap_rb_each_task_s *)info_;
128
+ iodine_hmap_each(info->m, iodine_minimap_c_each_task, info, 0);
129
+ return Qnil;
130
+ }
131
+
132
+ static VALUE iodine_minimap_rb_each(iodine_minimap_s *m) {
133
+ rb_need_block();
134
+ if (!iodine_hmap_count(&m->map))
135
+ return ULL2NUM(0);
136
+ iodine_hmap_rb_each_task_s i = {.m = &m->map};
137
+ VALUE exc;
138
+ int e = 0;
139
+ rb_protect(iodine_minimap_rb_each_task, (VALUE)&i, &e);
140
+ if (e)
141
+ goto error;
142
+ // iodine_hmap_each(m, iodine_hmap_c_each_task, &i, 0);
143
+ return ULL2NUM(i.r);
144
+ error:
145
+ exc = rb_errinfo();
146
+ if (!rb_obj_is_instance_of(exc, rb_eLocalJumpError))
147
+ iodine_handle_exception(NULL);
148
+ rb_set_errinfo(Qnil);
149
+ return ULL2NUM(i.r);
150
+ }
151
+
152
+ /* *****************************************************************************
153
+ Ruby Object - TypedData Wrapper
154
+ ***************************************************************************** */
155
+
156
+ static void iodine_minimap_free(void *p) {
157
+ iodine_minimap_s *m = (iodine_minimap_s *)p;
158
+ iodine_hmap_destroy(&m->map);
159
+ ruby_xfree(m);
160
+ FIO_LEAK_COUNTER_ON_FREE(iodine_minimap);
161
+ }
162
+
163
+ static size_t iodine_minimap_size(const void *p) {
164
+ iodine_minimap_s *m = (iodine_minimap_s *)p;
165
+ return sizeof(*m) + (iodine_hmap_capa(&m->map) * sizeof(m->map.map[0]));
166
+ }
167
+
168
+ static const rb_data_type_t IODINE_MINIMAP_DATA_TYPE = {
169
+ .wrap_struct_name = "IodineMiniMap",
170
+ .function =
171
+ {
172
+ .dmark = iodine_minimap_gc_mark,
173
+ .dfree = iodine_minimap_free,
174
+ .dsize = iodine_minimap_size,
175
+ },
176
+ .data = NULL,
177
+ // .flags = RUBY_TYPED_FREE_IMMEDIATELY,
178
+ };
179
+
180
+ static iodine_minimap_s *iodine_minimap_ptr(VALUE self) {
181
+ iodine_minimap_s *m;
182
+ TypedData_Get_Struct(self, iodine_minimap_s, &IODINE_MINIMAP_DATA_TYPE, m);
183
+ return m;
184
+ }
185
+
186
+ static VALUE iodine_minimap_alloc(VALUE klass) {
187
+
188
+ FIO_LEAK_COUNTER_ON_ALLOC(iodine_minimap);
189
+ iodine_minimap_s *m;
190
+ VALUE self = TypedData_Make_Struct(klass,
191
+ iodine_minimap_s,
192
+ &IODINE_MINIMAP_DATA_TYPE,
193
+ m);
194
+ *m = (iodine_minimap_s)FIO_MAP_INIT;
195
+ return self;
196
+ // return TypedData_Wrap_Struct(klass, &IODINE_MINIMAP_DATA_TYPE, m);
197
+ }
198
+
199
+ /* *****************************************************************************
200
+ API - Ruby Methods
201
+ ***************************************************************************** */
202
+
203
+ static VALUE iodine_minimap_get(VALUE o, VALUE key) {
204
+ VALUE r = iodine_minimap_rb_get(iodine_minimap_ptr(o), key);
205
+ if (!r)
206
+ r = Qnil;
207
+ return r;
208
+ }
209
+ static VALUE iodine_minimap_set(VALUE o, VALUE key, VALUE value) {
210
+ return iodine_minimap_store(iodine_minimap_ptr(o), key, value);
211
+ }
212
+ static VALUE iodine_minimap_each(VALUE o) {
213
+ return iodine_minimap_rb_each(iodine_minimap_ptr(o));
214
+ }
215
+ static VALUE iodine_minimap_count(VALUE o) {
216
+ return ULL2NUM(
217
+ (unsigned long long)iodine_hmap_count(&iodine_minimap_ptr(o)->map));
218
+ }
219
+ static VALUE iodine_minimap_clear(VALUE o) {
220
+ iodine_hmap_clear(&iodine_minimap_ptr(o)->map);
221
+ return o;
222
+ }
223
+
224
+ static VALUE iodine_minimap_capa(VALUE o) {
225
+ return ULL2NUM(
226
+ (unsigned long long)iodine_hmap_capa(&iodine_minimap_ptr(o)->map));
227
+ }
228
+ static VALUE iodine_minimap_reserve(VALUE o, VALUE s_) {
229
+ rb_check_type(s_, T_FIXNUM);
230
+ size_t s = (size_t)NUM2ULL(s_);
231
+ if (s > 0x0FFFFFFFULL)
232
+ rb_raise(
233
+ rb_eRangeError,
234
+ "cannot reserve negative values or values higher than 268,435,455.");
235
+ iodine_hmap_s *m = &iodine_minimap_ptr(o)->map;
236
+ iodine_hmap_reserve(m, s);
237
+ return o;
238
+ }
239
+
240
+ static inline bool iodine_is_minimap(VALUE o) {
241
+ return (rb_class_of(o) == iodine_rb_IODINE_BASE_MINIMAP);
242
+ }
243
+
244
+ static inline VALUE iodine_is_minimap_rb(VALUE o) {
245
+ return (rb_class_of(o) == iodine_rb_IODINE_BASE_MINIMAP) ? Qtrue : Qfalse;
246
+ }
247
+
248
+ /* *****************************************************************************
249
+ String (JSON) Output - Serialization
250
+ ***************************************************************************** */
251
+
252
+ static int iodine___minimap_to_s_task(iodine_hmap_each_s *e) {
253
+ VALUE tmp = e->key;
254
+ /* Note: keys MUST be Strings */
255
+ switch (rb_type(tmp)) {
256
+ case RUBY_T_SYMBOL: tmp = rb_sym2str(tmp); /* fall through */
257
+ case RUBY_T_STRING:
258
+ string_key:
259
+ *(char **)e->udata = fio_bstr_write(*(char **)e->udata, "\"", 1);
260
+ *(char **)e->udata = fio_bstr_write_escape(*(char **)e->udata,
261
+ RSTRING_PTR(tmp),
262
+ (size_t)RSTRING_LEN(tmp));
263
+ *(char **)e->udata = fio_bstr_write(*(char **)e->udata, "\"", 1);
264
+ break;
265
+ case RUBY_T_FIXNUM:
266
+ *(char **)e->udata = fio_bstr_write2(*(char **)e->udata,
267
+ FIO_STRING_WRITE_STR2("\"", 1),
268
+ FIO_STRING_WRITE_NUM(NUM2LL(tmp)),
269
+ FIO_STRING_WRITE_STR2("\"", 1));
270
+ break;
271
+ case RUBY_T_TRUE:
272
+ *(char **)e->udata = fio_bstr_write(*(char **)e->udata, "\"true\"", 6);
273
+ break;
274
+ case RUBY_T_FALSE:
275
+ *(char **)e->udata = fio_bstr_write(*(char **)e->udata, "\"false\"", 7);
276
+ break;
277
+ case RUBY_T_NIL:
278
+ *(char **)e->udata = fio_bstr_write(*(char **)e->udata, "\"null\"", 6);
279
+ break;
280
+ default:
281
+ tmp = rb_any_to_s(tmp);
282
+ if (RB_TYPE_P(tmp, RUBY_T_STRING))
283
+ goto string_key;
284
+ *(char **)e->udata = fio_bstr_write(*(char **)e->udata, "\"error\"", 7);
285
+ }
286
+ *(char **)e->udata = fio_bstr_write(*(char **)e->udata, ":", 1);
287
+ *(char **)e->udata = iodine_json_stringify2bstr(*(char **)e->udata, e->value);
288
+ *(char **)e->udata = fio_bstr_write(*(char **)e->udata, ",", 1);
289
+ return 0;
290
+ }
291
+
292
+ FIO_SFUNC VALUE iodine_minimap_to_s(int argc, VALUE *argv, VALUE o) {
293
+ VALUE r = Qnil;
294
+ iodine_hmap_s *m = &iodine_minimap_ptr(o)->map;
295
+ char *str;
296
+ if (!iodine_hmap_count(m))
297
+ goto empty;
298
+ str = fio_bstr_reserve(NULL, ((size_t)1 << 12) - 64);
299
+ str = fio_bstr_write(str, "{", 1);
300
+ iodine_hmap_each(m, iodine___minimap_to_s_task, &str, 0);
301
+ str[fio_bstr_len(str) - 1] = '}';
302
+ r = rb_str_new(str, (long)fio_bstr_len(str));
303
+ fio_bstr_free(str);
304
+ return r;
305
+ empty:
306
+ r = rb_str_new("{}", 2);
307
+ return r;
308
+ (void)argc, (void)argv;
309
+ }
310
+
311
+ /* *****************************************************************************
312
+ Benchmark - C Performance Testing
313
+
314
+ Compares MiniMap performance against Ruby Hash for:
315
+ - Insert operations
316
+ - Overwrite operations
317
+ - Find operations (existing keys)
318
+ - Find operations (missing keys)
319
+ - Iteration (each)
320
+ ***************************************************************************** */
321
+
322
+ static int each_task(void *_i) {
323
+ FIO_COMPILER_GUARD;
324
+ return 0;
325
+ (void)_i;
326
+ }
327
+
328
+ #define FIO_MAP_NAME iodine_mini_map
329
+ #define FIO_MAP_KEY size_t
330
+ #define FIO_MAP_KEY_CMP(a, b) ((a) == (b))
331
+ #define FIO_MAP_VALUE size_t
332
+ #define FIO_MAP_HASH_FN(k) (fio_risky_num(k, 0))
333
+ #include FIO_INCLUDE_FILE
334
+
335
+ typedef VALUE ruby_hash_s;
336
+ FIO_IFUNC void ruby_hash_destroy(VALUE *m) {
337
+ if (!*m)
338
+ return;
339
+ STORE.release(*m);
340
+ *m = 0;
341
+ }
342
+ FIO_IFUNC size_t ruby_hash_reserve(VALUE *m, size_t capa) {
343
+ *m = rb_hash_new();
344
+ STORE.hold(*m);
345
+ (void)capa;
346
+ return capa;
347
+ }
348
+
349
+ FIO_IFUNC size_t ruby_hash_count(VALUE *m) { return RHASH_SIZE(*m); }
350
+ FIO_IFUNC size_t ruby_hash_capa(VALUE *m) {
351
+ return rb_st_memsize(RHASH_TBL(*m)) / (sizeof(VALUE) * 3);
352
+ }
353
+ FIO_IFUNC VALUE ruby_hash_set(VALUE *m, size_t k, size_t v, size_t *old) {
354
+ rb_hash_aset(*m, k, v);
355
+ return v;
356
+ (void)old;
357
+ }
358
+ FIO_IFUNC size_t ruby_hash_get(VALUE *m, size_t o) {
359
+ VALUE tmp = rb_hash_aref(*m, o);
360
+ if (tmp == Qnil)
361
+ return 0;
362
+ return tmp;
363
+ }
364
+
365
+ FIO_SFUNC int ruby_hash_each___task(VALUE m, VALUE k, VALUE v) {
366
+ FIO_COMPILER_GUARD;
367
+ return ST_CONTINUE;
368
+ (void)m, (void)k, (void)v;
369
+ }
370
+ typedef struct {
371
+ int i;
372
+ } ruby_hash_each_s;
373
+ FIO_IFUNC size_t ruby_hash_each(VALUE *m,
374
+ int (*each_task)(ruby_hash_each_s *),
375
+ void *udata,
376
+ ssize_t start_at) {
377
+ rb_hash_foreach(*m, ruby_hash_each___task, (VALUE)udata);
378
+ return RHASH_SIZE((*m));
379
+ (void)start_at, (void)each_task;
380
+ }
381
+
382
+ static VALUE iodine_minimap_benchmark_c(VALUE klass, VALUE object_count) {
383
+ (void)klass;
384
+ if (object_count == Qnil)
385
+ object_count = ULONG2NUM(30UL);
386
+ if (NUM2ULL(object_count) > 10000000UL)
387
+ rb_raise(rb_eRangeError, "object count is too high.");
388
+ const size_t ORDERED_OBJECTS = 0U;
389
+ const size_t RANDOM_OBJECTS = NUM2ULL(object_count);
390
+ const size_t OBJECTS = ORDERED_OBJECTS + RANDOM_OBJECTS;
391
+ const size_t CYCLES = 10000000U / OBJECTS;
392
+ const size_t TESTS = 3U;
393
+ const size_t FIND_CYCLES = CYCLES;
394
+ const size_t EACH_CYCLES = CYCLES;
395
+ const size_t MISSING_OBJECTS = OBJECTS;
396
+ size_t *numbers = malloc(sizeof(*numbers) * (OBJECTS + MISSING_OBJECTS));
397
+ { /* make sure all objects are unique */
398
+ iodine_mini_map_s existing = {0};
399
+ for (size_t i = 1; i < ORDERED_OBJECTS + 1; ++i) {
400
+ numbers[i - 1] = i;
401
+ iodine_mini_map_set(&existing, i - i, i, NULL);
402
+ }
403
+ for (size_t i = ORDERED_OBJECTS; i < (OBJECTS + MISSING_OBJECTS); ++i) {
404
+ do {
405
+ numbers[i] = fio_rand64();
406
+ numbers[i] >>= 32;
407
+ numbers[i] = ULL2NUM(numbers[i]);
408
+ } while (iodine_mini_map_get_ptr(&existing, numbers[i]));
409
+ numbers[i] += !numbers[i];
410
+ iodine_mini_map_set(&existing, numbers[i], numbers[i], NULL);
411
+ }
412
+ iodine_mini_map_destroy(&existing);
413
+ }
414
+ #define PERFORM_TEST(klass) \
415
+ do { \
416
+ klass##_s m = {0}; \
417
+ uint64_t insert, overwrite, find, loop, find_missing, start; \
418
+ start = fio_time_micro(); \
419
+ start = fio_time_micro(); \
420
+ for (size_t j = 0; j < CYCLES; ++j) { \
421
+ klass##_destroy(&m); \
422
+ if (1) \
423
+ klass##_reserve(&m, 8 | ((OBJECTS) >> 31)); \
424
+ for (size_t i = 0; i < OBJECTS; ++i) { \
425
+ klass##_set(&m, numbers[i], numbers[i], NULL); \
426
+ } \
427
+ } \
428
+ insert = fio_time_micro() - start; \
429
+ if (klass##_count(&m) != OBJECTS) \
430
+ FIO_LOG_ERROR("map counter error (%zu != %zu)!", \
431
+ klass##_count(&m), \
432
+ OBJECTS); \
433
+ if (klass##_capa(&m) < OBJECTS) \
434
+ FIO_LOG_ERROR("map capacity error (%zu < %zu)!", \
435
+ klass##_capa(&m), \
436
+ OBJECTS); \
437
+ start = fio_time_micro(); \
438
+ for (size_t j = 0; j < CYCLES; ++j) { \
439
+ for (size_t i = 0; i < OBJECTS; ++i) \
440
+ klass##_set(&m, numbers[i], numbers[i], NULL); \
441
+ } \
442
+ overwrite = fio_time_micro() - start; \
443
+ if (klass##_count(&m) != OBJECTS) \
444
+ FIO_LOG_ERROR("map counter error (%zu != %zu)!", \
445
+ klass##_count(&m), \
446
+ OBJECTS); \
447
+ if (klass##_capa(&m) < OBJECTS) \
448
+ FIO_LOG_ERROR("map capacity error (%zu < %zu)!", \
449
+ klass##_capa(&m), \
450
+ OBJECTS); \
451
+ start = fio_time_micro(); \
452
+ for (size_t sc = 0; sc < FIND_CYCLES; ++sc) \
453
+ for (size_t i = 0; i < OBJECTS; ++i) { \
454
+ if (klass##_get(&m, numbers[i]) != numbers[i]) \
455
+ FIO_LOG_ERROR(FIO_MACRO2STR(klass) "_get error ([%zu] %zu != %zu)!", \
456
+ i, \
457
+ numbers[i], \
458
+ klass##_get(&m, i)); \
459
+ } \
460
+ find = fio_time_micro() - start; \
461
+ start = fio_time_micro(); \
462
+ for (size_t sc = 0; sc < FIND_CYCLES; ++sc) \
463
+ for (size_t i = OBJECTS; i < (OBJECTS + MISSING_OBJECTS); ++i) { \
464
+ if (klass##_get(&m, numbers[i])) \
465
+ FIO_LOG_ERROR( \
466
+ FIO_MACRO2STR(klass) "_get error" \
467
+ "([%zu] %zu shouldn't exist but == %zu)!", \
468
+ i, \
469
+ numbers[i], \
470
+ klass##_get(&m, numbers[i])); \
471
+ } \
472
+ find_missing = fio_time_micro() - start; \
473
+ start = fio_time_micro(); \
474
+ for (size_t i = 0; i < EACH_CYCLES; ++i) \
475
+ klass##_each(&m, (int (*)(klass##_each_s *))each_task, NULL, 0); \
476
+ loop = fio_time_micro() - start; \
477
+ FIO_LOG_INFO(" %-16s" \
478
+ "\tcapa: %zu/%-6zu" \
479
+ "\tinsert: %-6zu" \
480
+ "\toverwrite: %-6zu" \
481
+ "\tfind: %-6zu" \
482
+ "\tfind missing: %-6zu" \
483
+ "\tloop: %-6zu", \
484
+ FIO_MACRO2STR(klass), \
485
+ (size_t)klass##_count(&m), \
486
+ (size_t)klass##_capa(&m), \
487
+ insert, \
488
+ overwrite, \
489
+ find, \
490
+ find_missing, \
491
+ loop); \
492
+ klass##_destroy(&m); \
493
+ } while (0);
494
+ for (size_t tst = 0; tst < TESTS; ++tst) {
495
+ PERFORM_TEST(iodine_mini_map)
496
+ PERFORM_TEST(ruby_hash)
497
+ }
498
+ free(numbers);
499
+ return Qnil;
500
+ }
501
+
502
+ /* *****************************************************************************
503
+ Initialize - Ruby Class Registration
504
+
505
+ Usage example:
506
+ m = Iodine::Base::MiniMap.new
507
+ 10.times {|i| m[i] = i }
508
+ m.each {|k,v| puts "#{k.to_s} => #{v.to_s}"}
509
+
510
+ Benchmark:
511
+ require 'iodine/benchmark'
512
+ Iodine::Benchmark.minimap(100)
513
+ ***************************************************************************** */
514
+
515
+ /**
516
+ * Initializes the Iodine::Base::MiniMap Ruby class.
517
+ *
518
+ * Defines the class under Iodine::Base with:
519
+ * - [] / []= for get/set operations
520
+ * - count, capa, clear, reserve for map management
521
+ * - each for iteration
522
+ * - to_s / to_json for JSON serialization
523
+ * - is? for type checking
524
+ * - cbench class method for C-level benchmarking
525
+ */
526
+ static void Init_Iodine_MiniMap(void) {
527
+ VALUE m = iodine_rb_IODINE_BASE_MINIMAP =
528
+ rb_define_class_under(iodine_rb_IODINE_BASE, "MiniMap", rb_cObject);
529
+ STORE.hold(iodine_rb_IODINE_BASE_MINIMAP);
530
+ rb_define_alloc_func(m, iodine_minimap_alloc);
531
+ rb_define_method(m, "[]", iodine_minimap_get, 1);
532
+ rb_define_method(m, "[]=", iodine_minimap_set, 2);
533
+ rb_define_method(m, "clear", iodine_minimap_clear, 0);
534
+ rb_define_method(m, "count", iodine_minimap_count, 0);
535
+ rb_define_method(m, "capa", iodine_minimap_capa, 0);
536
+ rb_define_method(m, "each", iodine_minimap_each, 0);
537
+ rb_define_method(m, "reserve", iodine_minimap_reserve, 1);
538
+ rb_define_method(m, "to_s", iodine_minimap_to_s, -1);
539
+ rb_define_method(m, "to_json", iodine_minimap_to_s, -1);
540
+ rb_define_method(m, "is?", iodine_is_minimap_rb, 0);
541
+ rb_define_singleton_method(m, "cbench", iodine_minimap_benchmark_c, 1);
542
+ }
543
+ #endif /* H___IODINE_MINIMAP___H */