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.
- checksums.yaml +4 -4
- data/.clang-format +15 -0
- data/.rspec +1 -0
- data/.yardopts +1 -3
- data/CHANGELOG.md +132 -12
- data/Gemfile +5 -7
- data/LICENSE.txt +1 -1
- data/README.md +147 -671
- data/Rakefile +9 -16
- data/examples/chatroom.nru +9 -0
- data/examples/client +74 -0
- data/examples/config-hello.nru +9 -0
- data/examples/config-hello.ru +11 -0
- data/examples/config-hijack.ru +13 -0
- data/examples/config-shootout.ru +43 -0
- data/examples/config.nru +120 -0
- data/examples/config.rb +37 -0
- data/examples/config.ru +42 -53
- data/examples/fake +24 -0
- data/examples/hello_neorack +137 -0
- data/examples/routes.nru +29 -0
- data/examples/routes.ru +42 -0
- data/exe/iodine +218 -258
- data/ext/iodine/extconf.rb +27 -100
- data/ext/iodine/fio-stl.h +128336 -0
- data/ext/iodine/fio-stl.md +26396 -0
- data/ext/iodine/iodine.c +213 -1393
- data/ext/iodine/iodine.h +250 -55
- data/ext/iodine/iodine_arg_helper.h +329 -0
- data/ext/iodine/iodine_caller.h +234 -25
- data/ext/iodine/iodine_cli.h +434 -0
- data/ext/iodine/iodine_compression.h +312 -0
- data/ext/iodine/iodine_connection.h +3484 -34
- data/ext/iodine/iodine_core.h +363 -0
- data/ext/iodine/iodine_crypto.h +1441 -0
- data/ext/iodine/iodine_defer.h +225 -4
- data/ext/iodine/iodine_json.h +738 -4
- data/ext/iodine/iodine_listener.h +263 -0
- data/ext/iodine/iodine_log.h +297 -0
- data/ext/iodine/iodine_minimap.h +543 -0
- data/ext/iodine/iodine_mustache.h +461 -4
- data/ext/iodine/iodine_pubsub_eng.h +729 -0
- data/ext/iodine/iodine_pubsub_history.h +547 -0
- data/ext/iodine/iodine_pubsub_msg.h +228 -0
- data/ext/iodine/iodine_redis.h +322 -0
- data/ext/iodine/iodine_store.h +418 -16
- data/ext/iodine/iodine_threads.h +279 -0
- data/ext/iodine/iodine_tls.h +515 -6
- data/ext/iodine/iodine_utils.h +923 -0
- data/iodine.gemspec +37 -42
- data/lib/iodine/benchmark.rb +1690 -0
- data/lib/iodine/documentation.rb +3085 -0
- data/lib/iodine/version.rb +17 -1
- data/lib/iodine.rb +54 -255
- data/logo.svg +36 -0
- data/mustache.md +55 -0
- data/sig/iodine.rbs +4 -0
- metadata +58 -184
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
- data/.github/workflows/ruby.yml +0 -42
- data/.gitignore +0 -21
- data/LIMITS.md +0 -41
- data/SPEC-PubSub-Draft.md +0 -159
- data/SPEC-WebSocket-Draft.md +0 -239
- data/bin/console +0 -22
- data/bin/info.md +0 -353
- data/bin/mustache_bench.rb +0 -100
- data/bin/poc/Gemfile.lock +0 -23
- data/bin/poc/README.md +0 -37
- data/bin/poc/config.ru +0 -66
- data/bin/poc/gemfile +0 -1
- data/bin/poc/www/index.html +0 -57
- data/examples/async_task.ru +0 -92
- data/examples/bates/README.md +0 -3
- data/examples/bates/config.ru +0 -342
- data/examples/bates/david+bold.pdf +0 -0
- data/examples/bates/public/drop-pdf.png +0 -0
- data/examples/bates/public/index.html +0 -600
- data/examples/echo.ru +0 -59
- data/examples/etag.ru +0 -16
- data/examples/hello.ru +0 -29
- data/examples/pubsub_engine.ru +0 -81
- data/examples/rack3.ru +0 -12
- data/examples/redis.ru +0 -70
- data/examples/shootout.ru +0 -73
- data/examples/sub-protocols.ru +0 -90
- data/examples/tcp_client.rb +0 -66
- data/examples/x-sendfile.ru +0 -14
- data/ext/iodine/fio.c +0 -11995
- data/ext/iodine/fio.h +0 -6371
- data/ext/iodine/fio_cli.c +0 -431
- data/ext/iodine/fio_cli.h +0 -189
- data/ext/iodine/fio_json_parser.h +0 -688
- data/ext/iodine/fio_siphash.c +0 -157
- data/ext/iodine/fio_siphash.h +0 -37
- data/ext/iodine/fio_tls.h +0 -129
- data/ext/iodine/fio_tls_missing.c +0 -649
- data/ext/iodine/fio_tls_openssl.c +0 -1106
- data/ext/iodine/fio_tmpfile.h +0 -50
- data/ext/iodine/fiobj.h +0 -44
- data/ext/iodine/fiobj4fio.h +0 -21
- data/ext/iodine/fiobj_ary.c +0 -333
- data/ext/iodine/fiobj_ary.h +0 -139
- data/ext/iodine/fiobj_data.c +0 -1187
- data/ext/iodine/fiobj_data.h +0 -167
- data/ext/iodine/fiobj_hash.c +0 -409
- data/ext/iodine/fiobj_hash.h +0 -176
- data/ext/iodine/fiobj_json.c +0 -622
- data/ext/iodine/fiobj_json.h +0 -68
- data/ext/iodine/fiobj_mem.h +0 -71
- data/ext/iodine/fiobj_mustache.c +0 -317
- data/ext/iodine/fiobj_mustache.h +0 -62
- data/ext/iodine/fiobj_numbers.c +0 -344
- data/ext/iodine/fiobj_numbers.h +0 -127
- data/ext/iodine/fiobj_str.c +0 -433
- data/ext/iodine/fiobj_str.h +0 -172
- data/ext/iodine/fiobject.c +0 -620
- data/ext/iodine/fiobject.h +0 -655
- data/ext/iodine/hpack.h +0 -1923
- data/ext/iodine/http.c +0 -2743
- data/ext/iodine/http.h +0 -1002
- data/ext/iodine/http1.c +0 -910
- data/ext/iodine/http1.h +0 -29
- data/ext/iodine/http1_parser.h +0 -878
- data/ext/iodine/http_internal.c +0 -1279
- data/ext/iodine/http_internal.h +0 -237
- data/ext/iodine/http_mime_parser.h +0 -350
- data/ext/iodine/iodine_caller.c +0 -230
- data/ext/iodine/iodine_connection.c +0 -941
- data/ext/iodine/iodine_defer.c +0 -420
- data/ext/iodine/iodine_fiobj2rb.h +0 -120
- data/ext/iodine/iodine_helpers.c +0 -282
- data/ext/iodine/iodine_helpers.h +0 -12
- data/ext/iodine/iodine_http.c +0 -1181
- data/ext/iodine/iodine_http.h +0 -23
- data/ext/iodine/iodine_json.c +0 -302
- data/ext/iodine/iodine_mustache.c +0 -567
- data/ext/iodine/iodine_pubsub.c +0 -580
- data/ext/iodine/iodine_pubsub.h +0 -26
- data/ext/iodine/iodine_rack_io.c +0 -273
- data/ext/iodine/iodine_rack_io.h +0 -20
- data/ext/iodine/iodine_store.c +0 -148
- data/ext/iodine/iodine_tcp.c +0 -346
- data/ext/iodine/iodine_tcp.h +0 -13
- data/ext/iodine/iodine_tls.c +0 -261
- data/ext/iodine/mustache_parser.h +0 -1546
- data/ext/iodine/redis_engine.c +0 -957
- data/ext/iodine/redis_engine.h +0 -79
- data/ext/iodine/resp_parser.h +0 -317
- data/ext/iodine/websocket_parser.h +0 -506
- data/ext/iodine/websockets.c +0 -743
- data/ext/iodine/websockets.h +0 -185
- data/lib/iodine/connection.rb +0 -61
- data/lib/iodine/json.rb +0 -42
- data/lib/iodine/mustache.rb +0 -113
- data/lib/iodine/pubsub.rb +0 -55
- data/lib/iodine/rack_utils.rb +0 -43
- data/lib/iodine/tls.rb +0 -16
- data/lib/rack/handler/iodine.rb +0 -33
- 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 */
|