uringmachine 0.10 → 0.11.1

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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/examples/bm_http_parse.rb +108 -35
  4. data/examples/bm_side_running.rb +83 -0
  5. data/examples/bm_sqlite.rb +1 -1
  6. data/ext/um/um.c +17 -1
  7. data/ext/um/um.h +30 -0
  8. data/ext/um/um_ext.c +2 -0
  9. data/ext/um/um_stream.c +372 -0
  10. data/ext/um/um_stream_class.c +121 -0
  11. data/lib/uringmachine/version.rb +1 -1
  12. data/lib/uringmachine.rb +20 -16
  13. data/test/test_stream.rb +133 -0
  14. data/test/test_um.rb +63 -0
  15. data/uringmachine.gemspec +1 -0
  16. data/vendor/liburing/.github/workflows/{build.yml → ci.yml} +107 -42
  17. data/vendor/liburing/.gitignore +1 -0
  18. data/vendor/liburing/CHANGELOG +10 -0
  19. data/vendor/liburing/README +5 -0
  20. data/vendor/liburing/configure +1 -1
  21. data/vendor/liburing/examples/Makefile +1 -0
  22. data/vendor/liburing/examples/helpers.c +25 -0
  23. data/vendor/liburing/examples/helpers.h +13 -0
  24. data/vendor/liburing/examples/io_uring-test.c +3 -0
  25. data/vendor/liburing/examples/proxy.c +1 -1
  26. data/vendor/liburing/examples/reg-wait.c +41 -6
  27. data/vendor/liburing/examples/send-zerocopy.c +79 -32
  28. data/vendor/liburing/examples/zcrx.c +436 -0
  29. data/vendor/liburing/liburing.spec +1 -1
  30. data/vendor/liburing/src/Makefile +0 -1
  31. data/vendor/liburing/src/arch/generic/syscall.h +2 -2
  32. data/vendor/liburing/src/arch/syscall-defs.h +2 -2
  33. data/vendor/liburing/src/include/liburing/io_uring.h +101 -17
  34. data/vendor/liburing/src/include/liburing.h +179 -59
  35. data/vendor/liburing/src/int_flags.h +4 -1
  36. data/vendor/liburing/src/liburing-ffi.map +14 -2
  37. data/vendor/liburing/src/liburing.map +9 -2
  38. data/vendor/liburing/src/queue.c +35 -30
  39. data/vendor/liburing/src/register.c +46 -15
  40. data/vendor/liburing/src/sanitize.c +6 -9
  41. data/vendor/liburing/src/setup.c +37 -71
  42. data/vendor/liburing/src/syscall.c +2 -2
  43. data/vendor/liburing/test/232c93d07b74.c +1 -0
  44. data/vendor/liburing/test/Makefile +9 -0
  45. data/vendor/liburing/test/accept-test.c +1 -0
  46. data/vendor/liburing/test/cmd-discard.c +16 -8
  47. data/vendor/liburing/test/connect.c +11 -7
  48. data/vendor/liburing/test/epwait.c +420 -0
  49. data/vendor/liburing/test/eventfd-ring.c +30 -5
  50. data/vendor/liburing/test/fallocate.c +1 -1
  51. data/vendor/liburing/test/fixed-hugepage.c +10 -7
  52. data/vendor/liburing/test/fixed-seg.c +187 -0
  53. data/vendor/liburing/test/helpers.c +121 -0
  54. data/vendor/liburing/test/helpers.h +13 -0
  55. data/vendor/liburing/test/init-mem.c +2 -0
  56. data/vendor/liburing/test/io_uring_passthrough.c +78 -62
  57. data/vendor/liburing/test/iopoll-overflow.c +5 -4
  58. data/vendor/liburing/test/iopoll.c +20 -10
  59. data/vendor/liburing/test/iowait.c +141 -0
  60. data/vendor/liburing/test/nvme.h +2 -0
  61. data/vendor/liburing/test/pipe-bug.c +11 -5
  62. data/vendor/liburing/test/pipe-eof.c +11 -1
  63. data/vendor/liburing/test/read-inc-file.c +150 -0
  64. data/vendor/liburing/test/read-write.c +21 -14
  65. data/vendor/liburing/test/recv-bundle-short-ooo.c +435 -0
  66. data/vendor/liburing/test/recv-multishot.c +2 -2
  67. data/vendor/liburing/test/reg-wait.c +449 -120
  68. data/vendor/liburing/test/regbuf-clone.c +53 -0
  69. data/vendor/liburing/test/resize-rings.c +25 -2
  70. data/vendor/liburing/test/rsrc_tags.c +67 -14
  71. data/vendor/liburing/test/send-zerocopy.c +52 -130
  72. data/vendor/liburing/test/sendmsg_iov_clean.c +216 -0
  73. data/vendor/liburing/test/socket-nb.c +158 -0
  74. data/vendor/liburing/test/sqwait.c +9 -11
  75. data/vendor/liburing/test/timeout.c +198 -0
  76. data/vendor/liburing/test/vec-regbuf.c +609 -0
  77. data/vendor/liburing/test/wait-timeout.c +1 -1
  78. data/vendor/liburing/test/wq-aff.c +5 -1
  79. data/vendor/liburing/test/zcrx.c +928 -0
  80. metadata +30 -4
  81. data/vendor/liburing/.github/workflows/codespell.yml +0 -25
  82. data/vendor/liburing/.github/workflows/shellcheck.yml +0 -20
@@ -0,0 +1,372 @@
1
+ #include "um.h"
2
+
3
+ VALUE stream_get_line(struct um_stream *stream) {
4
+ char *start = RSTRING_PTR(stream->buffer) + stream->pos;
5
+ while (true) {
6
+ char * lf_ptr = memchr(start, '\n', stream->len - stream->pos);
7
+ if (lf_ptr) {
8
+ ulong len = lf_ptr - start;
9
+ if (len && (start[len - 1] == '\r')) len -= 1;
10
+
11
+ VALUE str = rb_str_new(start, len);
12
+ stream->pos += lf_ptr - start + 1;
13
+ return str;
14
+ }
15
+
16
+ if (!stream_read_more(stream)) return Qnil;
17
+ }
18
+ }
19
+
20
+ VALUE stream_get_string(struct um_stream *stream, ulong len) {
21
+ while (stream->len - stream->pos < len)
22
+ if (!stream_read_more(stream)) return Qnil;
23
+
24
+ char *start = RSTRING_PTR(stream->buffer) + stream->pos;
25
+ VALUE str = rb_utf8_str_new(start, len);
26
+ stream->pos += len;
27
+ return str;
28
+ }
29
+
30
+ static inline void stream_check_truncate_buffer(struct um_stream *stream) {
31
+ if ((stream->pos == stream->len) && (stream->len >= 1 << 12)) {
32
+ rb_str_modify(stream->buffer);
33
+ rb_str_set_len(stream->buffer, 0);
34
+ stream->len = 0;
35
+ stream->pos = 0;
36
+ }
37
+ else if (stream->pos >= 1 << 12) {
38
+ rb_str_modify(stream->buffer);
39
+ char *base = RSTRING_PTR(stream->buffer);
40
+ int len_rest = stream->len - stream->pos;
41
+ memmove(base, base + stream->pos, len_rest);
42
+ rb_str_set_len(stream->buffer, len_rest);
43
+ stream->len = len_rest;
44
+ stream->pos = 0;
45
+ }
46
+ }
47
+
48
+ int stream_read_more(struct um_stream *stream) {
49
+ stream_check_truncate_buffer(stream);
50
+
51
+ size_t maxlen = 1 << 12;
52
+ size_t capa = rb_str_capacity(stream->buffer);
53
+ if (capa - stream->pos < maxlen)
54
+ rb_str_modify_expand(stream->buffer, maxlen - (capa - stream->pos));
55
+
56
+ rb_str_modify(stream->buffer);
57
+ char *ptr = RSTRING_PTR(stream->buffer) + stream->pos;
58
+ size_t ret = um_read_raw(stream->machine, stream->fd, ptr, maxlen);
59
+
60
+ if (ret == 0) {
61
+ stream->eof = 1;
62
+ return 0;
63
+ }
64
+
65
+ stream->len = stream->pos + ret;
66
+ rb_str_set_len(stream->buffer, stream->len);
67
+ return 1;
68
+ }
69
+
70
+ // ensure string can hold at least len bytes
71
+ static inline void str_expand(VALUE str, size_t len) {
72
+ size_t capa = rb_str_capacity(str);
73
+ if (capa < len + 1) rb_str_modify_expand(str, len + 1 - capa);
74
+ }
75
+
76
+ VALUE resp_get_line(struct um_stream *stream, VALUE out_buffer) {
77
+ char *start = RSTRING_PTR(stream->buffer) + stream->pos;
78
+ while (true) {
79
+ char * lf_ptr = memchr(start, '\r', stream->len - stream->pos);
80
+ if (lf_ptr) {
81
+ ulong len = lf_ptr - start;
82
+ stream->pos += len + 2;
83
+
84
+ if (NIL_P(out_buffer)) {
85
+ VALUE str = rb_str_new(start, len + 1);
86
+ rb_str_set_len(str, len);
87
+ RSTRING_PTR(str)[len] = 0;
88
+ RB_GC_GUARD(str);
89
+ return str;
90
+ }
91
+
92
+ str_expand(out_buffer, len + 1);
93
+ char *dest_ptr = RSTRING_PTR(out_buffer);
94
+ memcpy(dest_ptr, start, len);
95
+ dest_ptr[len] = 0; // add null at end
96
+ rb_str_set_len(out_buffer, len);
97
+ return out_buffer;
98
+ }
99
+
100
+ if (stream_read_more(stream))
101
+ // buffer ptr and pos may have changed after reading
102
+ start = RSTRING_PTR(stream->buffer) + stream->pos;
103
+ else
104
+ return Qnil;
105
+ }
106
+ }
107
+
108
+ VALUE resp_get_string(struct um_stream *stream, ulong len, VALUE out_buffer) {
109
+ ulong read_len = len + 2;
110
+
111
+ while (stream->len - stream->pos < read_len)
112
+ if (!stream_read_more(stream)) return Qnil;
113
+
114
+ char *start = RSTRING_PTR(stream->buffer) + stream->pos;
115
+ stream->pos += read_len;
116
+
117
+ if (NIL_P(out_buffer)) return rb_utf8_str_new(start, len);
118
+
119
+ str_expand(out_buffer, len + 1);
120
+ char *dest_ptr = RSTRING_PTR(out_buffer);
121
+ memcpy(dest_ptr, start, len);
122
+ dest_ptr[len] = 0; // add null at end
123
+ rb_str_set_len(out_buffer, len);
124
+ return out_buffer;
125
+ }
126
+
127
+ inline ulong resp_parse_length_field(const char *ptr, int len) {
128
+ return strtoul(ptr + 1, NULL, 10);
129
+ }
130
+
131
+ VALUE resp_decode_hash(struct um_stream *stream, VALUE out_buffer, ulong len) {
132
+ VALUE hash = rb_hash_new();
133
+
134
+ for (ulong i = 0; i < len; i++) {
135
+ VALUE key = resp_decode(stream, out_buffer);
136
+ VALUE value = resp_decode(stream, out_buffer);
137
+ rb_hash_aset(hash, key, value);
138
+ RB_GC_GUARD(key);
139
+ RB_GC_GUARD(value);
140
+ }
141
+
142
+ RB_GC_GUARD(hash);
143
+ return hash;
144
+ }
145
+
146
+ VALUE resp_decode_array(struct um_stream *stream, VALUE out_buffer, ulong len) {
147
+ VALUE array = rb_ary_new2(len);
148
+
149
+ for (ulong i = 0; i < len; i++) {
150
+ VALUE value = resp_decode(stream, out_buffer);
151
+ rb_ary_push(array, value);
152
+ RB_GC_GUARD(value);
153
+ }
154
+
155
+ RB_GC_GUARD(array);
156
+ return array;
157
+ }
158
+
159
+ static inline VALUE resp_decode_simple_string(char *ptr, ulong len) {
160
+ return rb_str_new(ptr + 1, len - 1);
161
+ }
162
+
163
+ static inline VALUE resp_decode_string(struct um_stream *stream, VALUE out_buffer, ulong len) {
164
+ return resp_get_string(stream, len, out_buffer);
165
+ }
166
+
167
+ static inline VALUE resp_decode_string_with_encoding(struct um_stream *stream, VALUE out_buffer, ulong len) {
168
+ VALUE with_enc = resp_get_string(stream, len, out_buffer);
169
+ char *ptr = RSTRING_PTR(with_enc);
170
+ len = RSTRING_LEN(with_enc);
171
+ if ((len < 4) || (ptr[3] != ':')) return Qnil;
172
+
173
+ return rb_utf8_str_new(ptr + 4, len - 4);
174
+ }
175
+
176
+ static inline VALUE resp_decode_integer(char *ptr) {
177
+ long value = strtol(ptr + 1, NULL, 10);
178
+ return LONG2NUM(value);
179
+ }
180
+
181
+ static inline VALUE resp_decode_float(char *ptr) {
182
+ double value = strtod(ptr + 1, NULL);
183
+ return DBL2NUM(value);
184
+ }
185
+
186
+ static inline VALUE resp_decode_simple_error(char *ptr, ulong len) {
187
+ static ID ID_new = 0;
188
+ if (!ID_new) ID_new = rb_intern("new");
189
+
190
+ VALUE msg = rb_str_new(ptr + 1, len - 1);
191
+ VALUE err = rb_funcall(rb_eRuntimeError, ID_new, 1, msg);
192
+ RB_GC_GUARD(msg);
193
+ return err;
194
+ }
195
+
196
+ static inline VALUE resp_decode_error(struct um_stream *stream, VALUE out_buffer, ulong len) {
197
+ static ID ID_new = 0;
198
+ if (!ID_new) ID_new = rb_intern("new");
199
+
200
+ VALUE msg = resp_decode_string(stream, out_buffer, len);
201
+ VALUE err = rb_funcall(rb_eRuntimeError, ID_new, 1, msg);
202
+ RB_GC_GUARD(msg);
203
+ return err;
204
+ }
205
+
206
+ VALUE resp_decode(struct um_stream *stream, VALUE out_buffer) {
207
+ VALUE msg = resp_get_line(stream, out_buffer);
208
+ if (msg == Qnil) return Qnil;
209
+
210
+ char *ptr = RSTRING_PTR(msg);
211
+ ulong len = RSTRING_LEN(msg);
212
+ ulong data_len;
213
+ if (len == 0) return Qnil;
214
+
215
+ switch (ptr[0]) {
216
+ case '%': // hash
217
+ case '|': // attributes hash
218
+ data_len = resp_parse_length_field(ptr, len);
219
+ return resp_decode_hash(stream, out_buffer, data_len);
220
+
221
+ case '*': // array
222
+ case '~': // set
223
+ case '>': // pub/sub push
224
+ data_len = resp_parse_length_field(ptr, len);
225
+ return resp_decode_array(stream, out_buffer, data_len);
226
+
227
+ case '+': // simple string
228
+ return resp_decode_simple_string(ptr, len);
229
+ case '$': // string
230
+ data_len = resp_parse_length_field(ptr, len);
231
+ return resp_decode_string(stream, out_buffer, data_len);
232
+ case '=': // string with encoding
233
+ data_len = resp_parse_length_field(ptr, len);
234
+ return resp_decode_string_with_encoding(stream, out_buffer, data_len);
235
+
236
+ case '_': // null
237
+ return Qnil;
238
+ case '#': // boolean
239
+ return (len > 1) && (ptr[1] == 't') ? Qtrue : Qfalse;
240
+
241
+ case ':': // integer
242
+ return resp_decode_integer(ptr);
243
+ case '(': // big integer
244
+ rb_raise(rb_eRuntimeError, "Big integers are not supported");
245
+ case ',': // float
246
+ return resp_decode_float(ptr);
247
+
248
+ case '-': // simple error
249
+ return resp_decode_simple_error(ptr, len);
250
+ case '!': // error
251
+ data_len = resp_parse_length_field(ptr, len);
252
+ return resp_decode_error(stream, out_buffer, data_len);
253
+ default:
254
+ rb_raise(rb_eRuntimeError, "Invalid character encountered");
255
+ }
256
+
257
+ RB_GC_GUARD(msg);
258
+ }
259
+
260
+ void write_buffer_init(struct um_write_buffer *buf, VALUE str) {
261
+ size_t capa = 1 << 12;
262
+ size_t len = RSTRING_LEN(str);
263
+ while (capa < len) capa += 1 << 12;
264
+
265
+ rb_str_resize(str, capa);
266
+ rb_str_set_len(str, len);
267
+ buf->str = str;
268
+ buf->capa = capa;
269
+ buf->len = len;
270
+ buf->ptr = RSTRING_PTR(str);
271
+ }
272
+
273
+ static inline void write_buffer_expand(struct um_write_buffer *buf, size_t newsize) {
274
+ if (buf->capa < newsize) {
275
+ size_t old_capa = buf->capa;
276
+ while (buf->capa < newsize) buf->capa += 1 << 12;
277
+ rb_str_modify_expand(buf->str, buf->capa - old_capa);
278
+ buf->ptr = RSTRING_PTR(buf->str);
279
+ }
280
+ }
281
+
282
+ static inline void write_buffer_append(struct um_write_buffer *buf, const char *ptr, size_t len) {
283
+ size_t total_len = buf->len + len;
284
+ write_buffer_expand(buf, total_len);
285
+
286
+ memcpy(buf->ptr + buf->len, ptr, len);
287
+ buf->len = total_len;
288
+ }
289
+
290
+ static inline void write_buffer_append_cstr(struct um_write_buffer *buf, const char *str) {
291
+ write_buffer_append(buf, str, strlen(str));
292
+ }
293
+
294
+ static inline void write_buffer_append_resp_bulk_string(struct um_write_buffer *buf, VALUE str) {
295
+ // leave enough place for prefix and postfix
296
+ size_t str_len = RSTRING_LEN(str);
297
+ size_t total_len = buf->len + str_len + 16;
298
+ write_buffer_expand(buf, total_len);
299
+
300
+
301
+ int prefix_len = sprintf(buf->ptr + buf->len, "$%ld\r\n", str_len);
302
+ const char *src = RSTRING_PTR(str);
303
+ memcpy(buf->ptr + buf->len + prefix_len, src, str_len);
304
+ buf->ptr[buf->len + prefix_len + str_len + 0] = '\r';
305
+ buf->ptr[buf->len + prefix_len + str_len + 1] = '\n';
306
+ buf->len += prefix_len + str_len + 2;
307
+ }
308
+
309
+ inline void write_buffer_update_len(struct um_write_buffer *buf) {
310
+ rb_str_set_len(buf->str, buf->len);
311
+ }
312
+
313
+ struct resp_encode_hash_ctx {
314
+ struct um_write_buffer *buf;
315
+ VALUE obj;
316
+ };
317
+
318
+ int resp_encode_hash_entry(VALUE key, VALUE value, VALUE arg) {
319
+ struct resp_encode_hash_ctx *ctx = (struct resp_encode_hash_ctx *)arg;
320
+
321
+ resp_encode(ctx->buf, key);
322
+ resp_encode(ctx->buf, value);
323
+ return 0;
324
+ }
325
+
326
+ void resp_encode(struct um_write_buffer *buf, VALUE obj) {
327
+ char tmp[60];
328
+
329
+ switch (TYPE(obj)) {
330
+ case T_NIL:
331
+ return write_buffer_append_cstr(buf, "_\r\n");
332
+ return;
333
+ case T_FALSE:
334
+ write_buffer_append_cstr(buf, "#f\r\n");
335
+ return;
336
+ case T_TRUE:
337
+ write_buffer_append_cstr(buf, "#t\r\n");
338
+ return;
339
+ case T_FIXNUM:
340
+ sprintf(tmp, ":%ld\r\n", NUM2LONG(obj));
341
+ write_buffer_append_cstr(buf, tmp);
342
+ return;
343
+ case T_FLOAT:
344
+ sprintf(tmp, ",%lg\r\n", NUM2DBL(obj));
345
+ write_buffer_append_cstr(buf, tmp);
346
+ return;
347
+ case T_STRING:
348
+ write_buffer_append_resp_bulk_string(buf, obj);
349
+ return;
350
+ case T_ARRAY:
351
+ {
352
+ ulong len = RARRAY_LEN(obj);
353
+ sprintf(tmp, "*%ld\r\n", len);
354
+ write_buffer_append_cstr(buf, tmp);
355
+ for (ulong i = 0; i < len; i++)
356
+ resp_encode(buf, rb_ary_entry(obj, i));
357
+ return;
358
+ }
359
+ case T_HASH:
360
+ {
361
+ ulong len = rb_hash_size_num(obj);
362
+ sprintf(tmp, "%%%ld\r\n", len);
363
+ write_buffer_append_cstr(buf, tmp);
364
+
365
+ struct resp_encode_hash_ctx ctx = { buf, obj };
366
+ rb_hash_foreach(obj, resp_encode_hash_entry, (VALUE)&ctx);
367
+ return;
368
+ }
369
+ default:
370
+ rb_raise(rb_eRuntimeError, "Can't encode object");
371
+ }
372
+ }
@@ -0,0 +1,121 @@
1
+ #include "um.h"
2
+
3
+ VALUE cStream;
4
+
5
+ static void Stream_mark(void *ptr) {
6
+ struct um_stream *stream = ptr;
7
+ rb_gc_mark_movable(stream->self);
8
+ rb_gc_mark_movable(stream->buffer);
9
+ }
10
+
11
+ static void Stream_compact(void *ptr) {
12
+ struct um_stream *stream = ptr;
13
+ stream->self = rb_gc_location(stream->self);
14
+ stream->buffer = rb_gc_location(stream->buffer);
15
+ }
16
+
17
+ static void Stream_free(void *ptr) {
18
+ free(ptr);
19
+ }
20
+
21
+ static size_t Stream_size(const void *ptr) {
22
+ return sizeof(struct um_stream);
23
+ }
24
+
25
+ static const rb_data_type_t Stream_type = {
26
+ "UringMachine::Stream",
27
+ {Stream_mark, Stream_free, Stream_size, Stream_compact},
28
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
29
+ };
30
+
31
+ static VALUE Stream_allocate(VALUE klass) {
32
+ struct um_stream *stream = ALLOC(struct um_stream);
33
+
34
+ return TypedData_Wrap_Struct(klass, &Stream_type, stream);
35
+ }
36
+
37
+ VALUE Stream_initialize(VALUE self, VALUE machine, VALUE fd) {
38
+ struct um_stream *stream = RTYPEDDATA_DATA(self);
39
+
40
+ stream->self = self;
41
+
42
+ stream->machine = RTYPEDDATA_DATA(machine);
43
+ stream->fd = NUM2ULONG(fd);
44
+ stream->buffer = rb_utf8_str_new_literal("");
45
+ rb_str_resize(stream->buffer, 1 << 16); // 64KB
46
+ rb_str_set_len(stream->buffer, 0);
47
+
48
+ stream->len = 0;
49
+ stream->pos = 0;
50
+ stream->eof = 0;
51
+
52
+ return self;
53
+ }
54
+
55
+ VALUE Stream_get_line(VALUE self) {
56
+ struct um_stream *stream = RTYPEDDATA_DATA(self);
57
+ if (unlikely(stream->eof)) return Qnil;
58
+
59
+ return stream_get_line(stream);
60
+ }
61
+
62
+ VALUE Stream_get_string(VALUE self, VALUE len) {
63
+ struct um_stream *stream = RTYPEDDATA_DATA(self);
64
+ if (unlikely(stream->eof)) return Qnil;
65
+
66
+ return stream_get_string(stream, NUM2ULONG(len));
67
+ }
68
+
69
+ VALUE Stream_resp_get_line(VALUE self) {
70
+ struct um_stream *stream = RTYPEDDATA_DATA(self);
71
+ if (unlikely(stream->eof)) return Qnil;
72
+
73
+ VALUE line = resp_get_line(stream, Qnil);
74
+ RB_GC_GUARD(line);
75
+ return line;
76
+ }
77
+
78
+ VALUE Stream_resp_get_string(VALUE self, VALUE len) {
79
+ struct um_stream *stream = RTYPEDDATA_DATA(self);
80
+ if (unlikely(stream->eof)) return Qnil;
81
+
82
+ VALUE str = resp_get_string(stream, NUM2ULONG(len), Qnil);
83
+ RB_GC_GUARD(str);
84
+ return str;
85
+ }
86
+
87
+ VALUE Stream_resp_decode(VALUE self) {
88
+ struct um_stream *stream = RTYPEDDATA_DATA(self);
89
+ if (unlikely(stream->eof)) return Qnil;
90
+
91
+ VALUE out_buffer = rb_utf8_str_new_literal("");
92
+ VALUE obj = resp_decode(stream, out_buffer);
93
+ RB_GC_GUARD(out_buffer);
94
+ return obj;
95
+ }
96
+
97
+ VALUE Stream_resp_encode(VALUE self, VALUE str, VALUE obj) {
98
+ struct um_write_buffer buf;
99
+ write_buffer_init(&buf, str);
100
+ rb_str_modify(str);
101
+ resp_encode(&buf, obj);
102
+ write_buffer_update_len(&buf);
103
+ return str;
104
+ }
105
+
106
+ void Init_Stream(void) {
107
+ VALUE cStream = rb_define_class_under(cUM, "Stream", rb_cObject);
108
+ rb_define_alloc_func(cStream, Stream_allocate);
109
+
110
+ rb_define_method(cStream, "initialize", Stream_initialize, 2);
111
+
112
+ rb_define_method(cStream, "get_line", Stream_get_line, 0);
113
+ rb_define_method(cStream, "get_string", Stream_get_string, 1);
114
+
115
+ rb_define_method(cStream, "resp_get_line", Stream_resp_get_line, 0);
116
+ rb_define_method(cStream, "resp_get_string", Stream_resp_get_string, 1);
117
+
118
+ rb_define_method(cStream, "resp_decode", Stream_resp_decode, 0);
119
+
120
+ rb_define_singleton_method(cStream, "resp_encode", Stream_resp_encode, 2);
121
+ }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class UringMachine
4
- VERSION = '0.10'
4
+ VERSION = '0.11.1'
5
5
  end
data/lib/uringmachine.rb CHANGED
@@ -15,22 +15,11 @@ class UringMachine
15
15
  class Terminate < Exception
16
16
  end
17
17
 
18
- def spin(value = nil, fiber_class = Fiber, &block)
19
- f = fiber_class.new do |resume_value|
20
- f.set_result block.(resume_value)
21
- rescue Exception => e
22
- f.set_result e
23
- ensure
24
- f.mark_as_done
25
- # cleanup
26
- @@fiber_map.delete(f)
27
- self.notify_done_listeners(f)
28
- # transfer control to other fibers
29
- self.yield
30
- end
31
- self.schedule(f, value)
32
- @@fiber_map[f] = true
33
- f
18
+ def spin(value = nil, klass = Fiber, &block)
19
+ fiber = klass.new { |v| run_block_in_fiber(block, fiber, v) }
20
+ self.schedule(fiber, value)
21
+
22
+ @@fiber_map[fiber] = fiber
34
23
  end
35
24
 
36
25
  def join(*fibers)
@@ -63,6 +52,21 @@ class UringMachine
63
52
 
64
53
  private
65
54
 
55
+ def run_block_in_fiber(block, fiber, value)
56
+ ret = block.(value)
57
+ fiber.set_result(ret)
58
+ rescue Exception => e
59
+ fiber.set_result(e)
60
+ ensure
61
+ fiber.mark_as_done
62
+ # cleanup
63
+ @@fiber_map.delete(fiber)
64
+ self.notify_done_listeners(fiber)
65
+
66
+ # transfer control to UM scheduler
67
+ self.yield
68
+ end
69
+
66
70
  def notify_done_listeners(fiber)
67
71
  listeners = fiber.done_listeners
68
72
  return if !listeners
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helper'
4
+
5
+ class StreamBaseTest < UMBaseTest
6
+ def setup
7
+ super
8
+ @rfd, @wfd = UM.pipe
9
+ @stream = UM::Stream.new(@machine, @rfd)
10
+ end
11
+ end
12
+
13
+ class StreamTest < StreamBaseTest
14
+ def test_get_line
15
+ machine.write(@wfd, "foo\nbar\r\nbaz")
16
+ machine.close(@wfd)
17
+
18
+ assert_equal 'foo', @stream.get_line
19
+ assert_equal 'bar', @stream.get_line
20
+ assert_nil @stream.get_line
21
+ end
22
+
23
+ def test_get_string
24
+ machine.write(@wfd, "foobarbazblahzzz")
25
+ machine.close(@wfd)
26
+
27
+ assert_equal 'foobar', @stream.get_string(6)
28
+ assert_equal 'baz', @stream.get_string(3)
29
+ assert_equal 'blah', @stream.get_string(4)
30
+ assert_nil @stream.get_string(4)
31
+ end
32
+ end
33
+
34
+ class StreamRespTest < StreamBaseTest
35
+ def test_trdp_get_line
36
+ machine.write(@wfd, "foo\r\nbarbar\r\nbaz\n")
37
+ machine.close(@wfd)
38
+
39
+ assert_equal 'foo', @stream.resp_get_line
40
+ assert_equal 'barbar', @stream.resp_get_line
41
+ assert_nil @stream.resp_get_line
42
+ end
43
+
44
+ def test_resp_get_string
45
+ machine.write(@wfd, "foo\r\nbarbar\r\nbaz\n")
46
+ machine.close(@wfd)
47
+
48
+ assert_equal 'foo', @stream.resp_get_string(3)
49
+ assert_equal 'barbar', @stream.resp_get_string(6)
50
+ assert_nil @stream.resp_get_string(3)
51
+ end
52
+
53
+ def test_resp_decode
54
+ machine.write(@wfd, "+foo bar\r\n")
55
+ assert_equal "foo bar", @stream.resp_decode
56
+
57
+ machine.write(@wfd, "+baz\r\n")
58
+ assert_equal "baz", @stream.resp_decode
59
+
60
+ machine.write(@wfd, "-foobar\r\n")
61
+ o = @stream.resp_decode
62
+ assert_kind_of RuntimeError, o
63
+ assert_equal "foobar", o.message
64
+
65
+ machine.write(@wfd, "!3\r\nbaz\r\n")
66
+ o = @stream.resp_decode
67
+ assert_kind_of RuntimeError, o
68
+ assert_equal "baz", o.message
69
+
70
+ machine.write(@wfd, ":123\r\n")
71
+ assert_equal 123, @stream.resp_decode
72
+
73
+ machine.write(@wfd, ":-123\r\n")
74
+ assert_equal(-123, @stream.resp_decode)
75
+
76
+ machine.write(@wfd, ",123.321\r\n")
77
+ assert_equal 123.321, @stream.resp_decode
78
+
79
+ machine.write(@wfd, "_\r\n")
80
+ assert_nil @stream.resp_decode
81
+
82
+ machine.write(@wfd, "#t\r\n")
83
+ assert_equal true, @stream.resp_decode
84
+
85
+ machine.write(@wfd, "#f\r\n")
86
+ assert_equal false, @stream.resp_decode
87
+
88
+ machine.write(@wfd, "$6\r\nfoobar\r\n")
89
+ assert_equal "foobar", @stream.resp_decode
90
+
91
+ machine.write(@wfd, "$3\r\nbaz\r\n")
92
+ assert_equal "baz", @stream.resp_decode
93
+
94
+ machine.write(@wfd, "=10\r\ntxt:foobar\r\n")
95
+ assert_equal "foobar", @stream.resp_decode
96
+
97
+ machine.write(@wfd, "*3\r\n+foo\r\n:42\r\n$3\r\nbar\r\n")
98
+ assert_equal ['foo', 42, 'bar'], @stream.resp_decode
99
+
100
+ machine.write(@wfd, "~3\r\n+foo\r\n:42\r\n$3\r\nbar\r\n")
101
+ assert_equal ['foo', 42, 'bar'], @stream.resp_decode
102
+
103
+ machine.write(@wfd, ">3\r\n+foo\r\n:42\r\n$3\r\nbar\r\n")
104
+ assert_equal ['foo', 42, 'bar'], @stream.resp_decode
105
+
106
+ machine.write(@wfd, "%2\r\n+a\r\n:42\r\n+b\r\n:43\r\n")
107
+ assert_equal({ 'a' => 42, 'b' => 43 }, @stream.resp_decode)
108
+
109
+ machine.write(@wfd, "|2\r\n+a\r\n:42\r\n+b\r\n:43\r\n")
110
+ assert_equal({ 'a' => 42, 'b' => 43 }, @stream.resp_decode)
111
+
112
+ machine.write(@wfd, "%2\r\n+a\r\n:42\r\n+b\r\n*3\r\n+foo\r\n+bar\r\n+baz\r\n")
113
+ assert_equal({ 'a' => 42, 'b' => ['foo', 'bar', 'baz'] }, @stream.resp_decode)
114
+
115
+ end
116
+
117
+ def test_resp_encode
118
+ s = UM::Stream
119
+ assert_equal "_\r\n", s.resp_encode(+'', nil)
120
+ assert_equal "#t\r\n", s.resp_encode(+'', true)
121
+ assert_equal "#f\r\n", s.resp_encode(+'', false)
122
+ assert_equal ":42\r\n", s.resp_encode(+'', 42)
123
+ assert_equal ",42.1\r\n", s.resp_encode(+'', 42.1)
124
+ assert_equal "$6\r\nfoobar\r\n", s.resp_encode(+'', 'foobar')
125
+ assert_equal "$10\r\nפובאר\r\n", s.resp_encode(+'', 'פובאר')
126
+
127
+ assert_equal "*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",
128
+ s.resp_encode(+'', ['foo', 'bar'])
129
+
130
+ assert_equal "%2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n$3\r\nbaz\r\n:42\r\n",
131
+ s.resp_encode(+'', { 'foo' => 'bar', 'baz' => 42 })
132
+ end
133
+ end