uringmachine 0.29.2 → 0.31.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.
@@ -0,0 +1,394 @@
1
+ #include "um.h"
2
+
3
+ VALUE cConnection;
4
+ VALUE eConnectionRESPError;
5
+
6
+ VALUE SYM_fd;
7
+ VALUE SYM_socket;
8
+ VALUE SYM_ssl;
9
+
10
+ inline int connection_has_target_obj_p(struct um_connection *conn) {
11
+ switch (conn->mode) {
12
+ case CONNECTION_SSL:
13
+ case CONNECTION_STRING:
14
+ case CONNECTION_IO_BUFFER:
15
+ return true;
16
+ default:
17
+ return false;
18
+ }
19
+ }
20
+
21
+ inline void connection_mark_segments(struct um_connection *conn) {
22
+ struct um_segment *curr = conn->head;
23
+ while (curr) {
24
+ // rb_gc_mark_movable(curr->obj);
25
+ curr = curr->next;
26
+ }
27
+ }
28
+
29
+ inline void connection_compact_segments(struct um_connection *conn) {
30
+ struct um_segment *curr = conn->head;
31
+ while (curr) {
32
+ // curr->obj = rb_gc_location(curr->obj);
33
+ curr = curr->next;
34
+ }
35
+ }
36
+
37
+ static void Connection_mark(void *ptr) {
38
+ struct um_connection *conn = ptr;
39
+ rb_gc_mark_movable(conn->self);
40
+ rb_gc_mark_movable(conn->machine->self);
41
+
42
+ if (connection_has_target_obj_p(conn)) {
43
+ rb_gc_mark_movable(conn->target);
44
+ connection_mark_segments(conn);
45
+ }
46
+ }
47
+
48
+ static void Connection_compact(void *ptr) {
49
+ struct um_connection *conn = ptr;
50
+ conn->self = rb_gc_location(conn->self);
51
+
52
+ if (connection_has_target_obj_p(conn)) {
53
+ conn->target = rb_gc_location(conn->target);
54
+ connection_compact_segments(conn);
55
+ }
56
+ }
57
+
58
+ static void Connection_free(void *ptr) {
59
+ struct um_connection *conn = ptr;
60
+ connection_clear(conn);
61
+ }
62
+
63
+ static const rb_data_type_t Connection_type = {
64
+ .wrap_struct_name = "UringMachine::Connection",
65
+ .function = {
66
+ .dmark = Connection_mark,
67
+ .dfree = Connection_free,
68
+ .dsize = NULL,
69
+ .dcompact = Connection_compact
70
+ },
71
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
72
+ };
73
+
74
+ static VALUE Connection_allocate(VALUE klass) {
75
+ struct um_connection *conn;
76
+ VALUE self = TypedData_Make_Struct(klass, struct um_connection, &Connection_type, conn);
77
+ return self;
78
+ }
79
+
80
+ static inline struct um_connection *um_get_connection(VALUE self) {
81
+ struct um_connection *conn;
82
+ TypedData_Get_Struct(self, struct um_connection, &Connection_type, conn);
83
+ return conn;
84
+ }
85
+
86
+ static inline void connection_set_target(struct um_connection *conn, VALUE target, enum um_connection_mode mode) {
87
+ conn->mode = mode;
88
+ switch (mode) {
89
+ case CONNECTION_FD:
90
+ case CONNECTION_SOCKET:
91
+ conn->fd = NUM2INT(target);
92
+ return;
93
+ case CONNECTION_SSL:
94
+ conn->target = target;
95
+ um_ssl_set_bio(conn->machine, target);
96
+ return;
97
+ default:
98
+ rb_raise(eUMError, "Invalid connection mode");
99
+ }
100
+ }
101
+
102
+ static inline void connection_setup(struct um_connection *conn, VALUE target, VALUE mode) {
103
+ conn->working_buffer = NULL;
104
+ if (NIL_P(mode)) {
105
+ if (TYPE(target) == T_DATA)
106
+ connection_set_target(conn, target, CONNECTION_SSL);
107
+ else
108
+ connection_set_target(conn, target, CONNECTION_FD);
109
+ }
110
+ else if (mode == SYM_fd)
111
+ connection_set_target(conn, target, CONNECTION_FD);
112
+ else if (mode == SYM_socket)
113
+ connection_set_target(conn, target, CONNECTION_SOCKET);
114
+ else if (mode == SYM_ssl)
115
+ connection_set_target(conn, target, CONNECTION_SSL);
116
+ else
117
+ rb_raise(eUMError, "Invalid connection mode");
118
+ }
119
+
120
+ /* call-seq:
121
+ * UM::Stream.new(machine, fd, mode = nil) -> conn
122
+ * machine.connection(fd, mode = nil) -> conn
123
+ * machine.connection(fd, mode = nil) { |conn| ... }
124
+ *
125
+ * Initializes a new connection with the given UringMachine instance, target and
126
+ * optional mode. The target maybe a file descriptor, or an instance of
127
+ * OpenSSL::SSL::SSLSocket. In case of an SSL socket, the mode should be :ssl.
128
+ *
129
+ * @param machine [UringMachine] UringMachine instance
130
+ * @param target [integer, OpenSSL::SSL::SSLSocket] connection target: file descriptor or SSL socket
131
+ * @param mode [Symbol] optional connection mode: :fd, :socket, :ssl
132
+ * @return [void]
133
+ */
134
+ VALUE Connection_initialize(int argc, VALUE *argv, VALUE self) {
135
+ VALUE machine;
136
+ VALUE target;
137
+ VALUE mode;
138
+ rb_scan_args(argc, argv, "21", &machine, &target, &mode);
139
+
140
+ struct um_connection *conn = um_get_connection(self);
141
+ memset(conn, 0, sizeof(struct um_connection));
142
+
143
+ RB_OBJ_WRITE(self, &conn->self, self);
144
+ conn->machine = um_get_machine(machine);
145
+ connection_setup(conn, target, mode);
146
+
147
+ return self;
148
+ }
149
+
150
+ /* call-seq:
151
+ * conn.mode -> mode
152
+ *
153
+ * Returns the connection mode.
154
+ *
155
+ * @return [Symbol] connection mode
156
+ */
157
+ VALUE Connection_mode(VALUE self) {
158
+ struct um_connection *conn = um_get_connection(self);
159
+ switch (conn->mode) {
160
+ case CONNECTION_FD: return SYM_fd;
161
+ case CONNECTION_SOCKET: return SYM_socket;
162
+ case CONNECTION_SSL: return SYM_ssl;
163
+ default: return Qnil;
164
+ }
165
+ return Qnil;
166
+ }
167
+
168
+ /* call-seq:
169
+ * conn.read_line(limit) -> str
170
+ *
171
+ * Reads from the string until a newline character is encountered. Returns the
172
+ * line without the newline delimiter. If limit is 0, the line length is not
173
+ * limited. If no newline delimiter is found before EOF, returns nil.
174
+ *
175
+ * @param limit [integer] maximum line length (0 means no limit)
176
+ * @return [String, nil] read data or nil
177
+ */
178
+ VALUE Connection_read_line(VALUE self, VALUE limit) {
179
+ struct um_connection *conn = um_get_connection(self);
180
+ return connection_read_line(conn, Qnil, NUM2ULONG(limit));
181
+ }
182
+
183
+ /* call-seq:
184
+ * conn.read(len) -> str
185
+ *
186
+ * Reads len bytes from the conn. If len is 0, reads all available bytes. If
187
+ * len is negative, reads up to -len available bytes. If len is positive and eof
188
+ * is encountered before len bytes are read, returns nil.
189
+ *
190
+ * @param len [integer] number of bytes to read
191
+ * @return [String, nil] read data or nil
192
+ */
193
+ VALUE Connection_read(VALUE self, VALUE len) {
194
+ struct um_connection *conn = um_get_connection(self);
195
+ return connection_read(conn, Qnil, NUM2LONG(len), 0, false);
196
+ }
197
+
198
+ /* call-seq:
199
+ * conn.read_to_delim(delim, limit) -> str
200
+ *
201
+ * Reads from the string until a the given delimiter is encountered. Returns the
202
+ * line without the delimiter. If limit is 0, the length is not limited. If a
203
+ * delimiter is not found before EOF and limit is 0 or greater, returns nil.
204
+ *
205
+ * If no delimiter is found before EOF and limit is negative, returns the
206
+ * buffered data up to EOF or until the absolute-value length limit is reached.
207
+ *
208
+ * The `delim` parameter must be a single byte string.
209
+ *
210
+ * @param delim [String] delimiter (single byte) @param limit [integer] maximum
211
+ * line length (0 means no limit) @return [String, nil] read data or nil
212
+ */
213
+ VALUE Connection_read_to_delim(VALUE self, VALUE delim, VALUE limit) {
214
+ struct um_connection *conn = um_get_connection(self);
215
+ return connection_read_to_delim(conn, Qnil, delim, NUM2LONG(limit));
216
+ }
217
+
218
+ /* call-seq:
219
+ * conn.skip(len) -> len
220
+ *
221
+ * Skips len bytes in the conn.
222
+ *
223
+ * @param len [integer] number of bytes to skip
224
+ * @return [Integer] len
225
+ */
226
+ VALUE Connection_skip(VALUE self, VALUE len) {
227
+ struct um_connection *conn = um_get_connection(self);
228
+ connection_skip(conn, NUM2LONG(len), true);
229
+ return len;
230
+ }
231
+
232
+ /* call-seq:
233
+ * conn.read_each { |data| } -> conn
234
+ *
235
+ * Reads from the target, passing each chunk to the given block.
236
+ *
237
+ * @return [UringMachine::Connection] conn
238
+ */
239
+ VALUE Connection_read_each(VALUE self) {
240
+ struct um_connection *conn = um_get_connection(self);
241
+ connection_read_each(conn);
242
+ return self;
243
+ }
244
+
245
+ /* call-seq:
246
+ * conn.write(*bufs) -> len
247
+ *
248
+ * Writes to the connection, ensuring that all data has been written before
249
+ * returning the total number of bytes written.
250
+ *
251
+ * @param bufs [Array<String, IO::Buffer>] data to write
252
+ * @return [Integer] total bytes written
253
+ */
254
+ VALUE Connection_write(int argc, VALUE *argv, VALUE self) {
255
+ struct um_connection *conn = um_get_connection(self);
256
+ return connection_writev(conn, argc, argv);
257
+ }
258
+
259
+ /* call-seq:
260
+ * conn.resp_read -> obj
261
+ *
262
+ * Decodes an object from a RESP (Redis protocol) message.
263
+ *
264
+ * @return [any] decoded object
265
+ */
266
+ VALUE Connection_resp_read(VALUE self) {
267
+ struct um_connection *conn = um_get_connection(self);
268
+ VALUE out_buffer = rb_utf8_str_new_literal("");
269
+ VALUE obj = resp_read(conn, out_buffer);
270
+ RB_GC_GUARD(out_buffer);
271
+ return obj;
272
+ }
273
+
274
+ /* call-seq:
275
+ * conn.resp_write(obj) -> conn
276
+ *
277
+ * Writes the given object using RESP (Redis protocol) to the connection target.
278
+ * Returns the number of bytes written.
279
+ *
280
+ * @param obj [any] object to write
281
+ * @return [Integer] total bytes written
282
+ */
283
+ VALUE Connection_resp_write(VALUE self, VALUE obj) {
284
+ struct um_connection *conn = um_get_connection(self);
285
+
286
+ VALUE str = rb_str_new(NULL, 0);
287
+ struct um_write_buffer buf;
288
+ write_buffer_init(&buf, str);
289
+ rb_str_modify(str);
290
+ resp_encode(&buf, obj);
291
+ write_buffer_update_len(&buf);
292
+
293
+ size_t len = connection_write_raw(conn, buf.ptr, buf.len);
294
+ RB_GC_GUARD(str);
295
+ return ULONG2NUM(len);
296
+ }
297
+
298
+ /* call-seq:
299
+ * conn.resp_encode(obj) -> string
300
+ *
301
+ * Encodes an object into a RESP (Redis protocol) message.
302
+ *
303
+ * @param str [String] string buffer
304
+ * @param obj [any] object to be encoded
305
+ * @return [String] str
306
+ */
307
+ VALUE Connection_resp_encode(VALUE self, VALUE str, VALUE obj) {
308
+ struct um_write_buffer buf;
309
+ write_buffer_init(&buf, str);
310
+ rb_str_modify(str);
311
+ resp_encode(&buf, obj);
312
+ write_buffer_update_len(&buf);
313
+ return str;
314
+ }
315
+
316
+ /* call-seq:
317
+ * conn.eof? -> bool
318
+ *
319
+ * Returns true if connection has reached EOF.
320
+ *
321
+ * @return [bool] EOF reached
322
+ */
323
+ VALUE Connection_eof_p(VALUE self) {
324
+ struct um_connection *conn = um_get_connection(self);
325
+ return conn->eof ? Qtrue : Qfalse;
326
+ }
327
+
328
+ /* call-seq:
329
+ * conn.consumed -> int
330
+ *
331
+ * Returns the total number of bytes consumed from the conn.
332
+ *
333
+ * @return [Integer] total bytes consumed
334
+ */
335
+ VALUE Connection_consumed(VALUE self) {
336
+ struct um_connection *conn = um_get_connection(self);
337
+ return LONG2NUM(conn->consumed_bytes);
338
+ }
339
+
340
+ /* call-seq:
341
+ * conn.pending -> int
342
+ *
343
+ * Returns the number of bytes available for reading.
344
+ *
345
+ * @return [Integer] bytes available
346
+ */
347
+ VALUE Connection_pending(VALUE self) {
348
+ struct um_connection *conn = um_get_connection(self);
349
+ return LONG2NUM(conn->pending_bytes);
350
+ }
351
+
352
+ /* call-seq:
353
+ * conn.clear -> conn
354
+ *
355
+ * Clears all available bytes and stops any ongoing read operation.
356
+ *
357
+ * @return [UM::Stream] self
358
+ */
359
+ VALUE Connection_clear(VALUE self) {
360
+ struct um_connection *conn = um_get_connection(self);
361
+ connection_clear(conn);
362
+ return self;
363
+ }
364
+
365
+ void Init_Stream(void) {
366
+ cConnection = rb_define_class_under(cUM, "Connection", rb_cObject);
367
+ rb_define_alloc_func(cConnection, Connection_allocate);
368
+
369
+ rb_define_method(cConnection, "initialize", Connection_initialize, -1);
370
+ rb_define_method(cConnection, "mode", Connection_mode, 0);
371
+
372
+ rb_define_method(cConnection, "read_line", Connection_read_line, 1);
373
+ rb_define_method(cConnection, "read", Connection_read, 1);
374
+ rb_define_method(cConnection, "read_to_delim", Connection_read_to_delim, 2);
375
+ rb_define_method(cConnection, "skip", Connection_skip, 1);
376
+ rb_define_method(cConnection, "read_each", Connection_read_each, 0);
377
+
378
+ rb_define_method(cConnection, "write", Connection_write, -1);
379
+
380
+ rb_define_method(cConnection, "resp_read", Connection_resp_read, 0);
381
+ rb_define_method(cConnection, "resp_write", Connection_resp_write, 1);
382
+ rb_define_singleton_method(cConnection, "resp_encode", Connection_resp_encode, 2);
383
+
384
+ rb_define_method(cConnection, "eof?", Connection_eof_p, 0);
385
+ rb_define_method(cConnection, "consumed", Connection_consumed, 0);
386
+ rb_define_method(cConnection, "pending", Connection_pending, 0);
387
+ rb_define_method(cConnection, "clear", Connection_clear, 0);
388
+
389
+ eConnectionRESPError = rb_define_class_under(cConnection, "RESPError", rb_eStandardError);
390
+
391
+ SYM_fd = ID2SYM(rb_intern("fd"));
392
+ SYM_socket = ID2SYM(rb_intern("socket"));
393
+ SYM_ssl = ID2SYM(rb_intern("ssl"));
394
+ }
data/ext/um/um_ssl.c CHANGED
@@ -8,14 +8,14 @@
8
8
  static int um_bio_read(BIO *bio, char *buf, int blen)
9
9
  {
10
10
  struct um *machine = (struct um *)BIO_get_ex_data(bio, IDX_BIO_DATA_MACHINE);
11
- long fd = (long)BIO_get_ex_data(bio, IDX_BIO_DATA_FD);
11
+ int fd = (int)(long)BIO_get_ex_data(bio, IDX_BIO_DATA_FD);
12
12
  return (int)um_read_raw(machine, fd, buf, blen);
13
13
  }
14
14
 
15
15
  static int um_bio_write(BIO *bio, const char *buf, int blen)
16
16
  {
17
17
  struct um *machine = (struct um *)BIO_get_ex_data(bio, IDX_BIO_DATA_MACHINE);
18
- long fd = (long)BIO_get_ex_data(bio, IDX_BIO_DATA_FD);
18
+ int fd = (int)(long)BIO_get_ex_data(bio, IDX_BIO_DATA_FD);
19
19
  return (int)um_write_raw(machine, fd, buf, blen);
20
20
  }
21
21
 
@@ -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,23 +84,59 @@ 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;
106
107
  }
108
+
109
+ int um_ssl_write_raw(struct um *machine, VALUE ssl_obj, const char *buffer, size_t len) {
110
+ SSL *ssl = RTYPEDDATA_GET_DATA(ssl_obj);
111
+ if (unlikely(!len)) return INT2NUM(0);
112
+
113
+ int ret = SSL_write(ssl, buffer, (int)len);
114
+ if (ret <= 0) rb_raise(eUMError, "Failed to write");
115
+
116
+ return ret;
117
+ }
118
+
119
+ int um_ssl_write_all(struct um *machine, VALUE ssl_obj, VALUE buf) {
120
+ SSL *ssl = RTYPEDDATA_GET_DATA(ssl_obj);
121
+ const char *base;
122
+ size_t size;
123
+ um_get_buffer_bytes_for_writing(buf, (const void **)&base, &size, true);
124
+
125
+ size_t left = size;
126
+ while (left) {
127
+ int ret = SSL_write(ssl, base, (int)left);
128
+ if (ret <= 0) rb_raise(eUMError, "Failed to write");
129
+
130
+ left -= ret;
131
+ base += ret;
132
+ }
133
+
134
+ return size;
135
+ }
136
+
137
+ int um_ssl_writev(struct um *machine, VALUE ssl, int argc, VALUE *argv) {
138
+ size_t total = 0;
139
+ for (int i = 0; i < argc; i++)
140
+ total += um_ssl_write_all(machine, ssl, argv[i]);
141
+ return total;
142
+ }
data/ext/um/um_utils.c CHANGED
@@ -159,7 +159,7 @@ int um_setup_buffer_ring(struct um *machine, unsigned size, unsigned count) {
159
159
  return bg_id;
160
160
  }
161
161
 
162
- inline VALUE um_get_string_from_buffer_ring(struct um *machine, int bgid, __s32 result, __u32 flags) {
162
+ inline VALUE um_read_from_buffer_ring(struct um *machine, int bgid, __s32 result, __u32 flags) {
163
163
  if (!result) return Qnil;
164
164
 
165
165
  unsigned buf_idx = flags >> IORING_CQE_BUFFER_SHIFT;