uringmachine 0.29.1 → 0.30.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.
data/ext/um/um.h CHANGED
@@ -48,6 +48,9 @@ enum um_op_kind {
48
48
  OP_CLOSE,
49
49
  OP_CLOSE_ASYNC,
50
50
  OP_STATX,
51
+ OP_SPLICE,
52
+ OP_TEE,
53
+ OP_FSYNC,
51
54
 
52
55
  OP_ACCEPT,
53
56
  OP_RECV,
@@ -161,7 +164,7 @@ struct um_op {
161
164
  struct iovec *iovecs; // used for vectorized write/send
162
165
  siginfo_t siginfo; // used for waitid
163
166
  int int_value; // used for getsockopt
164
- size_t bp_commit_level; // buffer pool commit threshold
167
+ size_t bp_commit_level; // buffer pool commit level
165
168
  };
166
169
  };
167
170
 
@@ -283,7 +286,8 @@ struct um_stream {
283
286
  struct um_op *op;
284
287
  struct um_segment *head;
285
288
  struct um_segment *tail;
286
- size_t pending_len;
289
+ size_t consumed_bytes;
290
+ size_t pending_bytes;
287
291
  size_t pos;
288
292
  int eof;
289
293
  };
@@ -324,7 +328,6 @@ void um_op_list_compact(struct um *machine, struct um_op *head);
324
328
  void um_op_multishot_results_push(struct um *machine, struct um_op *op, __s32 res, __u32 flags);
325
329
  void um_op_multishot_results_clear(struct um *machine, struct um_op *op);
326
330
 
327
- struct um_segment *um_segment_alloc(struct um *machine);
328
331
  void um_segment_free(struct um *machine, struct um_segment *segment);
329
332
 
330
333
  void um_runqueue_push(struct um *machine, struct um_op *op);
@@ -390,6 +393,9 @@ VALUE um_waitid_status(struct um *machine, int idtype, int id, int options);
390
393
  #endif
391
394
 
392
395
  VALUE um_statx(struct um *machine, int dirfd, VALUE path, int flags, unsigned int mask);
396
+ VALUE um_splice(struct um *machine, int in_fd, int out_fd, uint nbytes);
397
+ VALUE um_tee(struct um *machine, int in_fd, int out_fd, uint nbytes);
398
+ VALUE um_fsync(struct um *machine, int fd);
393
399
 
394
400
  VALUE um_accept(struct um *machine, int fd);
395
401
  VALUE um_accept_each(struct um *machine, int fd);
@@ -436,6 +442,7 @@ void stream_clear(struct um_stream *stream);
436
442
  VALUE stream_get_line(struct um_stream *stream, VALUE buf, size_t maxlen);
437
443
  VALUE stream_get_string(struct um_stream *stream, VALUE out_buffer, ssize_t len, size_t inc, int safe_inc);
438
444
  void stream_skip(struct um_stream *stream, size_t inc, int safe_inc);
445
+ void stream_each(struct um_stream *stream);
439
446
  VALUE resp_decode(struct um_stream *stream, VALUE out_buffer);
440
447
  void resp_encode(struct um_write_buffer *buf, VALUE obj);
441
448
  void resp_encode_cmd(struct um_write_buffer *buf, int argc, VALUE *argv);
@@ -453,9 +460,9 @@ void um_sidecar_signal_wait(struct um *machine);
453
460
  void um_sidecar_signal_wake(struct um *machine);
454
461
 
455
462
  void um_ssl_set_bio(struct um *machine, VALUE ssl_obj);
456
- int um_ssl_read(struct um *machine, VALUE ssl, VALUE buf, int maxlen);
457
- int um_ssl_read_raw(struct um *machine, VALUE ssl_obj, char *ptr, int maxlen);
458
- int um_ssl_write(struct um *machine, VALUE ssl, VALUE buf, int len);
463
+ int um_ssl_read(struct um *machine, VALUE ssl, VALUE buf, size_t maxlen);
464
+ int um_ssl_read_raw(struct um *machine, VALUE ssl_obj, char *ptr, size_t maxlen);
465
+ int um_ssl_write(struct um *machine, VALUE ssl, VALUE buf, size_t len);
459
466
 
460
467
  void bp_setup(struct um *machine);
461
468
  void bp_teardown(struct um *machine);
@@ -18,7 +18,7 @@ inline struct um_buffer *buffer_alloc(struct um *machine) {
18
18
  return buffer;
19
19
  }
20
20
 
21
- inline struct um_buffer *bp_buffer_checkout(struct um *machine) {
21
+ struct um_buffer *bp_buffer_checkout(struct um *machine) {
22
22
  struct um_buffer *buffer = machine->bp_buffer_freelist;
23
23
  if (buffer) {
24
24
  struct um_buffer *next = buffer->next;
@@ -50,13 +50,13 @@ inline void buffer_free(struct um *machine, struct um_buffer *buffer) {
50
50
  }
51
51
  }
52
52
 
53
- inline void bp_buffer_checkin(struct um *machine, struct um_buffer *buffer) {
53
+ void bp_buffer_checkin(struct um *machine, struct um_buffer *buffer) {
54
54
  assert(buffer->ref_count > 0);
55
55
  buffer->ref_count--;
56
56
  if (!buffer->ref_count) buffer_free(machine, buffer);
57
57
  }
58
58
 
59
- inline void bp_discard_buffer_freelist(struct um *machine) {
59
+ void bp_discard_buffer_freelist(struct um *machine) {
60
60
  while (machine->bp_buffer_freelist) {
61
61
  struct um_buffer *buffer = machine->bp_buffer_freelist;
62
62
  struct um_buffer *next = buffer->next;
@@ -69,7 +69,7 @@ inline void bp_discard_buffer_freelist(struct um *machine) {
69
69
  }
70
70
  }
71
71
 
72
- inline void bp_setup(struct um *machine) {
72
+ void bp_setup(struct um *machine) {
73
73
  int ret;
74
74
  machine->bp_br = io_uring_setup_buf_ring(&machine->ring, BP_BR_ENTRIES, BP_BGID, IOU_PBUF_RING_INC, &ret);
75
75
  if (unlikely(!machine->bp_br)) rb_syserr_fail(ret, strerror(ret));
@@ -85,7 +85,7 @@ inline void bp_setup(struct um *machine) {
85
85
  machine->bp_total_commited = 0;
86
86
  }
87
87
 
88
- inline void bp_teardown(struct um *machine) {
88
+ void bp_teardown(struct um *machine) {
89
89
  bp_discard_buffer_freelist(machine);
90
90
  for (int i = 0; i < BP_BR_ENTRIES; i++) {
91
91
  struct um_buffer *buffer = machine->bp_commited_buffers[i];
@@ -162,7 +162,7 @@ inline int should_commit_more_p(struct um *machine) {
162
162
  (machine->bp_total_commited < machine->bp_commit_level);
163
163
  }
164
164
 
165
- inline void bp_ensure_commit_level(struct um *machine) {
165
+ void bp_ensure_commit_level(struct um *machine) {
166
166
  if (machine->bp_total_commited > (machine->bp_commit_level / 2))
167
167
  return;
168
168
 
@@ -179,7 +179,7 @@ inline void bp_ensure_commit_level(struct um *machine) {
179
179
  // size.
180
180
  }
181
181
 
182
- inline void bp_handle_enobufs(struct um *machine) {
182
+ void bp_handle_enobufs(struct um *machine) {
183
183
  if (unlikely(machine->bp_commit_level >= BP_MAX_COMMIT_LEVEL))
184
184
  rb_raise(eUMError, "Buffer starvation");
185
185
 
@@ -189,7 +189,7 @@ inline void bp_handle_enobufs(struct um *machine) {
189
189
  bp_discard_buffer_freelist(machine);
190
190
  }
191
191
 
192
- inline struct um_segment *um_segment_alloc(struct um *machine) {
192
+ inline struct um_segment *segment_alloc(struct um *machine) {
193
193
  if (machine->segment_freelist) {
194
194
  struct um_segment *segment = machine->segment_freelist;
195
195
  machine->segment_freelist = segment->next;
@@ -208,14 +208,14 @@ inline struct um_segment *um_segment_alloc(struct um *machine) {
208
208
  return batch;
209
209
  }
210
210
 
211
- inline void um_segment_free(struct um *machine, struct um_segment *segment) {
211
+ void um_segment_free(struct um *machine, struct um_segment *segment) {
212
212
  segment->next = machine->segment_freelist;
213
213
  machine->segment_freelist = segment;
214
214
  machine->metrics.segments_free++;
215
215
  }
216
216
 
217
- inline struct um_segment *bp_buffer_consume(struct um *machine, struct um_buffer *buffer, size_t len) {
218
- struct um_segment *segment = um_segment_alloc(machine);
217
+ struct um_segment *bp_buffer_consume(struct um *machine, struct um_buffer *buffer, size_t len) {
218
+ struct um_segment *segment = segment_alloc(machine);
219
219
  segment->ptr = buffer->buf + buffer->pos;
220
220
  segment->len = len;
221
221
  segment->buffer = buffer;
data/ext/um/um_class.c CHANGED
@@ -534,6 +534,60 @@ VALUE UM_statx(VALUE self, VALUE dirfd, VALUE path, VALUE flags, VALUE mask) {
534
534
  return um_statx(machine, NUM2INT(dirfd), path, NUM2INT(flags), NUM2UINT(mask));
535
535
  }
536
536
 
537
+ /* call-seq:
538
+ * machine.splice(in_fd, out_fd, nbytes) -> len
539
+ *
540
+ * Splices bytes from in_fd to out_fd. At least one of the given fds must be a
541
+ * pipe.
542
+ *
543
+ * - https://www.man7.org/linux/man-pages/man2/splice.2.html
544
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_splice.3.html
545
+ *
546
+ * @param in_fd [Integer] fd to splice from
547
+ * @param out_fd [Integer] fd to splice to
548
+ * @param nbytes [Integer] number of bytes to splice
549
+ * @return [Integer] number of bytes spliced
550
+ */
551
+ VALUE UM_splice(VALUE self, VALUE in_fd, VALUE out_fd, VALUE nbytes) {
552
+ struct um *machine = um_get_machine(self);
553
+ return um_splice(machine, NUM2INT(in_fd), NUM2INT(out_fd), NUM2UINT(nbytes));
554
+ }
555
+
556
+ /* call-seq:
557
+ * machine.tee(in_fd, out_fd, nbytes) -> len
558
+ *
559
+ * Duplicates bytes from in_fd to out_fd. At least one of the given fds must be
560
+ * a pipe.
561
+ *
562
+ * - https://www.man7.org/linux/man-pages/man2/tee.2.html
563
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_tee.3.html
564
+ *
565
+ * @param in_fd [Integer] fd to copy from
566
+ * @param out_fd [Integer] fd to copy to
567
+ * @param nbytes [Integer] number of bytes to duplicate
568
+ * @return [Integer] number of bytes duplicated
569
+ */
570
+ VALUE UM_tee(VALUE self, VALUE in_fd, VALUE out_fd, VALUE nbytes) {
571
+ struct um *machine = um_get_machine(self);
572
+ return um_tee(machine, NUM2INT(in_fd), NUM2INT(out_fd), NUM2UINT(nbytes));
573
+ }
574
+
575
+ /* call-seq:
576
+ * machine.fsync(fd) -> 0
577
+ *
578
+ * Flushes all modified file data to the storage device.
579
+ *
580
+ * - https://www.man7.org/linux/man-pages/man2/fsync.2.html
581
+ * - https://www.man7.org/linux/man-pages/man3/io_uring_prep_fsync.3.html
582
+ *
583
+ * @param fd [Integer] fd
584
+ * @return [Integer] 0 if successful
585
+ */
586
+ VALUE UM_fsync(VALUE self, VALUE fd) {
587
+ struct um *machine = um_get_machine(self);
588
+ return um_fsync(machine, NUM2INT(fd));
589
+ }
590
+
537
591
  /* call-seq:
538
592
  * machine.close(fd) -> 0
539
593
  *
@@ -1480,6 +1534,9 @@ void Init_UM(void) {
1480
1534
  rb_define_method(cUM, "writev", UM_writev, -1);
1481
1535
  rb_define_method(cUM, "write_async", UM_write_async, -1);
1482
1536
  rb_define_method(cUM, "statx", UM_statx, 4);
1537
+ rb_define_method(cUM, "splice", UM_splice, 3);
1538
+ rb_define_method(cUM, "tee", UM_tee, 3);
1539
+ rb_define_method(cUM, "fsync", UM_fsync, 1);
1483
1540
 
1484
1541
  rb_define_method(cUM, "poll", UM_poll, 2);
1485
1542
  rb_define_method(cUM, "select", UM_select, 3);
data/ext/um/um_ssl.c CHANGED
@@ -75,7 +75,7 @@ void um_ssl_set_bio(struct um *machine, VALUE ssl_obj)
75
75
  SSL_set0_wbio(ssl, bio);
76
76
  }
77
77
 
78
- int um_ssl_read_raw(struct um *machine, VALUE ssl_obj, char *ptr, int maxlen) {
78
+ int um_ssl_read_raw(struct um *machine, VALUE ssl_obj, char *ptr, size_t maxlen) {
79
79
  SSL *ssl = RTYPEDDATA_GET_DATA(ssl_obj);
80
80
 
81
81
  int ret = SSL_read(ssl, ptr, maxlen);
@@ -84,22 +84,23 @@ int um_ssl_read_raw(struct um *machine, VALUE ssl_obj, char *ptr, int maxlen) {
84
84
  return ret;
85
85
  }
86
86
 
87
- int um_ssl_read(struct um *machine, VALUE ssl_obj, VALUE buf, int maxlen) {
87
+ int um_ssl_read(struct um *machine, VALUE ssl_obj, VALUE buf, size_t maxlen) {
88
88
  void *ptr = um_prepare_read_buffer(buf, maxlen, 0);
89
89
  int ret = um_ssl_read_raw(machine, ssl_obj, ptr, maxlen);
90
90
  um_update_read_buffer(buf, 0, ret);
91
91
  return ret;
92
92
  }
93
93
 
94
- int um_ssl_write(struct um *machine, VALUE ssl_obj, VALUE buf, int len) {
94
+ int um_ssl_write(struct um *machine, VALUE ssl_obj, VALUE buf, size_t len) {
95
95
  SSL *ssl = RTYPEDDATA_GET_DATA(ssl_obj);
96
96
  const void *base;
97
97
  size_t size;
98
98
  um_get_buffer_bytes_for_writing(buf, &base, &size, true);
99
- if ((len == (int)-1) || (len > (int)size)) len = (int)size;
99
+ if (!len || (len > size)) len = size;
100
+ if (len > INT_MAX) len = INT_MAX;
100
101
  if (unlikely(!len)) return INT2NUM(0);
101
102
 
102
- int ret = SSL_write(ssl, base, len);
103
+ int ret = SSL_write(ssl, base, (int)len);
103
104
  if (ret <= 0) rb_raise(eUMError, "Failed to write");
104
105
 
105
106
  return ret;
data/ext/um/um_stream.c CHANGED
@@ -1,5 +1,6 @@
1
- #include "um.h"
2
1
  #include <stdlib.h>
2
+ #include <ruby/io/buffer.h>
3
+ #include "um.h"
3
4
 
4
5
  inline void stream_add_segment(struct um_stream *stream, struct um_segment *segment) {
5
6
  segment->next = NULL;
@@ -9,7 +10,7 @@ inline void stream_add_segment(struct um_stream *stream, struct um_segment *segm
9
10
  }
10
11
  else
11
12
  stream->head = stream->tail = segment;
12
- stream->pending_len += segment->len;
13
+ stream->pending_bytes += segment->len;
13
14
  }
14
15
 
15
16
  inline int stream_process_op_result(struct um_stream *stream, struct um_op_result *result) {
@@ -73,7 +74,7 @@ void um_stream_cleanup(struct um_stream *stream) {
73
74
  um_segment_checkin(stream->machine, stream->head);
74
75
  stream->head = next;
75
76
  }
76
- stream->pending_len = 0;
77
+ stream->pending_bytes = 0;
77
78
  }
78
79
 
79
80
  // returns true if case of ENOBUFS error, sets more to true if more data forthcoming
@@ -124,7 +125,7 @@ void stream_clear(struct um_stream *stream) {
124
125
  um_segment_checkin(stream->machine, stream->head);
125
126
  stream->head = next;
126
127
  }
127
- stream->pending_len = 0;
128
+ stream->pending_bytes = 0;
128
129
 
129
130
  if (stream->working_buffer) {
130
131
  bp_buffer_checkin(stream->machine, stream->working_buffer);
@@ -156,12 +157,12 @@ int stream_get_more_segments_bp(struct um_stream *stream) {
156
157
  enobufs = stream_process_segments(stream, &total_bytes, &more);
157
158
  um_op_multishot_results_clear(stream->machine, stream->op);
158
159
  if (unlikely(enobufs)) {
159
- int should_restart = stream->pending_len < (stream->machine->bp_buffer_size * 4);
160
+ int should_restart = stream->pending_bytes < (stream->machine->bp_buffer_size * 4);
160
161
  // int same_threshold = stream->op->bp_commit_level == stream->machine->bp_commit_level;
161
162
 
162
163
  // fprintf(stderr, "%p enobufs total: %ld pending: %ld threshold: %ld bc: %d (same: %d, restart: %d)\n",
163
164
  // stream,
164
- // total_bytes, stream->pending_len, stream->machine->bp_commit_level,
165
+ // total_bytes, stream->pending_bytes, stream->machine->bp_commit_level,
165
166
  // stream->machine->bp_buffer_count,
166
167
  // same_threshold, should_restart
167
168
  // );
@@ -173,7 +174,7 @@ int stream_get_more_segments_bp(struct um_stream *stream) {
173
174
  if (should_restart) {
174
175
  if (stream->op->bp_commit_level == stream->machine->bp_commit_level)
175
176
  bp_handle_enobufs(stream->machine);
176
-
177
+
177
178
  um_op_release(stream->machine, stream->op);
178
179
  stream->op = NULL;
179
180
  // stream_multishot_op_start(stream);
@@ -234,13 +235,24 @@ inline void stream_shift_head(struct um_stream *stream) {
234
235
  stream->pos = 0;
235
236
  }
236
237
 
238
+ inline VALUE make_segment_io_buffer(struct um_segment *segment, size_t pos) {
239
+ return rb_io_buffer_new(
240
+ segment->ptr + pos, segment->len - pos,
241
+ RB_IO_BUFFER_LOCKED|RB_IO_BUFFER_READONLY
242
+ );
243
+ }
244
+
237
245
  inline void stream_skip(struct um_stream *stream, size_t inc, int safe_inc) {
246
+ if (unlikely(stream->eof && !stream->head)) return;
247
+ if (safe_inc && !stream->tail && !stream_get_more_segments(stream)) return;
248
+
238
249
  while (inc) {
239
250
  size_t segment_len = stream->head->len - stream->pos;
240
251
  size_t inc_len = (segment_len <= inc) ? segment_len : inc;
241
252
  inc -= inc_len;
242
253
  stream->pos += inc_len;
243
- stream->pending_len -= inc_len;
254
+ stream->consumed_bytes += inc_len;
255
+ stream->pending_bytes -= inc_len;
244
256
  if (stream->pos == stream->head->len) {
245
257
  stream_shift_head(stream);
246
258
  if (inc && safe_inc && !stream->head) {
@@ -250,6 +262,30 @@ inline void stream_skip(struct um_stream *stream, size_t inc, int safe_inc) {
250
262
  }
251
263
  }
252
264
 
265
+ inline void stream_each(struct um_stream *stream) {
266
+ if (unlikely(stream->eof && !stream->head)) return;
267
+ if (!stream->tail && !stream_get_more_segments(stream)) return;
268
+
269
+ struct um_segment *current = stream->head;
270
+ size_t pos = stream->pos;
271
+
272
+ VALUE buffer = Qnil;
273
+ while (true) {
274
+ struct um_segment *next = current->next;
275
+ buffer = make_segment_io_buffer(current, pos);
276
+ rb_yield(buffer);
277
+ rb_io_buffer_free_locked(buffer);
278
+ stream_shift_head(stream);
279
+
280
+ if (!next) {
281
+ if (!stream_get_more_segments(stream)) return;
282
+ }
283
+ current = stream->head;
284
+ pos = 0;
285
+ }
286
+ RB_GC_GUARD(buffer);
287
+ }
288
+
253
289
  inline void stream_copy(struct um_stream *stream, char *dest, size_t len) {
254
290
  while (len) {
255
291
  char *segment_ptr = stream->head->ptr + stream->pos;
@@ -259,7 +295,8 @@ inline void stream_copy(struct um_stream *stream, char *dest, size_t len) {
259
295
 
260
296
  len -= cpy_len;
261
297
  stream->pos += cpy_len;
262
- stream->pending_len -= cpy_len;
298
+ stream->consumed_bytes += cpy_len;
299
+ stream->pending_bytes -= cpy_len;
263
300
  dest += cpy_len;
264
301
  if (stream->pos == stream->head->len) stream_shift_head(stream);
265
302
  }
@@ -102,6 +102,20 @@ static inline void stream_setup(struct um_stream *stream, VALUE target, VALUE mo
102
102
  rb_raise(eUMError, "Invalid stream mode");
103
103
  }
104
104
 
105
+ /* call-seq:
106
+ * UM::Stream.new(machine, fd, mode = nil) -> stream
107
+ * machine.stream(fd, mode = nil) -> stream
108
+ * machine.stream(fd, mode = nil) { |stream| ... }
109
+ *
110
+ * Initializes a new stream with the given UringMachine instance, target and
111
+ * optional mode. The target maybe a file descriptor, or an instance of
112
+ * OpenSSL::SSL::SSLSocket. In case of an SSL socket, the mode should be :ssl.
113
+ *
114
+ * @param machine [UringMachine] UringMachine instance
115
+ * @param target [integer, OpenSSL::SSL::SSLSocket] stream target: file descriptor or SSL socket
116
+ * @param mode [Symbol] optional stream mode: :bp_read, :bp_recv, :ssl
117
+ * @return [void]
118
+ */
105
119
  VALUE Stream_initialize(int argc, VALUE *argv, VALUE self) {
106
120
  VALUE machine;
107
121
  VALUE target;
@@ -118,6 +132,13 @@ VALUE Stream_initialize(int argc, VALUE *argv, VALUE self) {
118
132
  return self;
119
133
  }
120
134
 
135
+ /* call-seq:
136
+ * stream.mode -> mode
137
+ *
138
+ * Returns the stream mode.
139
+ *
140
+ * @return [Symbol] stream mode
141
+ */
121
142
  VALUE Stream_mode(VALUE self) {
122
143
  struct um_stream *stream = um_get_stream(self);
123
144
  switch (stream->mode) {
@@ -129,16 +150,70 @@ VALUE Stream_mode(VALUE self) {
129
150
  return Qnil;
130
151
  }
131
152
 
153
+ /* call-seq:
154
+ * stream.get_line(limit) -> str
155
+ *
156
+ * Reads from the string until a newline character is encountered. Returns the
157
+ * line without the newline delimiter. If limit is 0, the line length is not
158
+ * limited. If no newline delimiter is found before EOF, returns nil.
159
+ *
160
+ * @param limit [integer] maximum line length (0 means no limit)
161
+ * @return [String, nil] read data or nil
162
+ */
132
163
  VALUE Stream_get_line(VALUE self, VALUE limit) {
133
164
  struct um_stream *stream = um_get_stream(self);
134
165
  return stream_get_line(stream, Qnil, NUM2ULONG(limit));
135
166
  }
136
167
 
168
+ /* call-seq:
169
+ * stream.get_string(len) -> str
170
+ *
171
+ * Reads len bytes from the stream. If len is 0, reads all available bytes. If
172
+ * len is negative, reads up to -len available bytes. If len is positive and eof
173
+ * is encountered before len bytes are read, returns nil.
174
+ *
175
+ * @param len [integer] number of bytes to read
176
+ * @return [String, nil] read data or nil
177
+ */
137
178
  VALUE Stream_get_string(VALUE self, VALUE len) {
138
179
  struct um_stream *stream = um_get_stream(self);
139
180
  return stream_get_string(stream, Qnil, NUM2LONG(len), 0, false);
140
181
  }
141
182
 
183
+ /* call-seq:
184
+ * stream.skip(len) -> len
185
+ *
186
+ * Skips len bytes in the stream.
187
+ *
188
+ * @param len [integer] number of bytes to skip
189
+ * @return [Integer] len
190
+ */
191
+ VALUE Stream_skip(VALUE self, VALUE len) {
192
+ struct um_stream *stream = um_get_stream(self);
193
+ stream_skip(stream, NUM2LONG(len), true);
194
+ return len;
195
+ }
196
+
197
+ /* call-seq:
198
+ * stream.each { |data| } -> stream
199
+ *
200
+ * Reads from the target, passing each chunk to the given block.
201
+ *
202
+ * @return [UringMachine::Stream] stream
203
+ */
204
+ VALUE Stream_each(VALUE self) {
205
+ struct um_stream *stream = um_get_stream(self);
206
+ stream_each(stream);
207
+ return self;
208
+ }
209
+
210
+ /* call-seq:
211
+ * stream.resp_decode -> obj
212
+ *
213
+ * Decodes an object from a RESP (Redis protocol) message.
214
+ *
215
+ * @return [any] decoded object
216
+ */
142
217
  VALUE Stream_resp_decode(VALUE self) {
143
218
  struct um_stream *stream = um_get_stream(self);
144
219
  VALUE out_buffer = rb_utf8_str_new_literal("");
@@ -147,6 +222,15 @@ VALUE Stream_resp_decode(VALUE self) {
147
222
  return obj;
148
223
  }
149
224
 
225
+ /* call-seq:
226
+ * stream.resp_encode(obj) -> string
227
+ *
228
+ * Encodes an object into a RESP (Redis protocol) message.
229
+ *
230
+ * @param str [String] string buffer
231
+ * @param obj [any] object to be encoded
232
+ * @return [String] str
233
+ */
150
234
  VALUE Stream_resp_encode(VALUE self, VALUE str, VALUE obj) {
151
235
  struct um_write_buffer buf;
152
236
  write_buffer_init(&buf, str);
@@ -156,11 +240,49 @@ VALUE Stream_resp_encode(VALUE self, VALUE str, VALUE obj) {
156
240
  return str;
157
241
  }
158
242
 
243
+ /* call-seq:
244
+ * stream.eof? -> bool
245
+ *
246
+ * Returns true if stream has reached EOF.
247
+ *
248
+ * @return [bool] EOF reached
249
+ */
159
250
  VALUE Stream_eof_p(VALUE self) {
160
251
  struct um_stream *stream = um_get_stream(self);
161
252
  return stream->eof ? Qtrue : Qfalse;
162
253
  }
163
254
 
255
+ /* call-seq:
256
+ * stream.consumed -> int
257
+ *
258
+ * Returns the total number of bytes consumed from the stream.
259
+ *
260
+ * @return [Integer] total bytes consumed
261
+ */
262
+ VALUE Stream_consumed(VALUE self) {
263
+ struct um_stream *stream = um_get_stream(self);
264
+ return LONG2NUM(stream->consumed_bytes);
265
+ }
266
+
267
+ /* call-seq:
268
+ * stream.pending -> int
269
+ *
270
+ * Returns the number of bytes available for reading.
271
+ *
272
+ * @return [Integer] bytes available
273
+ */
274
+ VALUE Stream_pending(VALUE self) {
275
+ struct um_stream *stream = um_get_stream(self);
276
+ return LONG2NUM(stream->pending_bytes);
277
+ }
278
+
279
+ /* call-seq:
280
+ * stream.clear -> stream
281
+ *
282
+ * Clears all available bytes and stops any ongoing read operation.
283
+ *
284
+ * @return [UM::Stream] self
285
+ */
164
286
  VALUE Stream_clear(VALUE self) {
165
287
  struct um_stream *stream = um_get_stream(self);
166
288
  stream_clear(stream);
@@ -176,11 +298,15 @@ void Init_Stream(void) {
176
298
 
177
299
  rb_define_method(cStream, "get_line", Stream_get_line, 1);
178
300
  rb_define_method(cStream, "get_string", Stream_get_string, 1);
301
+ rb_define_method(cStream, "skip", Stream_skip, 1);
302
+ rb_define_method(cStream, "each", Stream_each, 0);
179
303
 
180
304
  rb_define_method(cStream, "resp_decode", Stream_resp_decode, 0);
181
305
  rb_define_singleton_method(cStream, "resp_encode", Stream_resp_encode, 2);
182
306
 
183
307
  rb_define_method(cStream, "eof?", Stream_eof_p, 0);
308
+ rb_define_method(cStream, "consumed", Stream_consumed, 0);
309
+ rb_define_method(cStream, "pending", Stream_pending, 0);
184
310
  rb_define_method(cStream, "clear", Stream_clear, 0);
185
311
 
186
312
  eStreamRESPError = rb_define_class_under(cStream, "RESPError", rb_eStandardError);