async-io 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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