nio4r 2.0.0.pre-java → 2.1.0-java
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/.rspec +0 -1
- data/.rubocop.yml +31 -38
- data/.ruby-version +1 -0
- data/.travis.yml +9 -19
- data/CHANGES.md +94 -42
- data/Gemfile +11 -3
- data/Guardfile +10 -0
- data/LICENSE.txt +1 -1
- data/README.md +43 -136
- data/Rakefile +2 -0
- data/examples/echo_server.rb +1 -0
- data/ext/libev/Changes +9 -13
- data/ext/libev/ev.c +100 -74
- data/ext/libev/ev.h +4 -9
- data/ext/libev/ev_epoll.c +6 -3
- data/ext/libev/ev_kqueue.c +8 -4
- data/ext/libev/ev_poll.c +6 -3
- data/ext/libev/ev_port.c +8 -4
- data/ext/libev/ev_select.c +4 -2
- data/ext/nio4r/bytebuffer.c +265 -257
- data/ext/nio4r/extconf.rb +3 -9
- data/ext/nio4r/monitor.c +93 -46
- data/ext/nio4r/nio4r.h +6 -16
- data/ext/nio4r/org/nio4r/ByteBuffer.java +193 -209
- data/ext/nio4r/org/nio4r/Monitor.java +164 -0
- data/ext/nio4r/org/nio4r/Nio4r.java +13 -391
- data/ext/nio4r/org/nio4r/Selector.java +278 -0
- data/ext/nio4r/selector.c +72 -64
- data/lib/nio.rb +3 -3
- data/lib/nio/bytebuffer.rb +179 -132
- data/lib/nio/monitor.rb +64 -4
- data/lib/nio/selector.rb +36 -13
- data/lib/nio/version.rb +1 -1
- data/nio4r.gemspec +25 -19
- data/spec/nio/acceptables_spec.rb +6 -4
- data/spec/nio/bytebuffer_spec.rb +323 -51
- data/spec/nio/monitor_spec.rb +122 -79
- data/spec/nio/selectables/pipe_spec.rb +5 -1
- data/spec/nio/selectables/ssl_socket_spec.rb +15 -12
- data/spec/nio/selectables/tcp_socket_spec.rb +42 -31
- data/spec/nio/selectables/udp_socket_spec.rb +2 -0
- data/spec/nio/selector_spec.rb +10 -4
- data/spec/spec_helper.rb +24 -3
- data/spec/support/selectable_examples.rb +7 -5
- data/tasks/extension.rake +2 -0
- data/tasks/rspec.rake +2 -0
- data/tasks/rubocop.rake +2 -0
- metadata +18 -15
- data/.rubocop_todo.yml +0 -35
data/lib/nio/selector.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "set"
|
2
4
|
|
3
5
|
module NIO
|
@@ -13,17 +15,33 @@ module NIO
|
|
13
15
|
@closed = false
|
14
16
|
end
|
15
17
|
|
18
|
+
# Return a symbol representing the backend I/O multiplexing mechanism used.
|
19
|
+
# Supported backends are:
|
20
|
+
# * :ruby - pure Ruby (i.e IO.select)
|
21
|
+
# * :java - Java NIO on JRuby
|
22
|
+
# * :epoll - libev w\ Linux epoll
|
23
|
+
# * :poll - libev w\ POSIX poll
|
24
|
+
# * :kqueue - libev w\ BSD kqueue
|
25
|
+
# * :select - libev w\ SysV select
|
26
|
+
# * :port - libev w\ I/O completion ports
|
27
|
+
# * :unknown - libev w\ unknown backend
|
28
|
+
def backend
|
29
|
+
:ruby
|
30
|
+
end
|
31
|
+
|
16
32
|
# Register interest in an IO object with the selector for the given types
|
17
33
|
# of events. Valid event types for interest are:
|
18
34
|
# * :r - is the IO readable?
|
19
35
|
# * :w - is the IO writeable?
|
20
36
|
# * :rw - is the IO either readable or writeable?
|
21
37
|
def register(io, interest)
|
38
|
+
io = IO.try_convert(io)
|
39
|
+
|
22
40
|
@lock.synchronize do
|
23
|
-
|
41
|
+
raise IOError, "selector is closed" if closed?
|
24
42
|
|
25
43
|
monitor = @selectables[io]
|
26
|
-
|
44
|
+
raise ArgumentError, "already registered as #{monitor.interests.inspect}" if monitor
|
27
45
|
|
28
46
|
monitor = Monitor.new(io, interest, self)
|
29
47
|
@selectables[monitor.io] = monitor
|
@@ -35,7 +53,7 @@ module NIO
|
|
35
53
|
# Deregister the given IO object from the selector
|
36
54
|
def deregister(io)
|
37
55
|
@lock.synchronize do
|
38
|
-
monitor = @selectables.delete io
|
56
|
+
monitor = @selectables.delete IO.try_convert(io)
|
39
57
|
monitor.close(false) if monitor && !monitor.closed?
|
40
58
|
monitor
|
41
59
|
end
|
@@ -60,15 +78,14 @@ module NIO
|
|
60
78
|
monitor.readiness = nil
|
61
79
|
end
|
62
80
|
|
63
|
-
ready_readers, ready_writers = Kernel.select
|
64
|
-
return unless ready_readers # timeout
|
81
|
+
ready_readers, ready_writers = Kernel.select(readers, writers, [], timeout)
|
82
|
+
return unless ready_readers # timeout
|
65
83
|
|
66
84
|
ready_readers.each do |io|
|
67
85
|
if io == @wakeup
|
68
86
|
# Clear all wakeup signals we've received by reading them
|
69
87
|
# Wakeups should have level triggered behavior
|
70
88
|
@wakeup.read(@wakeup.stat.size)
|
71
|
-
return
|
72
89
|
else
|
73
90
|
monitor = @selectables[io]
|
74
91
|
monitor.readiness = :r
|
@@ -78,18 +95,16 @@ module NIO
|
|
78
95
|
|
79
96
|
ready_writers.each do |io|
|
80
97
|
monitor = @selectables[io]
|
81
|
-
monitor.readiness =
|
98
|
+
monitor.readiness = monitor.readiness == :r ? :rw : :w
|
82
99
|
selected_monitors << monitor
|
83
100
|
end
|
84
101
|
end
|
85
102
|
|
86
103
|
if block_given?
|
87
|
-
selected_monitors.each
|
88
|
-
yield m
|
89
|
-
end
|
104
|
+
selected_monitors.each { |m| yield m }
|
90
105
|
selected_monitors.size
|
91
106
|
else
|
92
|
-
selected_monitors
|
107
|
+
selected_monitors.to_a
|
93
108
|
end
|
94
109
|
end
|
95
110
|
|
@@ -119,8 +134,16 @@ module NIO
|
|
119
134
|
@lock.synchronize do
|
120
135
|
return if @closed
|
121
136
|
|
122
|
-
|
123
|
-
|
137
|
+
begin
|
138
|
+
@wakeup.close
|
139
|
+
rescue IOError
|
140
|
+
end
|
141
|
+
|
142
|
+
begin
|
143
|
+
@waker.close
|
144
|
+
rescue IOError
|
145
|
+
end
|
146
|
+
|
124
147
|
@closed = true
|
125
148
|
end
|
126
149
|
end
|
data/lib/nio/version.rb
CHANGED
data/nio4r.gemspec
CHANGED
@@ -1,28 +1,34 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require File.expand_path("../lib/nio/version", __FILE__)
|
3
4
|
|
4
|
-
Gem::Specification.new do |
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.authors = ["Tony Arcieri"]
|
7
|
+
spec.email = ["bascule@gmail.com"]
|
8
|
+
spec.homepage = "https://github.com/socketry/nio4r"
|
9
|
+
spec.license = "MIT"
|
10
|
+
spec.summary = "New IO for Ruby"
|
11
|
+
spec.description = <<-DESCRIPTION.strip.gsub(/\s+/, " ")
|
12
|
+
Cross-platform asynchronous I/O primitives for scalable network clients
|
13
|
+
and servers. Inspired by the Java NIO API, but simplified for ease-of-use.
|
14
|
+
DESCRIPTION
|
15
|
+
|
16
|
+
spec.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
17
|
+
spec.files = `git ls-files`.split("\n")
|
18
|
+
spec.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
spec.name = "nio4r"
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
spec.version = NIO::VERSION
|
11
22
|
|
12
|
-
|
13
|
-
gem.files = `git ls-files`.split("\n")
|
14
|
-
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
15
|
-
gem.name = "nio4r"
|
16
|
-
gem.require_paths = ["lib"]
|
17
|
-
gem.version = NIO::VERSION
|
23
|
+
spec.required_ruby_version = ">= 2.2.2"
|
18
24
|
|
19
25
|
if defined? JRUBY_VERSION
|
20
|
-
|
21
|
-
|
26
|
+
spec.files << "lib/nio4r_ext.jar"
|
27
|
+
spec.platform = "java"
|
22
28
|
else
|
23
|
-
|
29
|
+
spec.extensions = ["ext/nio4r/extconf.rb"]
|
24
30
|
end
|
25
31
|
|
26
|
-
|
27
|
-
|
32
|
+
spec.add_development_dependency "rake"
|
33
|
+
spec.add_development_dependency "bundler"
|
28
34
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "spec_helper"
|
2
4
|
|
3
5
|
RSpec.describe "NIO acceptables" do
|
@@ -15,16 +17,16 @@ RSpec.describe "NIO acceptables" do
|
|
15
17
|
end
|
16
18
|
|
17
19
|
describe TCPServer do
|
18
|
-
let(:
|
20
|
+
let(:port) { next_available_tcp_port }
|
19
21
|
|
20
22
|
let :acceptable_subject do
|
21
|
-
server = TCPServer.new("localhost",
|
22
|
-
TCPSocket.open("localhost",
|
23
|
+
server = TCPServer.new("localhost", port)
|
24
|
+
TCPSocket.open("localhost", port)
|
23
25
|
server
|
24
26
|
end
|
25
27
|
|
26
28
|
let :unacceptable_subject do
|
27
|
-
TCPServer.new("localhost",
|
29
|
+
TCPServer.new("localhost", port + 1)
|
28
30
|
end
|
29
31
|
|
30
32
|
it_behaves_like "an NIO acceptable"
|
data/spec/nio/bytebuffer_spec.rb
CHANGED
@@ -1,76 +1,348 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "spec_helper"
|
2
4
|
|
3
5
|
RSpec.describe NIO::ByteBuffer do
|
4
|
-
|
5
|
-
|
6
|
+
let(:capacity) { 256 }
|
7
|
+
let(:example_string) { "Testing 1 2 3..." }
|
8
|
+
subject(:bytebuffer) { described_class.new(capacity) }
|
6
9
|
|
7
|
-
|
8
|
-
|
10
|
+
describe "#initialize" do
|
11
|
+
it "raises TypeError if given a bogus argument" do
|
12
|
+
expect { described_class.new(:symbols_are_bogus) }.to raise_error(TypeError)
|
13
|
+
end
|
14
|
+
end
|
9
15
|
|
10
|
-
|
11
|
-
|
12
|
-
|
16
|
+
describe "#clear" do
|
17
|
+
it "clears the buffer" do
|
18
|
+
bytebuffer << example_string
|
19
|
+
bytebuffer.clear
|
13
20
|
|
14
|
-
|
15
|
-
|
16
|
-
|
21
|
+
expect(bytebuffer.remaining).to eq capacity
|
22
|
+
end
|
23
|
+
end
|
17
24
|
|
18
|
-
|
19
|
-
|
20
|
-
|
25
|
+
describe "#position" do
|
26
|
+
it "defaults to zero" do
|
27
|
+
expect(bytebuffer.position).to be_zero
|
28
|
+
end
|
29
|
+
end
|
21
30
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
31
|
+
describe "#position=" do
|
32
|
+
let(:example_position) { 42 }
|
33
|
+
|
34
|
+
it "sets the buffer's position to a valid value" do
|
35
|
+
expect(bytebuffer.position).to be_zero
|
36
|
+
bytebuffer.position = example_position
|
37
|
+
expect(bytebuffer.position).to eq example_position
|
38
|
+
end
|
39
|
+
|
40
|
+
it "raises ArgumentError if the specified position is less than zero" do
|
41
|
+
expect { bytebuffer.position = -1 }.to raise_error(ArgumentError)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "raises ArgumentError if the specified position exceeds the limit" do
|
45
|
+
expect { bytebuffer.position = capacity + 1 }.to raise_error(ArgumentError)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "#limit" do
|
50
|
+
it "defaults to the buffer's capacity" do
|
51
|
+
expect(bytebuffer.limit).to eq capacity
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "#limit=" do
|
56
|
+
it "sets the buffer's limit to a valid value" do
|
57
|
+
bytebuffer.flip
|
58
|
+
expect(bytebuffer.limit).to be_zero
|
59
|
+
|
60
|
+
new_limit = capacity / 2
|
61
|
+
bytebuffer.limit = new_limit
|
62
|
+
expect(bytebuffer.limit).to eq new_limit
|
63
|
+
end
|
64
|
+
|
65
|
+
it "preserves position and mark if they're less than the new limit" do
|
66
|
+
bytebuffer << "four"
|
67
|
+
bytebuffer.mark
|
68
|
+
bytebuffer << "more"
|
69
|
+
|
70
|
+
bytebuffer.limit = capacity / 2
|
71
|
+
expect(bytebuffer.position).to eq 8
|
72
|
+
bytebuffer.reset
|
73
|
+
expect(bytebuffer.position).to eq 4
|
74
|
+
end
|
75
|
+
|
76
|
+
it "sets position to the new limit if the previous position is beyond the limit" do
|
77
|
+
bytebuffer << "four"
|
78
|
+
bytebuffer.limit = 2
|
79
|
+
expect(bytebuffer.position).to eq 2
|
80
|
+
end
|
81
|
+
|
82
|
+
it "clears the mark if the new limit is before the current mark" do
|
83
|
+
bytebuffer << "four"
|
84
|
+
bytebuffer.mark
|
85
|
+
bytebuffer.limit = 2
|
86
|
+
expect { bytebuffer.reset }.to raise_error(NIO::ByteBuffer::MarkUnsetError)
|
87
|
+
end
|
26
88
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
89
|
+
it "raises ArgumentError if specified limit is less than zero" do
|
90
|
+
expect { bytebuffer.limit = -1 }.to raise_error(ArgumentError)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "raises ArgumentError if specified limit exceeds capacity" do
|
94
|
+
expect { bytebuffer.limit = capacity }.not_to raise_error
|
95
|
+
expect { bytebuffer.limit = capacity + 1 }.to raise_error(ArgumentError)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "#capacity" do
|
100
|
+
it "has the requested capacity" do
|
101
|
+
expect(bytebuffer.capacity).to eq capacity
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "#remaining" do
|
106
|
+
it "calculates the number of bytes remaining" do
|
107
|
+
expect(bytebuffer.remaining).to eq capacity
|
108
|
+
bytebuffer << example_string
|
109
|
+
expect(bytebuffer.remaining).to eq(capacity - example_string.length)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "#full?" do
|
114
|
+
it "returns false when there is space remaining in the buffer" do
|
115
|
+
expect(bytebuffer).not_to be_full
|
116
|
+
end
|
117
|
+
|
118
|
+
it "returns true when the buffer is full" do
|
119
|
+
bytebuffer << "X" * capacity
|
120
|
+
expect(bytebuffer).to be_full
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe "#get" do
|
125
|
+
it "reads all remaining data if no length is given" do
|
126
|
+
bytebuffer << example_string
|
127
|
+
bytebuffer.flip
|
128
|
+
|
129
|
+
expect(bytebuffer.get).to eq example_string
|
130
|
+
end
|
131
|
+
|
132
|
+
it "reads zeroes from a newly initialized buffer" do
|
133
|
+
expect(bytebuffer.get(capacity)).to eq("\0" * capacity)
|
134
|
+
end
|
135
|
+
|
136
|
+
it "advances position as data is read" do
|
137
|
+
bytebuffer << "First"
|
138
|
+
bytebuffer << "Second"
|
139
|
+
bytebuffer << "Third"
|
140
|
+
bytebuffer.flip
|
141
|
+
|
142
|
+
expect(bytebuffer.position).to be_zero
|
143
|
+
expect(bytebuffer.get(10)).to eq "FirstSecon"
|
144
|
+
expect(bytebuffer.position).to eq 10
|
145
|
+
end
|
146
|
+
|
147
|
+
it "raises NIO::ByteBuffer::UnderflowError if there is not enough data in the buffer" do
|
148
|
+
bytebuffer << example_string
|
149
|
+
bytebuffer.flip
|
150
|
+
|
151
|
+
expect { bytebuffer.get(example_string.length + 1) }.to raise_error(NIO::ByteBuffer::UnderflowError)
|
152
|
+
expect(bytebuffer.get(example_string.length)).to eq example_string
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
describe "#[]" do
|
157
|
+
it "obtains bytes at a given index without altering position" do
|
158
|
+
bytebuffer << example_string
|
159
|
+
expect(bytebuffer[7]).to eq example_string.bytes[7]
|
160
|
+
expect(bytebuffer.position).to eq example_string.length
|
161
|
+
end
|
162
|
+
|
163
|
+
it "raises ArgumentError if the index is less than zero" do
|
164
|
+
expect { bytebuffer[-1] }.to raise_error(ArgumentError)
|
165
|
+
end
|
166
|
+
|
167
|
+
it "raises ArgumentError if the index exceeds the limit" do
|
168
|
+
bytebuffer << example_string
|
169
|
+
bytebuffer.flip
|
170
|
+
expect(bytebuffer[bytebuffer.limit - 1]).to eq example_string.bytes.last
|
171
|
+
expect { bytebuffer[bytebuffer.limit] }.to raise_error(ArgumentError)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
describe "#<<" do
|
176
|
+
it "adds strings to the buffer" do
|
177
|
+
bytebuffer << example_string
|
178
|
+
expect(bytebuffer.position).to eq example_string.length
|
179
|
+
expect(bytebuffer.limit).to eq capacity
|
180
|
+
end
|
181
|
+
|
182
|
+
it "raises NIO::ByteBuffer::OverflowError if the buffer is full" do
|
183
|
+
bytebuffer << "X" * (capacity - 1)
|
184
|
+
expect { bytebuffer << "X" }.not_to raise_error
|
185
|
+
expect { bytebuffer << "X" }.to raise_error(NIO::ByteBuffer::OverflowError)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
describe "#flip" do
|
190
|
+
it "flips the bytebuffer" do
|
191
|
+
bytebuffer << example_string
|
192
|
+
expect(bytebuffer.position).to eql example_string.length
|
193
|
+
|
194
|
+
expect(bytebuffer.flip).to eq bytebuffer
|
195
|
+
|
196
|
+
expect(bytebuffer.position).to be_zero
|
197
|
+
expect(bytebuffer.limit).to eq example_string.length
|
198
|
+
expect(bytebuffer.get).to eq example_string
|
199
|
+
end
|
200
|
+
|
201
|
+
it "sets remaining to the previous position" do
|
202
|
+
bytebuffer << example_string
|
203
|
+
previous_position = bytebuffer.position
|
204
|
+
expect(bytebuffer.remaining).to eq(capacity - previous_position)
|
205
|
+
expect(bytebuffer.flip.remaining).to eq previous_position
|
206
|
+
end
|
207
|
+
|
208
|
+
it "sets limit to the previous position" do
|
209
|
+
bytebuffer << example_string
|
210
|
+
expect(bytebuffer.limit).to eql(capacity)
|
211
|
+
|
212
|
+
previous_position = bytebuffer.position
|
213
|
+
expect(bytebuffer.flip.limit).to eql previous_position
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
describe "#rewind" do
|
218
|
+
it "rewinds the buffer leaving the limit intact" do
|
219
|
+
bytebuffer << example_string
|
220
|
+
expect(bytebuffer.rewind).to eq bytebuffer
|
221
|
+
|
222
|
+
expect(bytebuffer.position).to be_zero
|
223
|
+
expect(bytebuffer.limit).to eq capacity
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
describe "#mark" do
|
228
|
+
it "returns self" do
|
229
|
+
expect(bytebuffer.mark).to eql bytebuffer
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
describe "#reset" do
|
234
|
+
it "returns to a previously marked position" do
|
235
|
+
bytebuffer << "First"
|
236
|
+
expected_position = bytebuffer.position
|
237
|
+
|
238
|
+
expect(bytebuffer.mark).to eq bytebuffer
|
239
|
+
bytebuffer << "Second"
|
240
|
+
expect(bytebuffer.position).not_to eq expected_position
|
241
|
+
expect(bytebuffer.reset.position).to eq expected_position
|
242
|
+
end
|
243
|
+
|
244
|
+
it "raises NIO::ByteBuffer::MarkUnsetError unless mark has been set" do
|
245
|
+
expect { bytebuffer.reset }.to raise_error(NIO::ByteBuffer::MarkUnsetError)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
describe "#compact" do
|
250
|
+
let(:first_string) { "CompactMe" }
|
251
|
+
let(:second_string) { "Leftover" }
|
252
|
+
|
253
|
+
it "copies data from the current position to the beginning of the buffer" do
|
254
|
+
bytebuffer << first_string << second_string
|
255
|
+
bytebuffer.position = first_string.length
|
256
|
+
bytebuffer.limit = first_string.length + second_string.length
|
257
|
+
bytebuffer.compact
|
258
|
+
|
259
|
+
expect(bytebuffer.position).to eq second_string.length
|
260
|
+
expect(bytebuffer.limit).to eq capacity
|
261
|
+
expect(bytebuffer.flip.get).to eq second_string
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
describe "#each" do
|
266
|
+
it "iterates over data in the buffer" do
|
267
|
+
bytebuffer << example_string
|
268
|
+
bytebuffer.flip
|
269
|
+
|
270
|
+
bytes = []
|
271
|
+
bytebuffer.each { |byte| bytes << byte }
|
272
|
+
expect(bytes).to eq example_string.bytes
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
describe "#inspect" do
|
277
|
+
it "inspects the buffer offsets" do
|
278
|
+
regex = /\A#<NIO::ByteBuffer:.*? @position=0 @limit=#{capacity} @capacity=#{capacity}>\z/
|
279
|
+
expect(bytebuffer.inspect).to match(regex)
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
context "I/O" do
|
284
|
+
let(:addr) { "localhost" }
|
285
|
+
let(:port) { next_available_tcp_port }
|
286
|
+
let(:server) { TCPServer.new(addr, port) }
|
287
|
+
let(:client) { TCPSocket.new(addr, port) }
|
288
|
+
let(:peer) { server_thread.value }
|
289
|
+
|
290
|
+
let(:server_thread) do
|
291
|
+
server
|
292
|
+
|
293
|
+
thread = Thread.new { server.accept }
|
294
|
+
Thread.pass while thread.status && thread.status != "sleep"
|
295
|
+
|
296
|
+
thread
|
297
|
+
end
|
298
|
+
|
299
|
+
before do
|
300
|
+
server_thread
|
301
|
+
client
|
302
|
+
end
|
303
|
+
|
304
|
+
after do
|
305
|
+
server_thread.kill if server_thread.alive?
|
306
|
+
|
307
|
+
server.close rescue nil
|
308
|
+
client.close rescue nil
|
309
|
+
peer.close rescue nil
|
310
|
+
end
|
311
|
+
|
312
|
+
describe "#read_from" do
|
313
|
+
it "reads data into the buffer" do
|
314
|
+
client.write(example_string)
|
315
|
+
expect(bytebuffer.read_from(peer)).to eq example_string.length
|
31
316
|
bytebuffer.flip
|
32
|
-
expect(bytebuffer.read_next(5)).to eql "TestT"
|
33
|
-
end
|
34
317
|
|
35
|
-
|
318
|
+
expect(bytebuffer.get).to eq example_string
|
36
319
|
end
|
37
320
|
|
38
|
-
it "
|
39
|
-
|
40
|
-
bytebuffer << "
|
41
|
-
bytebuffer
|
42
|
-
bytebuffer.flip
|
43
|
-
bytebuffer.read_next 5
|
44
|
-
bytebuffer.compact
|
45
|
-
bytebuffer << " RRMARTIN"
|
46
|
-
bytebuffer.flip
|
47
|
-
# expect(bytebuffer.limit?).to eql(10)
|
48
|
-
expect(bytebuffer.get).to eql("TextDumb RRMARTIN")
|
321
|
+
it "raises NIO::ByteBuffer::OverflowError if the buffer is already full" do
|
322
|
+
client.write(example_string)
|
323
|
+
bytebuffer << "X" * capacity
|
324
|
+
expect { bytebuffer.read_from(peer) }.to raise_error(NIO::ByteBuffer::OverflowError)
|
49
325
|
end
|
50
326
|
|
51
|
-
it "
|
52
|
-
bytebuffer
|
53
|
-
bytebuffer.flip
|
54
|
-
expect(bytebuffer.get).to eql("Test")
|
327
|
+
it "returns 0 if no data is available" do
|
328
|
+
expect(bytebuffer.read_from(peer)).to eq 0
|
55
329
|
end
|
330
|
+
end
|
56
331
|
|
57
|
-
|
58
|
-
|
332
|
+
describe "#write_to" do
|
333
|
+
it "writes data from the buffer" do
|
334
|
+
bytebuffer << example_string
|
59
335
|
bytebuffer.flip
|
60
|
-
bytebuffer.read_next 5
|
61
|
-
expect(bytebuffer.read_next(4)).to eql("Snow")
|
62
|
-
end
|
63
336
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
expect(
|
337
|
+
expect(bytebuffer.write_to(client)).to eq example_string.length
|
338
|
+
client.close
|
339
|
+
|
340
|
+
expect(peer.read(example_string.length)).to eq example_string
|
68
341
|
end
|
69
342
|
|
70
|
-
it "
|
71
|
-
bytebuffer << "Test"
|
343
|
+
it "raises NIO::ByteBuffer::UnderflowError if the buffer is out of data" do
|
72
344
|
bytebuffer.flip
|
73
|
-
expect
|
345
|
+
expect { bytebuffer.write_to(peer) }.to raise_error(NIO::ByteBuffer::UnderflowError)
|
74
346
|
end
|
75
347
|
end
|
76
348
|
end
|