async-io 0.3.0 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cc74a7c6901edc284210da8593a505e0eb87ff89
4
- data.tar.gz: d5c4e46d022296613e94dfbcc6409727dc33dcb2
3
+ metadata.gz: de9c8da76ca6bc5d7065b6729d187dcc235d2ea7
4
+ data.tar.gz: 299c561ae952ae21a611cfa083f98b3789f89d6b
5
5
  SHA512:
6
- metadata.gz: 79274376a216fce20ee3a1d8f56f2767f9a9c6cc107d20bc9780ceb4ce29c5e32f7e5a34a7ac84b6d57d55eb28cd00d4d6fd8ee32208a7e1a7015b146f5e10b4
7
- data.tar.gz: dc4e658c5411145c90359ef91dc565027b8c1ed36e22abd92e4907b20958f2e3b03e9cccb3a503c443c0cdfbceb58a4204226cd6d735681e3df6cec60c4317b6
6
+ metadata.gz: 396a2e21a43f0048282a144737226361fe9b0c6cd1376c7cc359c8522ab1e0a20fb721397ee6a18d81bc5eb9db87a0aa36c341786a58d69f521f3a82287eab53
7
+ data.tar.gz: d0627c41180d5d5faa92155a4feea2ee5f75285ad54105f2f9f17fe9a6a35789b9ecc9b3c0d2f2829f6987de4c7616ef8923eb41ffbee4b187eb8fd62a0a0529
data/.gitignore CHANGED
@@ -10,3 +10,4 @@
10
10
 
11
11
  # rspec failure tracking
12
12
  .rspec_status
13
+ .tags
@@ -0,0 +1,39 @@
1
+ # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ module Async
22
+ module IO
23
+ class BinaryString < String
24
+ def initialize(*args)
25
+ super
26
+
27
+ force_encoding(Encoding::BINARY)
28
+ end
29
+
30
+ def << string
31
+ super
32
+
33
+ force_encoding(Encoding::BINARY)
34
+ end
35
+
36
+ alias concat <<
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,74 @@
1
+ # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative '../stream'
22
+
23
+ module Async
24
+ module IO
25
+ module Protocol
26
+ class Line
27
+ def initialize(stream, eol = $\)
28
+ @stream = stream
29
+ @eol = eol
30
+ end
31
+
32
+ attr :stream
33
+ attr :eol
34
+
35
+ def write_lines(*args)
36
+ if args.empty?
37
+ @stream.write(@eol)
38
+ else
39
+ args.each do |arg|
40
+ @stream.write(arg)
41
+ @stream.write(@eol)
42
+ end
43
+ end
44
+ end
45
+
46
+ def read_line
47
+ @stream.read_until(@eol) or raise EOFError
48
+ end
49
+
50
+ def peek_line
51
+ @stream.peek do |read_buffer|
52
+ if index = read_buffer.index(@eol)
53
+ return read_buffer.slice(0, index)
54
+ end
55
+ end
56
+
57
+ raise EOFError
58
+ end
59
+
60
+ def each_line
61
+ return to_enum(:each_line) unless block_given?
62
+
63
+ while line = @stream.read_until(@eol)
64
+ yield line
65
+ end
66
+ end
67
+
68
+ def read_lines
69
+ @stream.read.split(@eol)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -18,37 +18,18 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
- require 'socket'
21
+ require_relative 'binary_string'
22
22
  require_relative 'generic'
23
23
 
24
24
  module Async
25
25
  module IO
26
- class BinaryString < String
27
- def initialize(*args)
28
- super
29
-
30
- force_encoding(Encoding::BINARY)
31
- end
32
-
33
- def << string
34
- super
35
-
36
- force_encoding(Encoding::BINARY)
37
- end
38
-
39
- alias concat <<
40
- end
41
-
42
26
  class Stream
43
- include Enumerable
44
-
45
- def initialize(io, block_size: 1024, eol: $/)
27
+ def initialize(io, block_size: 1024*4, sync: false)
46
28
  @io = io
47
29
  @eof = false
48
- @sync = false
49
30
 
50
31
  @block_size = block_size
51
- @eol = eol
32
+ @sync = sync
52
33
 
53
34
  @read_buffer = BinaryString.new
54
35
  @write_buffer = BinaryString.new
@@ -59,38 +40,15 @@ module Async
59
40
  # The "sync mode" of the stream. See IO#sync for full details.
60
41
  attr_accessor :sync
61
42
 
62
- # Reads `size` bytes from the stream. If `buffer` is provided it must
63
- # reference a string which will receive the data.
64
- #
65
- # See IO#read for full details.
66
- def read(size = nil, output_buffer = nil)
67
- if size == 0
68
- if output_buffer
69
- output_buffer.clear
70
- return output_buffer
71
- else
72
- return ""
73
- end
74
- end
75
-
76
- until @eof
77
- break if size && size <= @read_buffer.size
43
+ # Reads `size` bytes from the stream. If size is not specified, read until end of file.
44
+ def read(size = nil)
45
+ return "" if size == 0
46
+
47
+ until @eof || (size && size <= @read_buffer.size)
78
48
  fill_read_buffer
79
- break unless size
80
49
  end
81
50
 
82
- buffer = consume_read_buffer(size)
83
-
84
- if buffer && output_buffer
85
- output_buffer.replace(buffer)
86
- buffer = output_buffer
87
- end
88
-
89
- if size
90
- return buffer
91
- else
92
- return buffer || ""
93
- end
51
+ return consume_read_buffer(size)
94
52
  end
95
53
 
96
54
  # Writes `string` to the buffer. When the buffer is full or #sync is true the
@@ -107,9 +65,17 @@ module Async
107
65
  return string.bytesize
108
66
  end
109
67
 
68
+ # Writes `string` to the stream and returns self.
69
+ def <<(string)
70
+ write(string)
71
+
72
+ return self
73
+ end
74
+
110
75
  # Flushes buffered data to the stream.
111
76
  def flush
112
77
  syswrite(@write_buffer)
78
+ @write_buffer.clear
113
79
  end
114
80
 
115
81
  # Closes the stream and flushes any unwritten data.
@@ -119,160 +85,42 @@ module Async
119
85
  @io.close
120
86
  end
121
87
 
122
- # Reads the next line from the stream. Lines are separated by +eol+. If
123
- # +limit+ is provided the result will not be longer than the given number of
124
- # bytes.
125
- #
126
- # +eol+ may be a String or Regexp.
127
- #
128
- # Unlike IO#gets the line read will not be assigned to +$_+.
129
- #
130
- # Unlike IO#gets the separator must be provided if a limit is provided.
131
- def gets(eol = @eol, limit = nil)
132
- index = @read_buffer.index(eol)
133
-
134
- until index || @eof
135
- fill_read_buffer
136
- index = @read_buffer.index(eol)
137
- end
138
-
139
- if eol.is_a?(Regexp)
140
- size = index ? index+$&.bytesize : nil
141
- else
142
- size = index ? index+eol.bytesize : nil
143
- end
144
-
145
- if limit && limit >= 0
146
- size = [size, limit].min
147
- end
148
-
149
- consume_read_buffer(size)
150
- end
151
-
152
- # Executes the block for every line in the stream where lines are separated
153
- # by +eol+.
154
- #
155
- # See also #gets
156
- def each(eol = @eol)
157
- while line = self.gets(eol)
158
- yield line
159
- end
160
- end
161
- alias each_line each
162
-
163
- # Reads lines from the stream which are separated by +eol+.
164
- #
165
- # See also #gets
166
- def readlines(eol = @eol)
167
- lines = []
168
-
169
- while line = self.gets(eol)
170
- lines << line
171
- end
172
-
173
- lines
174
- end
175
-
176
- # Reads a line from the stream which is separated by +eol+.
177
- #
178
- # Raises EOFError if at end of file.
179
- def readline(eol=@eol)
180
- gets(eol) or raise EOFError
181
- end
182
-
183
- # Reads one character from the stream. Returns nil if called at end of
184
- # file.
185
- def getc
186
- read(1)
187
- end
188
-
189
- # Calls the given block once for each byte in the stream.
190
- def each_byte # :yields: byte
191
- while c = getc
192
- yield(c.ord)
193
- end
194
- end
195
-
196
- # Reads a one-character string from the stream. Raises an EOFError at end
197
- # of file.
198
- def readchar
199
- getc or raise EOFError
200
- end
201
-
202
88
  # Returns true if the stream is at file which means there is no more data to be read.
203
89
  def eof?
204
90
  fill_read_buffer if !@eof && @read_buffer.empty?
205
91
 
206
- @eof && @read_buffer.empty?
92
+ return @eof && @read_buffer.empty?
207
93
  end
94
+
208
95
  alias eof eof?
209
-
210
- # Writes `string` to the stream and returns self.
211
- def <<(string)
212
- write(string)
96
+
97
+ # Efficiently read data from the stream until encountering pattern.
98
+ # @param pattern [String] The pattern to match.
99
+ # @return [String] The contents of the stream up until the pattern, which is consumed but not returned.
100
+ def read_until(pattern)
101
+ index = @read_buffer.index(pattern)
213
102
 
214
- return self
215
- end
216
-
217
- # Writes `args` to the stream along with a record separator. See `IO#puts` for full details.
218
- def puts(*args, eol: @eol)
219
- if args.empty?
220
- write(eol)
221
- else
222
- args.each do |arg|
223
- string = arg.to_s
224
- if string.end_with? eol
225
- write(string)
226
- else
227
- write(string)
228
- write(eol)
229
- end
230
- end
103
+ until index || @eof
104
+ fill_read_buffer
105
+
106
+ index = @read_buffer.index(pattern)
231
107
  end
232
108
 
233
- return nil
234
- end
235
-
236
- # Writes `args` to the stream. See `IO#print` for full details.
237
- def print(*args)
238
- args.each do |arg|
239
- write(arg.to_s)
109
+ if line = consume_read_buffer(index)
110
+ consume_read_buffer(pattern.bytesize)
111
+
112
+ return line
240
113
  end
241
-
242
- return nil
243
- end
244
-
245
- # Formats and writes to the stream converting parameters under control of the format string. See `Kernel#sprintf` for format string details.
246
- def printf(s, *args)
247
- write(s % args)
248
-
249
- return nil
250
114
  end
251
-
252
- private
253
115
 
254
- # Write a buffer to the underlying stream.
255
- # @param buffer [String] The string to write, any encoding is okay.
256
- def syswrite(buffer)
257
- remaining = buffer.bytesize
258
-
259
- # Fast path:
260
- written = @io.write(buffer)
261
- return if written == remaining
262
-
263
- # Slow path:
264
- remaining -= written
265
-
266
- while remaining > 0
267
- wrote = @io.write(buffer.byteslice(written, remaining))
268
-
269
- remaining -= wrote
270
- written += wrote
116
+ def peek
117
+ until yield(@read_buffer) || @eof
118
+ fill_read_buffer
271
119
  end
272
-
273
- return written
274
120
  end
275
-
121
+
122
+ private
123
+
276
124
  # Fills the buffer from the underlying stream.
277
125
  def fill_read_buffer
278
126
  if buffer = @io.read(@block_size)
@@ -302,6 +150,28 @@ module Async
302
150
 
303
151
  return result
304
152
  end
153
+
154
+ # Write a buffer to the underlying stream.
155
+ # @param buffer [String] The string to write, any encoding is okay.
156
+ def syswrite(buffer)
157
+ remaining = buffer.bytesize
158
+
159
+ # Fast path:
160
+ written = @io.write(buffer)
161
+ return if written == remaining
162
+
163
+ # Slow path:
164
+ remaining -= written
165
+
166
+ while remaining > 0
167
+ wrote = @io.write(buffer.byteslice(written, remaining))
168
+
169
+ remaining -= wrote
170
+ written += wrote
171
+ end
172
+
173
+ return written
174
+ end
305
175
  end
306
176
  end
307
177
  end
@@ -20,6 +20,6 @@
20
20
 
21
21
  module Async
22
22
  module IO
23
- VERSION = "0.3.0"
23
+ VERSION = "0.4.0"
24
24
  end
25
25
  end
@@ -0,0 +1,66 @@
1
+ # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'async/io/protocol/line'
22
+
23
+ RSpec.describe Async::IO::Protocol::Line do
24
+ let(:io) {StringIO.new}
25
+ let(:stream) {Async::IO::Stream.new(io)}
26
+ let(:protocol) {described_class.new(stream, "\n")}
27
+
28
+ describe '#write_lines' do
29
+ it "should write line" do
30
+ protocol.write_lines "Hello World"
31
+ stream.flush
32
+
33
+ expect(io.string).to be == "Hello World\n"
34
+ end
35
+ end
36
+
37
+ describe '#read_line' do
38
+ before(:each) do
39
+ io.puts "Hello World"
40
+ io.seek(0)
41
+ end
42
+
43
+ it "should read one line" do
44
+ expect(protocol.read_line).to be == "Hello World"
45
+ end
46
+
47
+ it "should be binary encoding" do
48
+ expect(protocol.read_line.encoding).to be == Encoding::BINARY
49
+ end
50
+ end
51
+
52
+ describe '#read_lines' do
53
+ before(:each) do
54
+ io << "Hello\nWorld\n"
55
+ io.seek(0)
56
+ end
57
+
58
+ it "should read multiple lines" do
59
+ expect(protocol.read_lines).to be == ["Hello", "World"]
60
+ end
61
+
62
+ it "should be binary encoding" do
63
+ expect(protocol.read_lines.first.encoding).to be == Encoding::BINARY
64
+ end
65
+ end
66
+ end
@@ -22,44 +22,24 @@ require 'async/io/stream'
22
22
 
23
23
  RSpec.describe Async::IO::Stream do
24
24
  let(:io) {StringIO.new}
25
- let(:stream) {Async::IO::Stream.new(io, eol: "\n")}
25
+ let(:stream) {Async::IO::Stream.new(io)}
26
26
 
27
- describe '#puts' do
28
- it "should write line" do
29
- stream.puts "Hello World"
30
- stream.flush
31
-
32
- expect(io.string).to be == "Hello World\n"
33
- end
34
- end
35
-
36
- describe '#readline' do
37
- before(:each) do
27
+ describe '#read' do
28
+ it "should read everything" do
38
29
  io.puts "Hello World"
39
30
  io.seek(0)
40
- end
41
-
42
- it "should read one line" do
43
- expect(stream.readline).to be == "Hello World\n"
44
- end
45
-
46
- it "should be binary encoding" do
47
- expect(stream.readline.encoding).to be == Encoding::BINARY
31
+
32
+ expect(stream.read).to be == "Hello World\n"
33
+ expect(stream).to be_eof
48
34
  end
49
35
  end
50
36
 
51
- describe '#readlines' do
52
- before(:each) do
53
- io << "Hello\nWorld\n"
54
- io.seek(0)
55
- end
56
-
57
- it "should read multiple lines" do
58
- expect(stream.readlines).to be == ["Hello\n", "World\n"]
59
- end
60
-
61
- it "should be binary encoding" do
62
- expect(stream.readlines.first.encoding).to be == Encoding::BINARY
37
+ describe '#write' do
38
+ it "should read one line" do
39
+ stream.write "Hello World\n"
40
+ stream.flush
41
+
42
+ expect(io.string).to be == "Hello World\n"
63
43
  end
64
44
  end
65
45
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: async-io
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-06-06 00:00:00.000000000 Z
11
+ date: 2017-06-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async
@@ -96,7 +96,9 @@ files:
96
96
  - async-io.gemspec
97
97
  - lib/async/io.rb
98
98
  - lib/async/io/address.rb
99
+ - lib/async/io/binary_string.rb
99
100
  - lib/async/io/generic.rb
101
+ - lib/async/io/protocol/line.rb
100
102
  - lib/async/io/socket.rb
101
103
  - lib/async/io/stream.rb
102
104
  - lib/async/io/tcp_socket.rb
@@ -108,6 +110,7 @@ files:
108
110
  - spec/async/io/c10k_spec.rb
109
111
  - spec/async/io/echo_spec.rb
110
112
  - spec/async/io/generic_spec.rb
113
+ - spec/async/io/protocol/line_spec.rb
111
114
  - spec/async/io/socket_spec.rb
112
115
  - spec/async/io/stream_spec.rb
113
116
  - spec/async/io/tcp_socket_spec.rb
@@ -143,6 +146,7 @@ test_files:
143
146
  - spec/async/io/c10k_spec.rb
144
147
  - spec/async/io/echo_spec.rb
145
148
  - spec/async/io/generic_spec.rb
149
+ - spec/async/io/protocol/line_spec.rb
146
150
  - spec/async/io/socket_spec.rb
147
151
  - spec/async/io/stream_spec.rb
148
152
  - spec/async/io/tcp_socket_spec.rb