uringmachine 0.10 → 0.11

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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/examples/bm_side_running.rb +83 -0
  4. data/examples/bm_sqlite.rb +1 -1
  5. data/ext/um/um.c +17 -1
  6. data/ext/um/um.h +29 -0
  7. data/ext/um/um_ext.c +2 -0
  8. data/ext/um/um_stream.c +344 -0
  9. data/ext/um/um_stream_class.c +140 -0
  10. data/lib/uringmachine/version.rb +1 -1
  11. data/lib/uringmachine.rb +20 -16
  12. data/test/test_stream.rb +133 -0
  13. data/test/test_um.rb +63 -0
  14. data/vendor/liburing/.github/workflows/{build.yml → ci.yml} +107 -42
  15. data/vendor/liburing/.gitignore +1 -0
  16. data/vendor/liburing/CHANGELOG +10 -0
  17. data/vendor/liburing/README +5 -0
  18. data/vendor/liburing/configure +1 -1
  19. data/vendor/liburing/examples/Makefile +1 -0
  20. data/vendor/liburing/examples/helpers.c +25 -0
  21. data/vendor/liburing/examples/helpers.h +13 -0
  22. data/vendor/liburing/examples/io_uring-test.c +3 -0
  23. data/vendor/liburing/examples/proxy.c +1 -1
  24. data/vendor/liburing/examples/reg-wait.c +41 -6
  25. data/vendor/liburing/examples/send-zerocopy.c +79 -32
  26. data/vendor/liburing/examples/zcrx.c +436 -0
  27. data/vendor/liburing/liburing.spec +1 -1
  28. data/vendor/liburing/src/Makefile +0 -1
  29. data/vendor/liburing/src/arch/generic/syscall.h +2 -2
  30. data/vendor/liburing/src/arch/syscall-defs.h +2 -2
  31. data/vendor/liburing/src/include/liburing/io_uring.h +101 -17
  32. data/vendor/liburing/src/include/liburing.h +179 -59
  33. data/vendor/liburing/src/int_flags.h +4 -1
  34. data/vendor/liburing/src/liburing-ffi.map +14 -2
  35. data/vendor/liburing/src/liburing.map +9 -2
  36. data/vendor/liburing/src/queue.c +35 -30
  37. data/vendor/liburing/src/register.c +46 -15
  38. data/vendor/liburing/src/sanitize.c +6 -9
  39. data/vendor/liburing/src/setup.c +37 -71
  40. data/vendor/liburing/src/syscall.c +2 -2
  41. data/vendor/liburing/test/232c93d07b74.c +1 -0
  42. data/vendor/liburing/test/Makefile +9 -0
  43. data/vendor/liburing/test/accept-test.c +1 -0
  44. data/vendor/liburing/test/cmd-discard.c +16 -8
  45. data/vendor/liburing/test/connect.c +11 -7
  46. data/vendor/liburing/test/epwait.c +420 -0
  47. data/vendor/liburing/test/eventfd-ring.c +30 -5
  48. data/vendor/liburing/test/fallocate.c +1 -1
  49. data/vendor/liburing/test/fixed-hugepage.c +10 -7
  50. data/vendor/liburing/test/fixed-seg.c +187 -0
  51. data/vendor/liburing/test/helpers.c +121 -0
  52. data/vendor/liburing/test/helpers.h +13 -0
  53. data/vendor/liburing/test/init-mem.c +2 -0
  54. data/vendor/liburing/test/io_uring_passthrough.c +78 -62
  55. data/vendor/liburing/test/iopoll-overflow.c +5 -4
  56. data/vendor/liburing/test/iopoll.c +20 -10
  57. data/vendor/liburing/test/iowait.c +141 -0
  58. data/vendor/liburing/test/nvme.h +2 -0
  59. data/vendor/liburing/test/pipe-bug.c +11 -5
  60. data/vendor/liburing/test/pipe-eof.c +11 -1
  61. data/vendor/liburing/test/read-inc-file.c +150 -0
  62. data/vendor/liburing/test/read-write.c +21 -14
  63. data/vendor/liburing/test/recv-bundle-short-ooo.c +435 -0
  64. data/vendor/liburing/test/recv-multishot.c +2 -2
  65. data/vendor/liburing/test/reg-wait.c +449 -120
  66. data/vendor/liburing/test/regbuf-clone.c +53 -0
  67. data/vendor/liburing/test/resize-rings.c +25 -2
  68. data/vendor/liburing/test/rsrc_tags.c +67 -14
  69. data/vendor/liburing/test/send-zerocopy.c +52 -130
  70. data/vendor/liburing/test/sendmsg_iov_clean.c +216 -0
  71. data/vendor/liburing/test/socket-nb.c +158 -0
  72. data/vendor/liburing/test/sqwait.c +9 -11
  73. data/vendor/liburing/test/timeout.c +198 -0
  74. data/vendor/liburing/test/vec-regbuf.c +609 -0
  75. data/vendor/liburing/test/wait-timeout.c +1 -1
  76. data/vendor/liburing/test/wq-aff.c +5 -1
  77. data/vendor/liburing/test/zcrx.c +928 -0
  78. metadata +16 -4
  79. data/vendor/liburing/.github/workflows/codespell.yml +0 -25
  80. data/vendor/liburing/.github/workflows/shellcheck.yml +0 -20
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 85de1c3c67da5ef2a64b64a0370312208009683e9a42504bfbd7577bb6bee9f7
4
- data.tar.gz: 13a34495c1987c85891ee5db9c196fbf09b6ebcd9a2221026341023e63e5a8e8
3
+ metadata.gz: 8297cccb35ae1c8086889c2400db8bb884b0dfccdbef561caa0a4e9f80b21053
4
+ data.tar.gz: 990e84bb2f975de00739150cff966bbf97a551ba135714eb92bc3b86cd14b5d0
5
5
  SHA512:
6
- metadata.gz: 7b10f6a2c6e4d0bccaebb75c50e702ca0126eeb712a25ac32631b21ddcafd550dcceab2028a4f1a9b380538aa19067e0fa08346137a760307193af0acef64ed1
7
- data.tar.gz: 0dc3949e9256f139cc1caab14b2de6b63e16af61c569de59b761153d8d05a2030bad0e299ca9044452cc628fd25accff8fe3e89c59e6d4c053f049339ddce2c3
6
+ metadata.gz: f66149736c1cf58f8df65d387b81ff070fd890e99aa7c8aa806a5afcb04b8248b78c95c1673d30b7d3270c81e05bc0b84afdb025d2a0a2f02beca5977196b321
7
+ data.tar.gz: 7b6e6c31193da3a1e3e9a6fa627c84ae0f4d35c36f5fa636595acf1a013b71cd419073bb78be04130333796bed05e0ce9d1bfa74ab195608346cd689ecf9abaf
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ # 2025-06-02 Version 0.11
2
+
3
+ - Implement `UM::Stream` class for read streams
4
+
1
5
  # 2025-05-05 Version 0.10
2
6
 
3
7
  - Add `Thread#machine`
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/inline'
4
+
5
+ gemfile do
6
+ source 'https://rubygems.org'
7
+ gem 'uringmachine', path: '..'
8
+ gem 'benchmark-ips'
9
+ end
10
+
11
+ require 'benchmark/ips'
12
+ require 'uringmachine'
13
+
14
+ def consume_from_queue(queue)
15
+ m = UM.new
16
+ while true
17
+ response_mailbox, closure = m.shift(queue)
18
+ result = closure.call
19
+ m.push(response_mailbox, result)
20
+ end
21
+ # rescue UM::Terminate
22
+ # # We can also add a timeout here
23
+ # t0 = Time.now
24
+ # while !queue.empty? && (Time.now - t0) < 10
25
+ # response_mailbox, closure = m.shift(queue)
26
+ # result = closure.call
27
+ # m.push(response_mailbox, result)
28
+ # end
29
+ end
30
+
31
+ N = 8
32
+
33
+ $machine = UM.new
34
+ @queue = UM::Queue.new
35
+ @threads = N.times.map { Thread.new { consume_from_queue(@queue) } }
36
+
37
+ def side_run(&block)
38
+ mailbox = Fiber.current.mailbox
39
+ $machine.push(@queue, [mailbox, block])
40
+ Thread.pass
41
+ $machine.shift(mailbox)
42
+ end
43
+
44
+ @rqueue = Queue.new
45
+ @rthreads = N.times.map { Thread.new { r_consume_from_queue(@rqueue) }}
46
+
47
+ def r_consume_from_queue(queue)
48
+ m = UM.new
49
+ while true
50
+ response_mailbox, closure = @rqueue.shift
51
+ result = closure.call
52
+ m.push(response_mailbox, result)
53
+ end
54
+ # rescue UM::Terminate
55
+ # # We can also add a timeout here
56
+ # t0 = Time.now
57
+ # while !queue.empty? && (Time.now - t0) < 10
58
+ # response_mailbox, closure = m.shift(queue)
59
+ # result = closure.call
60
+ # m.push(response_mailbox, result)
61
+ # end
62
+ end
63
+
64
+ def r_side_run(&block)
65
+ mailbox = Fiber.current.mailbox
66
+ @rqueue.push([mailbox, block])
67
+ Thread.pass
68
+ $machine.shift(mailbox)
69
+ end
70
+
71
+ # puts '*' * 40
72
+ # p r_side_run { }
73
+ # exit!
74
+
75
+ Benchmark.ips do |x|
76
+ x.config(:time => 5, :warmup => 2)
77
+
78
+ x.report("side-run") { side_run { } }
79
+ x.report("r-side-run") { r_side_run { } }
80
+ # x.report("snoozing") { $machine.snooze }
81
+
82
+ x.compare!
83
+ end
@@ -49,7 +49,7 @@ class UM
49
49
  def spin_actor(target)
50
50
  f = UM::Actor.new(self, target)
51
51
  schedule(f, nil)
52
- @@fiber_map[f] = true
52
+ @@fiber_map[f] = f
53
53
  f
54
54
  end
55
55
  end
data/ext/um/um.c CHANGED
@@ -356,6 +356,22 @@ inline VALUE um_read(struct um *machine, int fd, VALUE buffer, int maxlen, int b
356
356
  return raise_if_exception(ret);
357
357
  }
358
358
 
359
+ inline size_t um_read_raw(struct um *machine, int fd, char *buffer, int maxlen) {
360
+ struct um_op op;
361
+ um_prep_op(machine, &op, OP_READ);
362
+ struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
363
+ io_uring_prep_read(sqe, fd, buffer, maxlen, -1);
364
+
365
+ VALUE ret = um_fiber_switch(machine);
366
+ if (um_check_completion(machine, &op)) {
367
+ return op.result.res;
368
+
369
+ }
370
+
371
+ raise_if_exception(ret);
372
+ return 0;
373
+ }
374
+
359
375
  VALUE um_write(struct um *machine, int fd, VALUE str, int len) {
360
376
  struct um_op op;
361
377
  um_prep_op(machine, &op, OP_WRITE);
@@ -575,7 +591,7 @@ VALUE um_waitpid(struct um *machine, int pid, int options) {
575
591
  struct io_uring_sqe *sqe = um_get_sqe(machine, &op);
576
592
 
577
593
  siginfo_t infop;
578
- io_uring_prep_waitid(sqe, P_PID, pid, &infop, options, 0);
594
+ io_uring_prep_waitid(sqe, pid == 0 ? P_ALL : P_PID, pid, &infop, options, 0);
579
595
 
580
596
  VALUE ret = um_fiber_switch(machine);
581
597
  if (um_check_completion(machine, &op))
data/ext/um/um.h CHANGED
@@ -153,6 +153,24 @@ struct um_async_op {
153
153
  struct um_op *op;
154
154
  };
155
155
 
156
+ struct um_stream {
157
+ VALUE self;
158
+
159
+ struct um *machine;
160
+ int fd;
161
+ VALUE buffer;
162
+ ulong len;
163
+ ulong pos;
164
+ int eof;
165
+ };
166
+
167
+ struct um_write_buffer {
168
+ VALUE str;
169
+ size_t capa;
170
+ size_t len;
171
+ char *ptr;
172
+ };
173
+
156
174
  extern VALUE cUM;
157
175
  extern VALUE cMutex;
158
176
  extern VALUE cQueue;
@@ -210,6 +228,7 @@ VALUE um_timeout(struct um *machine, VALUE interval, VALUE class);
210
228
  VALUE um_sleep(struct um *machine, double duration);
211
229
  VALUE um_periodically(struct um *machine, double interval);
212
230
  VALUE um_read(struct um *machine, int fd, VALUE buffer, int maxlen, int buffer_offset);
231
+ size_t um_read_raw(struct um *machine, int fd, char *buffer, int maxlen);
213
232
  VALUE um_read_each(struct um *machine, int fd, int bgid);
214
233
  VALUE um_write(struct um *machine, int fd, VALUE str, int len);
215
234
  VALUE um_close(struct um *machine, int fd);
@@ -251,6 +270,16 @@ VALUE um_queue_pop(struct um *machine, struct um_queue *queue);
251
270
  VALUE um_queue_unshift(struct um *machine, struct um_queue *queue, VALUE value);
252
271
  VALUE um_queue_shift(struct um *machine, struct um_queue *queue);
253
272
 
273
+ int stream_read_more(struct um_stream *stream);
274
+
275
+ VALUE resp_get_line(struct um_stream *stream, VALUE out_buffer);
276
+ VALUE resp_get_string(struct um_stream *stream, ulong len, VALUE out_buffer);
277
+ VALUE resp_decode(struct um_stream *stream, VALUE out_buffer);
278
+ void resp_encode(struct um_write_buffer *buf, VALUE obj);
279
+
280
+ void write_buffer_init(struct um_write_buffer *buf, VALUE str);
281
+ void write_buffer_update_len(struct um_write_buffer *buf);
282
+
254
283
  void um_define_net_constants(VALUE mod);
255
284
 
256
285
  #endif // UM_H
data/ext/um/um_ext.c CHANGED
@@ -3,6 +3,7 @@ void Init_Mutex();
3
3
  void Init_Queue();
4
4
  void Init_AsyncOp();
5
5
  void Init_SSL();
6
+ void Init_Stream();
6
7
 
7
8
  void Init_um_ext(void) {
8
9
  Init_UM();
@@ -10,4 +11,5 @@ void Init_um_ext(void) {
10
11
  Init_Queue();
11
12
  Init_AsyncOp();
12
13
  // Init_SSL();
14
+ Init_Stream();
13
15
  }
@@ -0,0 +1,344 @@
1
+ #include "um.h"
2
+
3
+ static inline void stream_check_truncate_buffer(struct um_stream *stream) {
4
+ if ((stream->pos == stream->len) && (stream->len >= 1 << 12)) {
5
+ rb_str_modify(stream->buffer);
6
+ rb_str_set_len(stream->buffer, 0);
7
+ stream->len = 0;
8
+ stream->pos = 0;
9
+ }
10
+ else if (stream->pos >= 1 << 12) {
11
+ rb_str_modify(stream->buffer);
12
+ char *base = RSTRING_PTR(stream->buffer);
13
+ int len_rest = stream->len - stream->pos;
14
+ memmove(base, base + stream->pos, len_rest);
15
+ rb_str_set_len(stream->buffer, len_rest);
16
+ stream->len = len_rest;
17
+ stream->pos = 0;
18
+ }
19
+ }
20
+
21
+ int stream_read_more(struct um_stream *stream) {
22
+ stream_check_truncate_buffer(stream);
23
+
24
+ size_t maxlen = 1 << 12;
25
+ size_t capa = rb_str_capacity(stream->buffer);
26
+ if (capa - stream->pos < maxlen)
27
+ rb_str_modify_expand(stream->buffer, maxlen - (capa - stream->pos));
28
+
29
+ rb_str_modify(stream->buffer);
30
+ char *ptr = RSTRING_PTR(stream->buffer) + stream->pos;
31
+ size_t ret = um_read_raw(stream->machine, stream->fd, ptr, maxlen);
32
+
33
+ if (ret == 0) {
34
+ stream->eof = 1;
35
+ return 0;
36
+ }
37
+
38
+ stream->len = stream->pos + ret;
39
+ rb_str_set_len(stream->buffer, stream->len);
40
+ return 1;
41
+ }
42
+
43
+ // ensure string can hold at least len bytes
44
+ static inline void str_expand(VALUE str, size_t len) {
45
+ size_t capa = rb_str_capacity(str);
46
+ if (capa < len + 1) rb_str_modify_expand(str, len + 1 - capa);
47
+ }
48
+
49
+ VALUE resp_get_line(struct um_stream *stream, VALUE out_buffer) {
50
+ char *start = RSTRING_PTR(stream->buffer) + stream->pos;
51
+ while (true) {
52
+ char * lf_ptr = memchr(start, '\r', stream->len - stream->pos);
53
+ if (lf_ptr) {
54
+ ulong len = lf_ptr - start;
55
+ stream->pos += len + 2;
56
+
57
+ if (NIL_P(out_buffer)) {
58
+ VALUE str = rb_str_new(start, len + 1);
59
+ rb_str_set_len(str, len);
60
+ RSTRING_PTR(str)[len] = 0;
61
+ return str;
62
+ }
63
+
64
+ str_expand(out_buffer, len + 1);
65
+ char *dest_ptr = RSTRING_PTR(out_buffer);
66
+ memcpy(dest_ptr, start, len);
67
+ dest_ptr[len] = 0; // add null at end
68
+ rb_str_set_len(out_buffer, len);
69
+ return out_buffer;
70
+ }
71
+
72
+ if (stream_read_more(stream))
73
+ // buffer ptr and pos may have changed after reading
74
+ start = RSTRING_PTR(stream->buffer) + stream->pos;
75
+ else
76
+ return Qnil;
77
+ }
78
+ }
79
+
80
+ VALUE resp_get_string(struct um_stream *stream, ulong len, VALUE out_buffer) {
81
+ ulong read_len = len + 2;
82
+
83
+ while (stream->len - stream->pos < read_len)
84
+ if (!stream_read_more(stream)) return Qnil;
85
+
86
+ char *start = RSTRING_PTR(stream->buffer) + stream->pos;
87
+ stream->pos += read_len;
88
+
89
+ if (NIL_P(out_buffer)) return rb_utf8_str_new(start, len);
90
+
91
+ str_expand(out_buffer, len + 1);
92
+ char *dest_ptr = RSTRING_PTR(out_buffer);
93
+ memcpy(dest_ptr, start, len);
94
+ dest_ptr[len] = 0; // add null at end
95
+ rb_str_set_len(out_buffer, len);
96
+ return out_buffer;
97
+ }
98
+
99
+ inline ulong resp_parse_length_field(const char *ptr, int len) {
100
+ return strtoul(ptr + 1, NULL, 10);
101
+ }
102
+
103
+ VALUE resp_decode_hash(struct um_stream *stream, VALUE out_buffer, ulong len) {
104
+ VALUE hash = rb_hash_new();
105
+
106
+ for (ulong i = 0; i < len; i++) {
107
+ VALUE key = resp_decode(stream, out_buffer);
108
+ VALUE value = resp_decode(stream, out_buffer);
109
+ rb_hash_aset(hash, key, value);
110
+ RB_GC_GUARD(key);
111
+ RB_GC_GUARD(value);
112
+ }
113
+
114
+ RB_GC_GUARD(hash);
115
+ return hash;
116
+ }
117
+
118
+ VALUE resp_decode_array(struct um_stream *stream, VALUE out_buffer, ulong len) {
119
+ VALUE array = rb_ary_new2(len);
120
+
121
+ for (ulong i = 0; i < len; i++) {
122
+ VALUE value = resp_decode(stream, out_buffer);
123
+ rb_ary_push(array, value);
124
+ RB_GC_GUARD(value);
125
+ }
126
+
127
+ RB_GC_GUARD(array);
128
+ return array;
129
+ }
130
+
131
+ static inline VALUE resp_decode_simple_string(char *ptr, ulong len) {
132
+ return rb_str_new(ptr + 1, len - 1);
133
+ }
134
+
135
+ static inline VALUE resp_decode_string(struct um_stream *stream, VALUE out_buffer, ulong len) {
136
+ return resp_get_string(stream, len, out_buffer);
137
+ }
138
+
139
+ static inline VALUE resp_decode_string_with_encoding(struct um_stream *stream, VALUE out_buffer, ulong len) {
140
+ VALUE with_enc = resp_get_string(stream, len, out_buffer);
141
+ char *ptr = RSTRING_PTR(with_enc);
142
+ len = RSTRING_LEN(with_enc);
143
+ if ((len < 4) || (ptr[3] != ':')) return Qnil;
144
+
145
+ return rb_utf8_str_new(ptr + 4, len - 4);
146
+ }
147
+
148
+ static inline VALUE resp_decode_integer(char *ptr) {
149
+ long value = strtol(ptr + 1, NULL, 10);
150
+ return LONG2NUM(value);
151
+ }
152
+
153
+ static inline VALUE resp_decode_float(char *ptr) {
154
+ double value = strtod(ptr + 1, NULL);
155
+ return DBL2NUM(value);
156
+ }
157
+
158
+ static inline VALUE resp_decode_simple_error(char *ptr, ulong len) {
159
+ static ID ID_new = 0;
160
+ if (!ID_new) ID_new = rb_intern("new");
161
+
162
+ VALUE msg = rb_str_new(ptr + 1, len - 1);
163
+ VALUE err = rb_funcall(rb_eRuntimeError, ID_new, 1, msg);
164
+ RB_GC_GUARD(msg);
165
+ return err;
166
+ }
167
+
168
+ static inline VALUE resp_decode_error(struct um_stream *stream, VALUE out_buffer, ulong len) {
169
+ static ID ID_new = 0;
170
+ if (!ID_new) ID_new = rb_intern("new");
171
+
172
+ VALUE msg = resp_decode_string(stream, out_buffer, len);
173
+ VALUE err = rb_funcall(rb_eRuntimeError, ID_new, 1, msg);
174
+ RB_GC_GUARD(msg);
175
+ return err;
176
+ }
177
+
178
+ VALUE resp_decode(struct um_stream *stream, VALUE out_buffer) {
179
+ VALUE msg = resp_get_line(stream, out_buffer);
180
+ if (msg == Qnil) return Qnil;
181
+
182
+ char *ptr = RSTRING_PTR(msg);
183
+ ulong len = RSTRING_LEN(msg);
184
+ ulong data_len;
185
+ if (len == 0) return Qnil;
186
+
187
+ switch (ptr[0]) {
188
+ case '%': // hash
189
+ case '|': // attributes hash
190
+ data_len = resp_parse_length_field(ptr, len);
191
+ return resp_decode_hash(stream, out_buffer, data_len);
192
+
193
+ case '*': // array
194
+ case '~': // set
195
+ case '>': // pub/sub push
196
+ data_len = resp_parse_length_field(ptr, len);
197
+ return resp_decode_array(stream, out_buffer, data_len);
198
+
199
+ case '+': // simple string
200
+ return resp_decode_simple_string(ptr, len);
201
+ case '$': // string
202
+ data_len = resp_parse_length_field(ptr, len);
203
+ return resp_decode_string(stream, out_buffer, data_len);
204
+ case '=': // string with encoding
205
+ data_len = resp_parse_length_field(ptr, len);
206
+ return resp_decode_string_with_encoding(stream, out_buffer, data_len);
207
+
208
+ case '_': // null
209
+ return Qnil;
210
+ case '#': // boolean
211
+ return (len > 1) && (ptr[1] == 't') ? Qtrue : Qfalse;
212
+
213
+ case ':': // integer
214
+ return resp_decode_integer(ptr);
215
+ case '(': // big integer
216
+ rb_raise(rb_eRuntimeError, "Big integers are not supported");
217
+ case ',': // float
218
+ return resp_decode_float(ptr);
219
+
220
+ case '-': // simple error
221
+ return resp_decode_simple_error(ptr, len);
222
+ case '!': // error
223
+ data_len = resp_parse_length_field(ptr, len);
224
+ return resp_decode_error(stream, out_buffer, data_len);
225
+ default:
226
+ rb_raise(rb_eRuntimeError, "Invalid character encountered");
227
+ }
228
+
229
+ RB_GC_GUARD(msg);
230
+ }
231
+
232
+ void write_buffer_init(struct um_write_buffer *buf, VALUE str) {
233
+ size_t capa = 1 << 12;
234
+ size_t len = RSTRING_LEN(str);
235
+ while (capa < len) capa += 1 << 12;
236
+
237
+ rb_str_resize(str, capa);
238
+ rb_str_set_len(str, len);
239
+ buf->str = str;
240
+ buf->capa = capa;
241
+ buf->len = len;
242
+ buf->ptr = RSTRING_PTR(str);
243
+ }
244
+
245
+ static inline void write_buffer_expand(struct um_write_buffer *buf, size_t newsize) {
246
+ if (buf->capa < newsize) {
247
+ size_t old_capa = buf->capa;
248
+ while (buf->capa < newsize) buf->capa += 1 << 12;
249
+ rb_str_modify_expand(buf->str, buf->capa - old_capa);
250
+ buf->ptr = RSTRING_PTR(buf->str);
251
+ }
252
+ }
253
+
254
+ static inline void write_buffer_append(struct um_write_buffer *buf, const char *ptr, size_t len) {
255
+ size_t total_len = buf->len + len;
256
+ write_buffer_expand(buf, total_len);
257
+
258
+ memcpy(buf->ptr + buf->len, ptr, len);
259
+ buf->len = total_len;
260
+ }
261
+
262
+ static inline void write_buffer_append_cstr(struct um_write_buffer *buf, const char *str) {
263
+ write_buffer_append(buf, str, strlen(str));
264
+ }
265
+
266
+ static inline void write_buffer_append_resp_bulk_string(struct um_write_buffer *buf, VALUE str) {
267
+ // leave enough place for prefix and postfix
268
+ size_t str_len = RSTRING_LEN(str);
269
+ size_t total_len = buf->len + str_len + 16;
270
+ write_buffer_expand(buf, total_len);
271
+
272
+
273
+ int prefix_len = sprintf(buf->ptr + buf->len, "$%ld\r\n", str_len);
274
+ const char *src = RSTRING_PTR(str);
275
+ memcpy(buf->ptr + buf->len + prefix_len, src, str_len);
276
+ buf->ptr[buf->len + prefix_len + str_len + 0] = '\r';
277
+ buf->ptr[buf->len + prefix_len + str_len + 1] = '\n';
278
+ buf->len += prefix_len + str_len + 2;
279
+ }
280
+
281
+ inline void write_buffer_update_len(struct um_write_buffer *buf) {
282
+ rb_str_set_len(buf->str, buf->len);
283
+ }
284
+
285
+ struct resp_encode_hash_ctx {
286
+ struct um_write_buffer *buf;
287
+ VALUE obj;
288
+ };
289
+
290
+ int resp_encode_hash_entry(VALUE key, VALUE value, VALUE arg) {
291
+ struct resp_encode_hash_ctx *ctx = (struct resp_encode_hash_ctx *)arg;
292
+
293
+ resp_encode(ctx->buf, key);
294
+ resp_encode(ctx->buf, value);
295
+ return 0;
296
+ }
297
+
298
+ void resp_encode(struct um_write_buffer *buf, VALUE obj) {
299
+ char tmp[60];
300
+
301
+ switch (TYPE(obj)) {
302
+ case T_NIL:
303
+ return write_buffer_append_cstr(buf, "_\r\n");
304
+ return;
305
+ case T_FALSE:
306
+ write_buffer_append_cstr(buf, "#f\r\n");
307
+ return;
308
+ case T_TRUE:
309
+ write_buffer_append_cstr(buf, "#t\r\n");
310
+ return;
311
+ case T_FIXNUM:
312
+ sprintf(tmp, ":%ld\r\n", NUM2LONG(obj));
313
+ write_buffer_append_cstr(buf, tmp);
314
+ return;
315
+ case T_FLOAT:
316
+ sprintf(tmp, ",%lg\r\n", NUM2DBL(obj));
317
+ write_buffer_append_cstr(buf, tmp);
318
+ return;
319
+ case T_STRING:
320
+ write_buffer_append_resp_bulk_string(buf, obj);
321
+ return;
322
+ case T_ARRAY:
323
+ {
324
+ ulong len = RARRAY_LEN(obj);
325
+ sprintf(tmp, "*%ld\r\n", len);
326
+ write_buffer_append_cstr(buf, tmp);
327
+ for (ulong i = 0; i < len; i++)
328
+ resp_encode(buf, rb_ary_entry(obj, i));
329
+ return;
330
+ }
331
+ case T_HASH:
332
+ {
333
+ ulong len = rb_hash_size_num(obj);
334
+ sprintf(tmp, "%%%ld\r\n", len);
335
+ write_buffer_append_cstr(buf, tmp);
336
+
337
+ struct resp_encode_hash_ctx ctx = { buf, obj };
338
+ rb_hash_foreach(obj, resp_encode_hash_entry, (VALUE)&ctx);
339
+ return;
340
+ }
341
+ default:
342
+ rb_raise(rb_eRuntimeError, "Can't encode object");
343
+ }
344
+ }
@@ -0,0 +1,140 @@
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->machine = RTYPEDDATA_DATA(machine);
41
+ stream->fd = NUM2ULONG(fd);
42
+ stream->buffer = rb_utf8_str_new_literal("");
43
+ rb_str_resize(stream->buffer, 1 << 16); // 64KB
44
+ rb_str_set_len(stream->buffer, 0);
45
+
46
+ stream->len = 0;
47
+ stream->pos = 0;
48
+ stream->eof = 0;
49
+
50
+ return self;
51
+ }
52
+
53
+ VALUE Stream_get_line(VALUE self) {
54
+ struct um_stream *stream = RTYPEDDATA_DATA(self);
55
+ if (unlikely(stream->eof)) return Qnil;
56
+
57
+ char *start = RSTRING_PTR(stream->buffer) + stream->pos;
58
+ while (true) {
59
+ char * lf_ptr = memchr(start, '\n', stream->len - stream->pos);
60
+ if (lf_ptr) {
61
+ ulong len = lf_ptr - start;
62
+ if (len && (start[len - 1] == '\r')) len -= 1;
63
+
64
+ VALUE str = rb_str_new(start, len);
65
+ stream->pos += lf_ptr - start + 1;
66
+ return str;
67
+ }
68
+
69
+ if (!stream_read_more(stream)) return Qnil;
70
+ }
71
+ }
72
+
73
+ VALUE Stream_get_string(VALUE self, VALUE len) {
74
+ struct um_stream *stream = RTYPEDDATA_DATA(self);
75
+ if (unlikely(stream->eof)) return Qnil;
76
+
77
+ ulong ulen = NUM2ULONG(len);
78
+
79
+ while (stream->len - stream->pos < ulen)
80
+ if (!stream_read_more(stream)) return Qnil;
81
+
82
+ char *start = RSTRING_PTR(stream->buffer) + stream->pos;
83
+ VALUE str = rb_utf8_str_new(start, ulen);
84
+ stream->pos += ulen;
85
+ return str;
86
+ }
87
+
88
+ VALUE Stream_resp_get_line(VALUE self) {
89
+ struct um_stream *stream = RTYPEDDATA_DATA(self);
90
+ if (unlikely(stream->eof)) return Qnil;
91
+
92
+ VALUE line = resp_get_line(stream, Qnil);
93
+ RB_GC_GUARD(line);
94
+ return line;
95
+ }
96
+
97
+ VALUE Stream_resp_get_string(VALUE self, VALUE len) {
98
+ struct um_stream *stream = RTYPEDDATA_DATA(self);
99
+ if (unlikely(stream->eof)) return Qnil;
100
+
101
+ VALUE str = resp_get_string(stream, NUM2ULONG(len), Qnil);
102
+ RB_GC_GUARD(str);
103
+ return str;
104
+ }
105
+
106
+ VALUE Stream_resp_decode(VALUE self) {
107
+ struct um_stream *stream = RTYPEDDATA_DATA(self);
108
+ if (unlikely(stream->eof)) return Qnil;
109
+
110
+ VALUE out_buffer = rb_utf8_str_new_literal("");
111
+ VALUE obj = resp_decode(stream, out_buffer);
112
+ RB_GC_GUARD(out_buffer);
113
+ return obj;
114
+ }
115
+
116
+ VALUE Stream_resp_encode(VALUE self, VALUE str, VALUE obj) {
117
+ struct um_write_buffer buf;
118
+ write_buffer_init(&buf, str);
119
+ rb_str_modify(str);
120
+ resp_encode(&buf, obj);
121
+ write_buffer_update_len(&buf);
122
+ return str;
123
+ }
124
+
125
+ void Init_Stream(void) {
126
+ VALUE cStream = rb_define_class_under(cUM, "Stream", rb_cObject);
127
+ rb_define_alloc_func(cStream, Stream_allocate);
128
+
129
+ rb_define_method(cStream, "initialize", Stream_initialize, 2);
130
+
131
+ rb_define_method(cStream, "get_line", Stream_get_line, 0);
132
+ rb_define_method(cStream, "get_string", Stream_get_string, 1);
133
+
134
+ rb_define_method(cStream, "resp_get_line", Stream_resp_get_line, 0);
135
+ rb_define_method(cStream, "resp_get_string", Stream_resp_get_string, 1);
136
+
137
+ rb_define_method(cStream, "resp_decode", Stream_resp_decode, 0);
138
+
139
+ rb_define_singleton_method(cStream, "resp_encode", Stream_resp_encode, 2);
140
+ }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class UringMachine
4
- VERSION = '0.10'
4
+ VERSION = '0.11'
5
5
  end