uringmachine 0.28.3 → 0.29.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -1
- data/TODO.md +29 -35
- data/benchmark/common.rb +6 -6
- data/benchmark/gets.rb +49 -0
- data/benchmark/gets_concurrent.rb +122 -0
- data/benchmark/{read_each.rb → output.rb} +27 -24
- data/docs/design/buffer_pool.md +35 -0
- data/docs/um_api.md +2 -0
- data/ext/um/extconf.rb +6 -5
- data/ext/um/um.c +50 -16
- data/ext/um/um.h +102 -32
- data/ext/um/um_buffer_pool.c +248 -0
- data/ext/um/um_class.c +28 -16
- data/ext/um/um_op.c +29 -13
- data/ext/um/um_ssl.c +24 -27
- data/ext/um/um_stream.c +382 -150
- data/ext/um/um_stream_class.c +119 -63
- data/ext/um/um_utils.c +6 -6
- data/grant-2025/tasks.md +13 -7
- data/lib/uringmachine/fiber_scheduler.rb +36 -10
- data/lib/uringmachine/version.rb +1 -1
- data/lib/uringmachine.rb +60 -19
- data/test/helper.rb +4 -0
- data/test/test_fiber.rb +93 -12
- data/test/test_fiber_scheduler.rb +8 -50
- data/test/test_stream.rb +466 -124
- data/test/test_um.rb +133 -49
- metadata +6 -4
- data/ext/um/um_buffer.c +0 -49
data/ext/um/um_stream.c
CHANGED
|
@@ -1,164 +1,428 @@
|
|
|
1
1
|
#include "um.h"
|
|
2
2
|
#include <stdlib.h>
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
stream->
|
|
9
|
-
stream->pos = 0;
|
|
10
|
-
}
|
|
11
|
-
else if (stream->pos >= 1 << 12) {
|
|
12
|
-
rb_str_modify(stream->buffer);
|
|
13
|
-
char *base = RSTRING_PTR(stream->buffer);
|
|
14
|
-
int len_rest = stream->len - stream->pos;
|
|
15
|
-
memmove(base, base + stream->pos, len_rest);
|
|
16
|
-
rb_str_set_len(stream->buffer, len_rest);
|
|
17
|
-
stream->len = len_rest;
|
|
18
|
-
stream->pos = 0;
|
|
4
|
+
inline void stream_add_segment(struct um_stream *stream, struct um_segment *segment) {
|
|
5
|
+
segment->next = NULL;
|
|
6
|
+
if (stream->tail) {
|
|
7
|
+
stream->tail->next = segment;
|
|
8
|
+
stream->tail = segment;
|
|
19
9
|
}
|
|
10
|
+
else
|
|
11
|
+
stream->head = stream->tail = segment;
|
|
12
|
+
stream->pending_len += segment->len;
|
|
20
13
|
}
|
|
21
14
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
15
|
+
inline int stream_process_op_result(struct um_stream *stream, struct um_op_result *result) {
|
|
16
|
+
if (likely(result->res > 0)) {
|
|
17
|
+
if (likely(result->segment)) {
|
|
18
|
+
stream_add_segment(stream, result->segment);
|
|
19
|
+
result->segment = NULL;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
else
|
|
23
|
+
stream->eof = 1;
|
|
30
24
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
size_t ret = um_read_raw(stream->machine, stream->fd, ptr, maxlen);
|
|
25
|
+
return result->res;
|
|
26
|
+
}
|
|
34
27
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
28
|
+
#define STREAM_OP_FLAGS (OP_F_MULTISHOT | OP_F_BUFFER_POOL)
|
|
29
|
+
|
|
30
|
+
void stream_multishot_op_start(struct um_stream *stream) {
|
|
31
|
+
if (!stream->op)
|
|
32
|
+
stream->op = um_op_acquire(stream->machine);
|
|
33
|
+
struct io_uring_sqe *sqe;
|
|
34
|
+
|
|
35
|
+
bp_ensure_commit_level(stream->machine);
|
|
36
|
+
|
|
37
|
+
switch (stream->mode) {
|
|
38
|
+
case STREAM_BP_READ:
|
|
39
|
+
um_prep_op(stream->machine, stream->op, OP_READ_MULTISHOT, 2, STREAM_OP_FLAGS);
|
|
40
|
+
sqe = um_get_sqe(stream->machine, stream->op);
|
|
41
|
+
io_uring_prep_read_multishot(sqe, stream->fd, 0, -1, BP_BGID);
|
|
42
|
+
break;
|
|
43
|
+
case STREAM_BP_RECV:
|
|
44
|
+
um_prep_op(stream->machine, stream->op, OP_RECV_MULTISHOT, 2, STREAM_OP_FLAGS);
|
|
45
|
+
sqe = um_get_sqe(stream->machine, stream->op);
|
|
46
|
+
io_uring_prep_recv_multishot(sqe, stream->fd, NULL, 0, 0);
|
|
47
|
+
sqe->buf_group = BP_BGID;
|
|
48
|
+
sqe->flags |= IOSQE_BUFFER_SELECT;
|
|
49
|
+
break;
|
|
50
|
+
default:
|
|
51
|
+
um_raise_internal_error("Invalid multishot op");
|
|
38
52
|
}
|
|
39
|
-
|
|
40
|
-
stream->len = stream->pos + ret;
|
|
41
|
-
rb_str_set_len(stream->buffer, stream->len);
|
|
42
|
-
return 1;
|
|
53
|
+
stream->op->bp_commit_level = stream->machine->bp_commit_level;
|
|
43
54
|
}
|
|
44
55
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
56
|
+
void stream_multishot_op_stop(struct um_stream *stream) {
|
|
57
|
+
assert(!stream->op);
|
|
58
|
+
|
|
59
|
+
if (!(stream->op->flags & OP_F_CQE_DONE)) {
|
|
60
|
+
stream->op->flags |= OP_F_ASYNC;
|
|
61
|
+
um_cancel_op(stream->machine, stream->op);
|
|
62
|
+
}
|
|
63
|
+
else
|
|
64
|
+
um_op_release(stream->machine, stream->op);
|
|
65
|
+
stream->op = NULL;
|
|
48
66
|
}
|
|
49
67
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
68
|
+
void um_stream_cleanup(struct um_stream *stream) {
|
|
69
|
+
if (stream->op) stream_multishot_op_stop(stream);
|
|
70
|
+
|
|
71
|
+
while (stream->head) {
|
|
72
|
+
struct um_segment *next = stream->head->next;
|
|
73
|
+
um_segment_checkin(stream->machine, stream->head);
|
|
74
|
+
stream->head = next;
|
|
75
|
+
}
|
|
76
|
+
stream->pending_len = 0;
|
|
56
77
|
}
|
|
57
78
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
79
|
+
// returns true if case of ENOBUFS error, sets more to true if more data forthcoming
|
|
80
|
+
inline int stream_process_segments(
|
|
81
|
+
struct um_stream *stream, size_t *total_bytes, int *more) {
|
|
82
|
+
|
|
83
|
+
*more = 0;
|
|
84
|
+
struct um_op_result *result = &stream->op->result;
|
|
85
|
+
stream->op->flags &= ~OP_F_CQE_SEEN;
|
|
86
|
+
while (result) {
|
|
87
|
+
if (unlikely(result->res == -ENOBUFS)) {
|
|
88
|
+
*more = 0;
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
if (unlikely(result->res == -ECANCELED)) {
|
|
92
|
+
*more = 0;
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
um_raise_on_error_result(result->res);
|
|
71
96
|
|
|
72
|
-
|
|
73
|
-
|
|
97
|
+
*more = (result->flags & IORING_CQE_F_MORE);
|
|
98
|
+
*total_bytes += result->res;
|
|
99
|
+
stream_process_op_result(stream, result);
|
|
100
|
+
result = result->next;
|
|
101
|
+
}
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
74
104
|
|
|
75
|
-
|
|
76
|
-
|
|
105
|
+
void stream_clear(struct um_stream *stream) {
|
|
106
|
+
if (stream->op && stream->machine->ring_initialized) {
|
|
107
|
+
if (OP_CQE_SEEN_P(stream->op)) {
|
|
108
|
+
size_t total_bytes = 0;
|
|
109
|
+
int more = false;
|
|
110
|
+
stream_process_segments(stream, &total_bytes, &more);
|
|
111
|
+
um_op_multishot_results_clear(stream->machine, stream->op);
|
|
77
112
|
}
|
|
78
|
-
else if (should_limit_len && pending_len > search_len)
|
|
79
|
-
// hit maxlen
|
|
80
|
-
return Qnil;
|
|
81
113
|
|
|
82
|
-
if (
|
|
83
|
-
|
|
114
|
+
if (OP_CQE_DONE_P(stream->op))
|
|
115
|
+
um_op_release(stream->machine, stream->op);
|
|
116
|
+
else
|
|
117
|
+
um_cancel_op_and_discard_cqe(stream->machine, stream->op);
|
|
118
|
+
|
|
119
|
+
stream->op = NULL;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
while (stream->head) {
|
|
123
|
+
struct um_segment *next = stream->head->next;
|
|
124
|
+
um_segment_checkin(stream->machine, stream->head);
|
|
125
|
+
stream->head = next;
|
|
126
|
+
}
|
|
127
|
+
stream->pending_len = 0;
|
|
128
|
+
|
|
129
|
+
if (stream->working_buffer) {
|
|
130
|
+
bp_buffer_checkin(stream->machine, stream->working_buffer);
|
|
131
|
+
stream->working_buffer = NULL;
|
|
84
132
|
}
|
|
85
133
|
}
|
|
86
134
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
135
|
+
inline void stream_await_segments(struct um_stream *stream) {
|
|
136
|
+
if (unlikely(!stream->op)) stream_multishot_op_start(stream);
|
|
137
|
+
|
|
138
|
+
if (!OP_CQE_SEEN_P(stream->op)) {
|
|
139
|
+
stream->op->flags &= ~OP_F_ASYNC;
|
|
140
|
+
VALUE ret = um_yield(stream->machine);
|
|
141
|
+
stream->op->flags |= OP_F_ASYNC;
|
|
142
|
+
if (!OP_CQE_SEEN_P(stream->op)) RAISE_IF_EXCEPTION(ret);
|
|
143
|
+
RB_GC_GUARD(ret);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
int stream_get_more_segments_bp(struct um_stream *stream) {
|
|
148
|
+
size_t total_bytes = 0;
|
|
149
|
+
int more = false;
|
|
150
|
+
int enobufs = false;
|
|
151
|
+
|
|
152
|
+
while (1) {
|
|
153
|
+
if (unlikely(stream->eof)) return 0;
|
|
154
|
+
|
|
155
|
+
stream_await_segments(stream);
|
|
156
|
+
enobufs = stream_process_segments(stream, &total_bytes, &more);
|
|
157
|
+
um_op_multishot_results_clear(stream->machine, stream->op);
|
|
158
|
+
if (unlikely(enobufs)) {
|
|
159
|
+
int should_restart = stream->pending_len < (stream->machine->bp_buffer_size * 4);
|
|
160
|
+
// int same_threshold = stream->op->bp_commit_level == stream->machine->bp_commit_level;
|
|
161
|
+
|
|
162
|
+
// fprintf(stderr, "%p enobufs total: %ld pending: %ld threshold: %ld bc: %d (same: %d, restart: %d)\n",
|
|
163
|
+
// stream,
|
|
164
|
+
// total_bytes, stream->pending_len, stream->machine->bp_commit_level,
|
|
165
|
+
// stream->machine->bp_buffer_count,
|
|
166
|
+
// same_threshold, should_restart
|
|
167
|
+
// );
|
|
168
|
+
|
|
169
|
+
// If multiple stream ops are happening at the same time, they'll all get
|
|
170
|
+
// ENOBUFS! We track the commit threshold in the op in order to prevent
|
|
171
|
+
// running bp_handle_enobufs() more than once.
|
|
172
|
+
|
|
173
|
+
if (should_restart) {
|
|
174
|
+
if (stream->op->bp_commit_level == stream->machine->bp_commit_level)
|
|
175
|
+
bp_handle_enobufs(stream->machine);
|
|
176
|
+
|
|
177
|
+
um_op_release(stream->machine, stream->op);
|
|
178
|
+
stream->op = NULL;
|
|
179
|
+
// stream_multishot_op_start(stream);
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
um_op_release(stream->machine, stream->op);
|
|
183
|
+
stream->op = NULL;
|
|
184
|
+
}
|
|
93
185
|
|
|
94
|
-
|
|
186
|
+
if (total_bytes) return total_bytes;
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
if (more)
|
|
190
|
+
stream->op->flags &= ~OP_F_CQE_SEEN;
|
|
191
|
+
if (total_bytes || stream->eof) return total_bytes;
|
|
95
192
|
}
|
|
96
193
|
}
|
|
194
|
+
}
|
|
97
195
|
|
|
98
|
-
|
|
99
|
-
stream->
|
|
196
|
+
int stream_get_more_segments_ssl(struct um_stream *stream) {
|
|
197
|
+
if (!stream->working_buffer)
|
|
198
|
+
stream->working_buffer = bp_buffer_checkout(stream->machine);
|
|
100
199
|
|
|
101
|
-
|
|
200
|
+
char *ptr = stream->working_buffer->buf + stream->working_buffer->pos;
|
|
201
|
+
size_t maxlen = stream->working_buffer->len - stream->working_buffer->pos;
|
|
202
|
+
int res = um_ssl_read_raw(stream->machine, stream->target, ptr, maxlen);
|
|
203
|
+
if (res == 0) return 0;
|
|
204
|
+
if (res < 0) rb_raise(eUMError, "Failed to read segment");
|
|
102
205
|
|
|
103
|
-
|
|
104
|
-
|
|
206
|
+
struct um_segment *segment = bp_buffer_consume(stream->machine, stream->working_buffer, res);
|
|
207
|
+
if ((size_t)res == maxlen) {
|
|
208
|
+
bp_buffer_checkin(stream->machine, stream->working_buffer);
|
|
209
|
+
stream->working_buffer = NULL;
|
|
210
|
+
}
|
|
211
|
+
stream_add_segment(stream, segment);
|
|
212
|
+
return 1;
|
|
105
213
|
}
|
|
106
214
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
215
|
+
int stream_get_more_segments(struct um_stream *stream) {
|
|
216
|
+
switch (stream->mode) {
|
|
217
|
+
case STREAM_BP_READ:
|
|
218
|
+
case STREAM_BP_RECV:
|
|
219
|
+
return stream_get_more_segments_bp(stream);
|
|
220
|
+
case STREAM_SSL:
|
|
221
|
+
return stream_get_more_segments_ssl(stream);
|
|
222
|
+
default:
|
|
223
|
+
rb_raise(eUMError, "Invalid stream mode");
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
///////////////////////////////////////////////////////////////////////////////////////
|
|
228
|
+
|
|
229
|
+
inline void stream_shift_head(struct um_stream *stream) {
|
|
230
|
+
struct um_segment *consumed = stream->head;
|
|
231
|
+
stream->head = consumed->next;
|
|
232
|
+
if (!stream->head) stream->tail = NULL;
|
|
233
|
+
um_segment_checkin(stream->machine, consumed);
|
|
234
|
+
stream->pos = 0;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
inline void stream_skip(struct um_stream *stream, size_t inc, int safe_inc) {
|
|
238
|
+
while (inc) {
|
|
239
|
+
size_t segment_len = stream->head->len - stream->pos;
|
|
240
|
+
size_t inc_len = (segment_len <= inc) ? segment_len : inc;
|
|
241
|
+
inc -= inc_len;
|
|
242
|
+
stream->pos += inc_len;
|
|
243
|
+
stream->pending_len -= inc_len;
|
|
244
|
+
if (stream->pos == stream->head->len) {
|
|
245
|
+
stream_shift_head(stream);
|
|
246
|
+
if (inc && safe_inc && !stream->head) {
|
|
247
|
+
if (!stream_get_more_segments(stream)) break;
|
|
248
|
+
}
|
|
111
249
|
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
112
252
|
|
|
113
|
-
|
|
114
|
-
|
|
253
|
+
inline void stream_copy(struct um_stream *stream, char *dest, size_t len) {
|
|
254
|
+
while (len) {
|
|
255
|
+
char *segment_ptr = stream->head->ptr + stream->pos;
|
|
256
|
+
size_t segment_len = stream->head->len - stream->pos;
|
|
257
|
+
size_t cpy_len = (segment_len <= len) ? segment_len : len;
|
|
258
|
+
memcpy(dest, segment_ptr, cpy_len);
|
|
259
|
+
|
|
260
|
+
len -= cpy_len;
|
|
261
|
+
stream->pos += cpy_len;
|
|
262
|
+
stream->pending_len -= cpy_len;
|
|
263
|
+
dest += cpy_len;
|
|
264
|
+
if (stream->pos == stream->head->len) stream_shift_head(stream);
|
|
265
|
+
}
|
|
115
266
|
}
|
|
116
267
|
|
|
117
|
-
VALUE
|
|
118
|
-
|
|
268
|
+
VALUE stream_consume_string(struct um_stream *stream, VALUE out_buffer, size_t len, size_t inc, int safe_inc) {
|
|
269
|
+
VALUE str = Qnil;
|
|
270
|
+
if (!NIL_P(out_buffer)) {
|
|
271
|
+
str = out_buffer;
|
|
272
|
+
size_t str_len = RSTRING_LEN(str);
|
|
273
|
+
if (str_len < len)
|
|
274
|
+
rb_str_resize(str, len);
|
|
275
|
+
else if (str_len > len)
|
|
276
|
+
rb_str_set_len(str, len);
|
|
277
|
+
}
|
|
278
|
+
else
|
|
279
|
+
str = rb_str_new(NULL, len);
|
|
280
|
+
char *dest = RSTRING_PTR(str);
|
|
281
|
+
|
|
282
|
+
stream_copy(stream, dest, len);
|
|
283
|
+
stream_skip(stream, inc, safe_inc);
|
|
284
|
+
return str;
|
|
285
|
+
RB_GC_GUARD(str);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
VALUE stream_get_line(struct um_stream *stream, VALUE out_buffer, size_t maxlen) {
|
|
289
|
+
// if (stream->head) {
|
|
290
|
+
// fprintf(stderr, "head: %p pos: %ld: %.*s\n",
|
|
291
|
+
// stream->head, stream->pos,
|
|
292
|
+
// (int)(stream->head->len - stream->pos), stream->head->ptr + stream->pos
|
|
293
|
+
// );
|
|
294
|
+
// }
|
|
295
|
+
|
|
296
|
+
if (unlikely(stream->eof && !stream->head)) return Qnil;
|
|
297
|
+
if (!stream->tail && !stream_get_more_segments(stream)) return Qnil;
|
|
298
|
+
|
|
299
|
+
struct um_segment *last = NULL;
|
|
300
|
+
struct um_segment *current = stream->head;
|
|
301
|
+
size_t remaining_len = maxlen;
|
|
302
|
+
size_t total_len = 0;
|
|
303
|
+
size_t inc = 1;
|
|
304
|
+
size_t pos = stream->pos;
|
|
305
|
+
|
|
119
306
|
while (true) {
|
|
120
|
-
|
|
307
|
+
size_t segment_len = current->len - pos;
|
|
308
|
+
size_t search_len = segment_len;
|
|
309
|
+
if (maxlen && (search_len > remaining_len)) search_len = remaining_len;
|
|
310
|
+
char *start = current->ptr + pos;
|
|
311
|
+
char *lf_ptr = memchr(start, '\n', search_len);
|
|
312
|
+
|
|
121
313
|
if (lf_ptr) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
314
|
+
size_t len = lf_ptr - start;
|
|
315
|
+
|
|
316
|
+
total_len += len;
|
|
317
|
+
|
|
318
|
+
// search for \r
|
|
319
|
+
if (total_len) {
|
|
320
|
+
if (len) {
|
|
321
|
+
if (start[len - 1] == '\r') {
|
|
322
|
+
total_len -= 1;
|
|
323
|
+
inc = 2;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
if (last && (((char *)last->ptr)[last->len - 1] == '\r')) {
|
|
328
|
+
total_len -= 1;
|
|
329
|
+
inc = 2;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
131
332
|
}
|
|
132
333
|
|
|
133
|
-
|
|
134
|
-
return out_buffer;
|
|
334
|
+
return stream_consume_string(stream, out_buffer, total_len, inc, false);
|
|
135
335
|
}
|
|
336
|
+
else {
|
|
337
|
+
// not found, early return if segment len exceeds maxlen
|
|
338
|
+
if (maxlen && segment_len >= maxlen) return Qnil;
|
|
136
339
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
340
|
+
total_len += segment_len;
|
|
341
|
+
remaining_len -= segment_len;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (!current->next) {
|
|
345
|
+
if (!stream_get_more_segments(stream)) {
|
|
346
|
+
return Qnil;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
last = current;
|
|
351
|
+
current = current->next;
|
|
352
|
+
pos = 0;
|
|
142
353
|
}
|
|
143
354
|
}
|
|
144
355
|
|
|
145
|
-
VALUE
|
|
146
|
-
|
|
356
|
+
VALUE stream_get_string(struct um_stream *stream, VALUE out_buffer, ssize_t len, size_t inc, int safe_inc) {
|
|
357
|
+
if (unlikely(stream->eof && !stream->head)) return Qnil;
|
|
358
|
+
if (!stream->tail && !stream_get_more_segments(stream)) return Qnil;
|
|
147
359
|
|
|
148
|
-
|
|
149
|
-
|
|
360
|
+
struct um_segment *current = stream->head;
|
|
361
|
+
size_t abs_len = labs(len);
|
|
362
|
+
size_t remaining_len = abs_len;
|
|
363
|
+
size_t total_len = 0;
|
|
364
|
+
size_t pos = stream->pos;
|
|
150
365
|
|
|
151
|
-
|
|
152
|
-
|
|
366
|
+
while (true) {
|
|
367
|
+
size_t segment_len = current->len - pos;
|
|
368
|
+
if (abs_len && segment_len > remaining_len) {
|
|
369
|
+
segment_len = remaining_len;
|
|
370
|
+
}
|
|
371
|
+
total_len += segment_len;
|
|
372
|
+
if (abs_len) {
|
|
373
|
+
remaining_len -= segment_len;
|
|
374
|
+
if (!remaining_len)
|
|
375
|
+
return stream_consume_string(stream, out_buffer, total_len, inc, safe_inc);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (!current->next) {
|
|
379
|
+
if (len <= 0)
|
|
380
|
+
return stream_consume_string(stream, out_buffer, total_len, inc, safe_inc);
|
|
381
|
+
|
|
382
|
+
if (!stream_get_more_segments(stream))
|
|
383
|
+
return Qnil;
|
|
384
|
+
}
|
|
385
|
+
current = current->next;
|
|
386
|
+
pos = 0;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
VALUE resp_get_line(struct um_stream *stream, VALUE out_buffer) {
|
|
391
|
+
if (unlikely(stream->eof && !stream->head)) return Qnil;
|
|
392
|
+
if (!stream->tail && !stream_get_more_segments(stream)) return Qnil;
|
|
393
|
+
|
|
394
|
+
struct um_segment *current = stream->head;
|
|
395
|
+
size_t total_len = 0;
|
|
396
|
+
size_t pos = stream->pos;
|
|
397
|
+
|
|
398
|
+
while (true) {
|
|
399
|
+
size_t segment_len = current->len - pos;
|
|
400
|
+
char *start = current->ptr + pos;
|
|
401
|
+
char *lf_ptr = memchr(start, '\r', segment_len);
|
|
402
|
+
if (lf_ptr) {
|
|
403
|
+
size_t len = lf_ptr - start;
|
|
404
|
+
total_len += len;
|
|
405
|
+
return stream_consume_string(stream, out_buffer, total_len, 2, true);
|
|
406
|
+
}
|
|
407
|
+
else
|
|
408
|
+
total_len += segment_len;
|
|
153
409
|
|
|
154
|
-
|
|
410
|
+
if (!current->next)
|
|
411
|
+
if (!stream_get_more_segments(stream)) return Qnil;
|
|
155
412
|
|
|
156
|
-
|
|
157
|
-
|
|
413
|
+
current = current->next;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
inline VALUE resp_get_string(struct um_stream *stream, ulong len, VALUE out_buffer) {
|
|
418
|
+
return stream_get_string(stream, out_buffer, len, 2, true);
|
|
158
419
|
}
|
|
159
420
|
|
|
160
421
|
inline ulong resp_parse_length_field(const char *ptr, int len) {
|
|
161
|
-
|
|
422
|
+
ulong acc = 0;
|
|
423
|
+
for(int i = 1; i < len; i++)
|
|
424
|
+
acc = acc * 10 + (ptr[i] - '0');
|
|
425
|
+
return acc;
|
|
162
426
|
}
|
|
163
427
|
|
|
164
428
|
VALUE resp_decode_hash(struct um_stream *stream, VALUE out_buffer, ulong len) {
|
|
@@ -180,8 +444,7 @@ VALUE resp_decode_array(struct um_stream *stream, VALUE out_buffer, ulong len) {
|
|
|
180
444
|
VALUE array = rb_ary_new2(len);
|
|
181
445
|
|
|
182
446
|
for (ulong i = 0; i < len; i++) {
|
|
183
|
-
VALUE
|
|
184
|
-
VALUE value = resp_decode(stream, buf);
|
|
447
|
+
VALUE value = resp_decode(stream, out_buffer);
|
|
185
448
|
rb_ary_push(array, value);
|
|
186
449
|
RB_GC_GUARD(value);
|
|
187
450
|
}
|
|
@@ -191,11 +454,11 @@ VALUE resp_decode_array(struct um_stream *stream, VALUE out_buffer, ulong len) {
|
|
|
191
454
|
}
|
|
192
455
|
|
|
193
456
|
static inline VALUE resp_decode_simple_string(char *ptr, ulong len) {
|
|
194
|
-
return
|
|
457
|
+
return rb_str_new(ptr + 1, len - 1);
|
|
195
458
|
}
|
|
196
459
|
|
|
197
|
-
static inline VALUE resp_decode_string(struct um_stream *stream, ulong len) {
|
|
198
|
-
return resp_get_string(stream, len,
|
|
460
|
+
static inline VALUE resp_decode_string(struct um_stream *stream, VALUE out_buffer, ulong len) {
|
|
461
|
+
return resp_get_string(stream, len, out_buffer);
|
|
199
462
|
}
|
|
200
463
|
|
|
201
464
|
static inline VALUE resp_decode_string_with_encoding(struct um_stream *stream, VALUE out_buffer, ulong len) {
|
|
@@ -221,7 +484,7 @@ static inline VALUE resp_decode_simple_error(char *ptr, ulong len) {
|
|
|
221
484
|
static ID ID_new = 0;
|
|
222
485
|
if (!ID_new) ID_new = rb_intern("new");
|
|
223
486
|
|
|
224
|
-
VALUE msg =
|
|
487
|
+
VALUE msg = rb_str_new(ptr + 1, len - 1);
|
|
225
488
|
VALUE err = rb_funcall(eStreamRESPError, ID_new, 1, msg);
|
|
226
489
|
RB_GC_GUARD(msg);
|
|
227
490
|
return err;
|
|
@@ -231,7 +494,7 @@ static inline VALUE resp_decode_error(struct um_stream *stream, VALUE out_buffer
|
|
|
231
494
|
static ID ID_new = 0;
|
|
232
495
|
if (!ID_new) ID_new = rb_intern("new");
|
|
233
496
|
|
|
234
|
-
VALUE msg = resp_decode_string(stream, len);
|
|
497
|
+
VALUE msg = resp_decode_string(stream, out_buffer, len);
|
|
235
498
|
VALUE err = rb_funcall(eStreamRESPError, ID_new, 1, msg);
|
|
236
499
|
RB_GC_GUARD(msg);
|
|
237
500
|
return err;
|
|
@@ -262,7 +525,7 @@ VALUE resp_decode(struct um_stream *stream, VALUE out_buffer) {
|
|
|
262
525
|
return resp_decode_simple_string(ptr, len);
|
|
263
526
|
case '$': // string
|
|
264
527
|
data_len = resp_parse_length_field(ptr, len);
|
|
265
|
-
return resp_decode_string(stream, data_len);
|
|
528
|
+
return resp_decode_string(stream, out_buffer, data_len);
|
|
266
529
|
case '=': // string with encoding
|
|
267
530
|
data_len = resp_parse_length_field(ptr, len);
|
|
268
531
|
return resp_decode_string_with_encoding(stream, out_buffer, data_len);
|
|
@@ -404,34 +667,3 @@ void resp_encode(struct um_write_buffer *buf, VALUE obj) {
|
|
|
404
667
|
um_raise_internal_error("Can't encode object");
|
|
405
668
|
}
|
|
406
669
|
}
|
|
407
|
-
|
|
408
|
-
void resp_encode_cmd(struct um_write_buffer *buf, int argc, VALUE *argv) {
|
|
409
|
-
char tmp1[48];
|
|
410
|
-
char tmp2[60];
|
|
411
|
-
|
|
412
|
-
sprintf(tmp1, "*%d\r\n", argc);
|
|
413
|
-
write_buffer_append_cstr(buf, tmp1);
|
|
414
|
-
for (int i = 0; i < argc; i++) {
|
|
415
|
-
switch (TYPE(argv[i])) {
|
|
416
|
-
case T_FIXNUM:
|
|
417
|
-
sprintf(tmp1, "%ld", NUM2LONG(argv[i]));
|
|
418
|
-
sprintf(tmp2, "$%ld\r\n%s\r\n", strlen(tmp1), (char *)tmp1);
|
|
419
|
-
write_buffer_append_cstr(buf, tmp2);
|
|
420
|
-
break;
|
|
421
|
-
case T_FLOAT:
|
|
422
|
-
sprintf(tmp1, "%lg", NUM2DBL(argv[i]));
|
|
423
|
-
sprintf(tmp2, "$%ld\r\n%s\r\n", strlen(tmp1), (char *)tmp1);
|
|
424
|
-
write_buffer_append_cstr(buf, tmp2);
|
|
425
|
-
break;
|
|
426
|
-
case T_STRING:
|
|
427
|
-
write_buffer_append_resp_bulk_string(buf, argv[i]);
|
|
428
|
-
break;
|
|
429
|
-
case T_SYMBOL:
|
|
430
|
-
write_buffer_append_resp_bulk_string(buf, rb_sym_to_s(argv[i]));
|
|
431
|
-
break;
|
|
432
|
-
default:
|
|
433
|
-
um_raise_internal_error("Can't encode object");
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
return;
|
|
437
|
-
}
|