uringmachine 0.11.1 → 0.12

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ccb8488c3f313b7a3890ae110fc5cebc7ac0062bbaa9ef362fb7ff5b02c680b9
4
- data.tar.gz: 41f2c23a9427609c4d01a6eef319cbd40d029cf85918fd6c8d00e5b502f83ef6
3
+ metadata.gz: 1f8224da7817436bd74a52e650362c9b77d5dbc8877c0fa1cefd1c52e57ceb03
4
+ data.tar.gz: c7bb88c42bf59a9d1f105e2356c4f71f0fec49c7cbbe662744465ee3b34d5141
5
5
  SHA512:
6
- metadata.gz: 9bd08ebae21a7a10407ff04102d9cbf491bfab65ebe601b6ab3a6b7790632c946873415af4f45321e959bd39e875ff386e8c85f74329743204a9c0c047ce5da0
7
- data.tar.gz: 3cd63222c0b990ef83f2b324aa46b36bd142a7b43a1a273428a9b70a95cfe25e0a1b06a2985a7463fe51a995c78e82838e34ee20d3c332cc46278ffd738652dd
6
+ metadata.gz: 39d930e9f4d8a17676c28e4bf24f38b585dbd0cd124316272778abb285391be4bb9ecf14452d19568ffa8f6a0144c6a98b2e3ee1f50d58ec7c5101333f5de5e7
7
+ data.tar.gz: 95468f71d402a2a8abf675dbbae6e94b4e1919c5552f0b831b7387016d3fb24271756585cf66283130ed3440818ccc94f504d73a46d2ab8f9faa4607b0cc822b
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ # 2025-06-03 Version 0.12
2
+
3
+ - Add buffer, maxlen params to `Stream#get_line`
4
+ - Add buffer param to `Stream#get_string`
5
+ - Remove `Stream#resp_get_line`, `Stream#resp_get_string` methods
6
+
1
7
  # 2025-06-02 Version 0.11.1
2
8
 
3
9
  - Fix `UM::Stream` behaviour on GC
data/TODO.md CHANGED
@@ -22,3 +22,27 @@
22
22
  When doing a `call`, we need to provide a mailbox for the response. can this be
23
23
  automatic?
24
24
 
25
+ # streams
26
+
27
+ We're still missing:
28
+
29
+ - limit on line length in `get_line`
30
+ - ability to supply buffer to `get_line` and `get_string`
31
+ - allow read to eof, maybe with `read_to_eof`
32
+
33
+ For the sake of performance, simplicity and explicitness, we change the API as follows:
34
+
35
+ ```ruby
36
+ stream.get_line(buf, limit)
37
+ # the defaults:
38
+ stream.get_line(nil, -1)
39
+
40
+ stream.get_string(len, buf)
41
+ # defaults:
42
+ stream.get_string(len, nil)
43
+
44
+ # and
45
+ stream.read_to_eof(buf)
46
+ # defaults:
47
+ stream.read_to_eof(nil)
48
+ ```
@@ -132,11 +132,12 @@ end
132
132
  def stream_parse_headers(fd)
133
133
  stream = UM::Stream.new($machine, fd)
134
134
 
135
- headers = stream_get_request_line(stream)
135
+ buf = String.new(capacity: 65536)
136
+ headers = stream_get_request_line(stream, buf)
136
137
  return nil if !headers
137
138
 
138
139
  while true
139
- line = stream.get_line()
140
+ line = stream.get_line(buf, 0)
140
141
  break if line.empty?
141
142
 
142
143
  m = line.match(RE_HEADER_LINE)
@@ -148,8 +149,8 @@ def stream_parse_headers(fd)
148
149
  headers
149
150
  end
150
151
 
151
- def stream_get_request_line(stream)
152
- line = stream.get_line()
152
+ def stream_get_request_line(stream, buf)
153
+ line = stream.get_line(buf, 0)
153
154
 
154
155
  m = line.match(RE_REQUEST_LINE)
155
156
  return nil if !m
@@ -181,42 +182,43 @@ ensure
181
182
  ($machine.close(wfd) rescue nil) if wfd
182
183
  end
183
184
 
184
- # 10000.times { parse_http_parser }
185
- # 10000.times { parse_http_stringio }
186
- # 10000.times { parse_http_stream }
187
- # exit
188
-
189
- # GC.disable
190
-
191
- # OS = ObjectSpace
192
-
193
- # def object_count
194
- # counts = ObjectSpace.count_objects
195
- # counts[:TOTAL] - counts[:FREE]
196
- # end
185
+ def compare_allocs
186
+ GC.disable
187
+ x = 1000
188
+ p(
189
+ alloc_http_parser: alloc_count { x.times { parse_http_parser } },
190
+ alloc_stringio: alloc_count { x.times { parse_http_stringio } },
191
+ alloc_stream: alloc_count { x.times { parse_http_stream } }
192
+ )
193
+ ensure
194
+ GC.enable
195
+ end
197
196
 
198
- # def alloc_count
199
- # GC.start
200
- # count0 = object_count
201
- # yield
202
- # count1 = object_count
203
- # count1 - count0
204
- # end
197
+ def object_count
198
+ counts = ObjectSpace.count_objects
199
+ counts[:TOTAL] - counts[:FREE]
200
+ end
205
201
 
206
- # X = 100
207
- # p(
208
- # alloc_http_parser: alloc_count { X.times { parse_http_parser } },
209
- # alloc_stringio: alloc_count { X.times { parse_http_stringio } },
210
- # alloc_stream: alloc_count { X.times { parse_http_stream } }
211
- # )
212
- # exit
202
+ def alloc_count
203
+ GC.start
204
+ count0 = object_count
205
+ yield
206
+ # GC.start
207
+ count1 = object_count
208
+ count1 - count0
209
+ end
213
210
 
214
- Benchmark.ips do |x|
215
- x.config(:time => 5, :warmup => 3)
211
+ def benchmark
212
+ Benchmark.ips do |x|
213
+ x.config(:time => 5, :warmup => 3)
216
214
 
217
- x.report("http_parser") { parse_http_parser }
218
- x.report("stringio") { parse_http_stringio }
219
- x.report("stream") { parse_http_stream }
215
+ x.report("http_parser") { parse_http_parser }
216
+ x.report("stringio") { parse_http_stringio }
217
+ x.report("stream") { parse_http_stream }
220
218
 
221
- x.compare!
219
+ x.compare!
220
+ end
222
221
  end
222
+
223
+ compare_allocs
224
+ benchmark
data/ext/um/um.h CHANGED
@@ -270,11 +270,8 @@ VALUE um_queue_pop(struct um *machine, struct um_queue *queue);
270
270
  VALUE um_queue_unshift(struct um *machine, struct um_queue *queue, VALUE value);
271
271
  VALUE um_queue_shift(struct um *machine, struct um_queue *queue);
272
272
 
273
- int stream_read_more(struct um_stream *stream);
274
- VALUE stream_get_line(struct um_stream *stream);
275
- VALUE stream_get_string(struct um_stream *stream, ulong len);
276
- VALUE resp_get_line(struct um_stream *stream, VALUE out_buffer);
277
- VALUE resp_get_string(struct um_stream *stream, ulong len, VALUE out_buffer);
273
+ VALUE stream_get_line(struct um_stream *stream, VALUE buf, ssize_t maxlen);
274
+ VALUE stream_get_string(struct um_stream *stream, VALUE buf, ssize_t len);
278
275
  VALUE resp_decode(struct um_stream *stream, VALUE out_buffer);
279
276
  void resp_encode(struct um_write_buffer *buf, VALUE obj);
280
277
 
data/ext/um/um_stream.c CHANGED
@@ -1,31 +1,5 @@
1
1
  #include "um.h"
2
-
3
- VALUE stream_get_line(struct um_stream *stream) {
4
- char *start = RSTRING_PTR(stream->buffer) + stream->pos;
5
- while (true) {
6
- char * lf_ptr = memchr(start, '\n', stream->len - stream->pos);
7
- if (lf_ptr) {
8
- ulong len = lf_ptr - start;
9
- if (len && (start[len - 1] == '\r')) len -= 1;
10
-
11
- VALUE str = rb_str_new(start, len);
12
- stream->pos += lf_ptr - start + 1;
13
- return str;
14
- }
15
-
16
- if (!stream_read_more(stream)) return Qnil;
17
- }
18
- }
19
-
20
- VALUE stream_get_string(struct um_stream *stream, ulong len) {
21
- while (stream->len - stream->pos < len)
22
- if (!stream_read_more(stream)) return Qnil;
23
-
24
- char *start = RSTRING_PTR(stream->buffer) + stream->pos;
25
- VALUE str = rb_utf8_str_new(start, len);
26
- stream->pos += len;
27
- return str;
28
- }
2
+ #include <stdlib.h>
29
3
 
30
4
  static inline void stream_check_truncate_buffer(struct um_stream *stream) {
31
5
  if ((stream->pos == stream->len) && (stream->len >= 1 << 12)) {
@@ -67,10 +41,67 @@ int stream_read_more(struct um_stream *stream) {
67
41
  return 1;
68
42
  }
69
43
 
70
- // ensure string can hold at least len bytes
44
+ // ensures given string can hold at least given len bytes (+trailing null)
71
45
  static inline void str_expand(VALUE str, size_t len) {
72
- size_t capa = rb_str_capacity(str);
73
- if (capa < len + 1) rb_str_modify_expand(str, len + 1 - capa);
46
+ rb_str_resize(str, len);
47
+ }
48
+
49
+ static inline void str_copy_bytes(VALUE dest, const char *src, ssize_t len) {
50
+ str_expand(dest, len + 1);
51
+ char *dest_ptr = RSTRING_PTR(dest);
52
+ memcpy(dest_ptr, src, len);
53
+ dest_ptr[len] = 0;
54
+ rb_str_set_len(dest, len);
55
+ }
56
+
57
+ VALUE stream_get_line(struct um_stream *stream, VALUE buf, ssize_t maxlen) {
58
+ char *start = RSTRING_PTR(stream->buffer) + stream->pos;
59
+ while (true) {
60
+ ssize_t pending_len = stream->len - stream->pos;
61
+ ssize_t search_len = pending_len;
62
+ ssize_t absmax_len = labs(maxlen);
63
+ int should_limit_len = (absmax_len > 0) && (search_len > maxlen);
64
+ if (should_limit_len) search_len = absmax_len;
65
+
66
+ char * lf_ptr = memchr(start, '\n', search_len);
67
+ if (lf_ptr) {
68
+ ssize_t len = lf_ptr - start;
69
+ if (len && (start[len - 1] == '\r')) len -= 1;
70
+
71
+ stream->pos += lf_ptr - start + 1;
72
+ if (NIL_P(buf)) return rb_utf8_str_new(start, len);
73
+
74
+ str_copy_bytes(buf, start, len);
75
+ return buf;
76
+ }
77
+ else if (should_limit_len && pending_len > search_len)
78
+ // maxlen
79
+ return Qnil;
80
+
81
+ if (!stream_read_more(stream))
82
+ return Qnil;
83
+ else
84
+ // update start ptr (it might have changed after reading)
85
+ start = RSTRING_PTR(stream->buffer) + stream->pos;
86
+ }
87
+ }
88
+
89
+ VALUE stream_get_string(struct um_stream *stream, VALUE buf, ssize_t len) {
90
+ size_t abslen = labs(len);
91
+ while (stream->len - stream->pos < abslen)
92
+ if (!stream_read_more(stream)) {
93
+ if (len > 0) return Qnil;
94
+
95
+ abslen = stream->len - stream->pos;
96
+ }
97
+
98
+ char *start = RSTRING_PTR(stream->buffer) + stream->pos;
99
+ stream->pos += abslen;
100
+
101
+ if (NIL_P(buf)) return rb_utf8_str_new(start, abslen);
102
+
103
+ str_copy_bytes(buf, start, len);
104
+ return buf;
74
105
  }
75
106
 
76
107
  VALUE resp_get_line(struct um_stream *stream, VALUE out_buffer) {
@@ -89,11 +120,7 @@ VALUE resp_get_line(struct um_stream *stream, VALUE out_buffer) {
89
120
  return str;
90
121
  }
91
122
 
92
- str_expand(out_buffer, len + 1);
93
- char *dest_ptr = RSTRING_PTR(out_buffer);
94
- memcpy(dest_ptr, start, len);
95
- dest_ptr[len] = 0; // add null at end
96
- rb_str_set_len(out_buffer, len);
123
+ str_copy_bytes(out_buffer, start, len);
97
124
  return out_buffer;
98
125
  }
99
126
 
@@ -116,11 +143,7 @@ VALUE resp_get_string(struct um_stream *stream, ulong len, VALUE out_buffer) {
116
143
 
117
144
  if (NIL_P(out_buffer)) return rb_utf8_str_new(start, len);
118
145
 
119
- str_expand(out_buffer, len + 1);
120
- char *dest_ptr = RSTRING_PTR(out_buffer);
121
- memcpy(dest_ptr, start, len);
122
- dest_ptr[len] = 0; // add null at end
123
- rb_str_set_len(out_buffer, len);
146
+ str_copy_bytes(out_buffer, start, len);
124
147
  return out_buffer;
125
148
  }
126
149
 
@@ -52,36 +52,18 @@ VALUE Stream_initialize(VALUE self, VALUE machine, VALUE fd) {
52
52
  return self;
53
53
  }
54
54
 
55
- VALUE Stream_get_line(VALUE self) {
55
+ VALUE Stream_get_line(VALUE self, VALUE buf, VALUE limit) {
56
56
  struct um_stream *stream = RTYPEDDATA_DATA(self);
57
57
  if (unlikely(stream->eof)) return Qnil;
58
58
 
59
- return stream_get_line(stream);
59
+ return stream_get_line(stream, buf, NUM2LONG(limit));
60
60
  }
61
61
 
62
- VALUE Stream_get_string(VALUE self, VALUE len) {
62
+ VALUE Stream_get_string(VALUE self, VALUE buf, VALUE len) {
63
63
  struct um_stream *stream = RTYPEDDATA_DATA(self);
64
64
  if (unlikely(stream->eof)) return Qnil;
65
65
 
66
- return stream_get_string(stream, NUM2ULONG(len));
67
- }
68
-
69
- VALUE Stream_resp_get_line(VALUE self) {
70
- struct um_stream *stream = RTYPEDDATA_DATA(self);
71
- if (unlikely(stream->eof)) return Qnil;
72
-
73
- VALUE line = resp_get_line(stream, Qnil);
74
- RB_GC_GUARD(line);
75
- return line;
76
- }
77
-
78
- VALUE Stream_resp_get_string(VALUE self, VALUE len) {
79
- struct um_stream *stream = RTYPEDDATA_DATA(self);
80
- if (unlikely(stream->eof)) return Qnil;
81
-
82
- VALUE str = resp_get_string(stream, NUM2ULONG(len), Qnil);
83
- RB_GC_GUARD(str);
84
- return str;
66
+ return stream_get_string(stream, buf, NUM2LONG(len));
85
67
  }
86
68
 
87
69
  VALUE Stream_resp_decode(VALUE self) {
@@ -109,11 +91,8 @@ void Init_Stream(void) {
109
91
 
110
92
  rb_define_method(cStream, "initialize", Stream_initialize, 2);
111
93
 
112
- rb_define_method(cStream, "get_line", Stream_get_line, 0);
113
- rb_define_method(cStream, "get_string", Stream_get_string, 1);
114
-
115
- rb_define_method(cStream, "resp_get_line", Stream_resp_get_line, 0);
116
- rb_define_method(cStream, "resp_get_string", Stream_resp_get_string, 1);
94
+ rb_define_method(cStream, "get_line", Stream_get_line, 2);
95
+ rb_define_method(cStream, "get_string", Stream_get_string, 2);
117
96
 
118
97
  rb_define_method(cStream, "resp_decode", Stream_resp_decode, 0);
119
98
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class UringMachine
4
- VERSION = '0.11.1'
4
+ VERSION = '0.12'
5
5
  end
data/test/test_stream.rb CHANGED
@@ -15,41 +15,92 @@ class StreamTest < StreamBaseTest
15
15
  machine.write(@wfd, "foo\nbar\r\nbaz")
16
16
  machine.close(@wfd)
17
17
 
18
- assert_equal 'foo', @stream.get_line
19
- assert_equal 'bar', @stream.get_line
20
- assert_nil @stream.get_line
18
+ assert_equal 'foo', @stream.get_line(nil, 0)
19
+ assert_equal 'bar', @stream.get_line(nil, 0)
20
+ assert_nil @stream.get_line(nil, 0)
21
+ end
22
+
23
+ def test_get_line_with_buf
24
+ machine.write(@wfd, "foo\nbar\r\nbaz")
25
+ machine.close(@wfd)
26
+
27
+ buf = +''
28
+ ret = @stream.get_line(buf, 0)
29
+ assert_equal 'foo', buf
30
+ assert_equal ret, buf
31
+
32
+ ret = @stream.get_line(buf, 0)
33
+ assert_equal 'bar', buf
34
+ assert_equal ret, buf
35
+ end
36
+
37
+ def test_get_line_with_positive_maxlen
38
+ machine.write(@wfd, "foobar\r\n")
39
+ machine.close(@wfd)
40
+
41
+ buf = +''
42
+ ret = @stream.get_line(buf, 3)
43
+ assert_nil ret
44
+ assert_equal '', buf
45
+
46
+ # verify that stream pos has not changed
47
+ ret = @stream.get_line(buf, 0)
48
+ assert_equal 'foobar', buf
49
+ assert_equal ret, buf
50
+ end
51
+
52
+ def test_get_line_with_negative_maxlen
53
+ machine.write(@wfd, "foobar\r\n")
54
+ machine.close(@wfd)
55
+
56
+ buf = +''
57
+ ret = @stream.get_line(buf, -3)
58
+ assert_nil ret
59
+ assert_equal '', buf
60
+
61
+ # verify that stream pos has not changed
62
+ ret = @stream.get_line(buf, 0)
63
+ assert_equal 'foobar', buf
64
+ assert_equal ret, buf
21
65
  end
22
66
 
23
67
  def test_get_string
24
68
  machine.write(@wfd, "foobarbazblahzzz")
25
69
  machine.close(@wfd)
26
70
 
27
- assert_equal 'foobar', @stream.get_string(6)
28
- assert_equal 'baz', @stream.get_string(3)
29
- assert_equal 'blah', @stream.get_string(4)
30
- assert_nil @stream.get_string(4)
71
+ assert_equal 'foobar', @stream.get_string(nil, 6)
72
+ assert_equal 'baz', @stream.get_string(nil, 3)
73
+ assert_equal 'blah', @stream.get_string(nil, 4)
74
+ assert_nil @stream.get_string(nil, 4)
31
75
  end
32
- end
33
76
 
34
- class StreamRespTest < StreamBaseTest
35
- def test_trdp_get_line
36
- machine.write(@wfd, "foo\r\nbarbar\r\nbaz\n")
77
+ def test_get_string_with_buf
78
+ machine.write(@wfd, "foobarbazblahzzz")
37
79
  machine.close(@wfd)
38
-
39
- assert_equal 'foo', @stream.resp_get_line
40
- assert_equal 'barbar', @stream.resp_get_line
41
- assert_nil @stream.resp_get_line
80
+
81
+ buf = +''
82
+ ret = @stream.get_string(buf, 6)
83
+ assert_equal 'foobar', buf
84
+ assert_equal ret, buf
85
+
86
+ ret = @stream.get_string(buf, 3)
87
+ assert_equal 'baz', buf
88
+ assert_equal ret, buf
42
89
  end
43
90
 
44
- def test_resp_get_string
45
- machine.write(@wfd, "foo\r\nbarbar\r\nbaz\n")
91
+ def test_get_string_with_negative_len
92
+ machine.write(@wfd, "foobar")
46
93
  machine.close(@wfd)
47
94
 
48
- assert_equal 'foo', @stream.resp_get_string(3)
49
- assert_equal 'barbar', @stream.resp_get_string(6)
50
- assert_nil @stream.resp_get_string(3)
95
+ ret = @stream.get_string(nil, -12)
96
+ assert_equal 'foobar', ret
97
+
98
+ ret = @stream.get_string(nil, -4)
99
+ assert_nil ret
51
100
  end
101
+ end
52
102
 
103
+ class StreamRespTest < StreamBaseTest
53
104
  def test_resp_decode
54
105
  machine.write(@wfd, "+foo bar\r\n")
55
106
  assert_equal "foo bar", @stream.resp_decode
data/test/test_um.rb CHANGED
@@ -271,7 +271,7 @@ class PeriodicallyTest < UMBaseTest
271
271
  rescue Cancel
272
272
  cancel = 1
273
273
  end
274
- machine.snooze
274
+ 2.times { machine.snooze }
275
275
  assert_equal 0, machine.pending_count
276
276
  t1 = monotonic_clock
277
277
  assert_in_range 0.05..0.08, t1 - t0
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uringmachine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.1
4
+ version: '0.12'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner