io-stream 0.8.0 → 0.9.1

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: 5f73e60fdaf4001c88fcfaa4ccb29bfc8ee90d2fc3ffc81408c8673b414038ae
4
- data.tar.gz: 933183cda89e7d555239ba15a752dadccc938628d6197675b3bedb8cfeb60f43
3
+ metadata.gz: 506d80a7bb9aee846def3d18d0bf53f2085b751de5d511a8a6a31f8a5de0efd2
4
+ data.tar.gz: 9c828735d78bdeb4dba18b81ed350f5776ba3436cca7f18b17c465d6efd4ce7e
5
5
  SHA512:
6
- metadata.gz: 732318c46b27af3ecabb0c99361b60de5ae3c2733013261ae3089fbaa0f4f2f542e70ee10c410227262a8d137a59d5f1e36ba9419307e801fefd0cfebb412ce5
7
- data.tar.gz: 5ae80afa0d558011183648ce9d34c02dc52e2b8f0cf24bde53a319d1cba3dd188fa8705a477baa8c7fcd5afeb6ee487087e72c1a75d39c80566ea9c378d4e7b1
6
+ metadata.gz: 19d26e89eca00835b6331548e72fc74bf17add777028af9bb6d6a7749ee760eab41a5bae780876de278c6f82eeec02366ce83bb160c798cb441a28dbef56f263
7
+ data.tar.gz: 798d2863752eca4a03d9ae7b21000530442b7a4af218f2e89d1590baa2e821650fcb66689e6ae88ba91b5ec742136ef46c328a7a48afb2d7048990084a12af05
checksums.yaml.gz.sig CHANGED
Binary file
@@ -58,9 +58,18 @@ module IO::Stream
58
58
 
59
59
  # Read data from the stream.
60
60
  # @parameter size [Integer | Nil] The number of bytes to read. If nil, read until end of stream.
61
- # @returns [String] The data read from the stream.
62
- def read(size = nil)
63
- return String.new(encoding: Encoding::BINARY) if size == 0
61
+ # @parameter buffer [String | Nil] An optional buffer to fill with data instead of allocating a new string.
62
+ # @returns [String] The data read from the stream, or the provided buffer filled with data.
63
+ def read(size = nil, buffer = nil)
64
+ if size == 0
65
+ if buffer
66
+ buffer.clear
67
+ buffer.force_encoding(Encoding::BINARY)
68
+ return buffer
69
+ else
70
+ return String.new(encoding: Encoding::BINARY)
71
+ end
72
+ end
64
73
 
65
74
  if size
66
75
  until @done or @read_buffer.bytesize >= size
@@ -74,28 +83,50 @@ module IO::Stream
74
83
  until @done
75
84
  fill_read_buffer
76
85
  end
86
+
87
+ if buffer
88
+ buffer.replace(@read_buffer)
89
+ @read_buffer.clear
90
+ else
91
+ buffer = @read_buffer
92
+ @read_buffer = StringBuffer.new
93
+ end
94
+
95
+ # Read without size always returns a non-nil value, even if it is an empty string.
96
+ return buffer
77
97
  end
78
98
 
79
- return consume_read_buffer(size)
99
+ return consume_read_buffer(size, buffer)
80
100
  end
81
101
 
82
102
  # Read at most `size` bytes from the stream. Will avoid reading from the underlying stream if possible.
83
- def read_partial(size = nil)
84
- return String.new(encoding: Encoding::BINARY) if size == 0
103
+ # @parameter size [Integer | Nil] The number of bytes to read. If nil, read all available data.
104
+ # @parameter buffer [String | Nil] An optional buffer to fill with data instead of allocating a new string.
105
+ # @returns [String] The data read from the stream, or the provided buffer filled with data.
106
+ def read_partial(size = nil, buffer = nil)
107
+ if size == 0
108
+ if buffer
109
+ buffer.clear
110
+ buffer.force_encoding(Encoding::BINARY)
111
+ return buffer
112
+ else
113
+ return String.new(encoding: Encoding::BINARY)
114
+ end
115
+ end
85
116
 
86
117
  if !@done and @read_buffer.empty?
87
118
  fill_read_buffer
88
119
  end
89
120
 
90
- return consume_read_buffer(size)
121
+ return consume_read_buffer(size, buffer)
91
122
  end
92
123
 
93
124
  # Read exactly the specified number of bytes.
94
125
  # @parameter size [Integer] The number of bytes to read.
95
126
  # @parameter exception [Class] The exception to raise if not enough data is available.
96
127
  # @returns [String] The data read from the stream.
97
- def read_exactly(size, exception: EOFError)
98
- if buffer = read(size)
128
+ def read_exactly(size, buffer = nil, exception: EOFError)
129
+ if buffer = read(size, buffer)
99
130
  if buffer.bytesize != size
100
131
  raise exception, "Could not read enough data!"
101
132
  end
@@ -107,8 +138,11 @@ module IO::Stream
107
138
  end
108
139
 
109
140
  # This is a compatibility shim for existing code that uses `readpartial`.
110
- def readpartial(size = nil)
111
- read_partial(size) or raise EOFError, "Encountered done while reading data!"
141
+ # @parameter size [Integer | Nil] The number of bytes to read.
142
+ # @parameter buffer [String | Nil] An optional buffer to fill with data instead of allocating a new string.
143
+ # @returns [String] The data read from the stream.
144
+ def readpartial(size = nil, buffer = nil)
145
+ read_partial(size, buffer) or raise EOFError, "Encountered done while reading data!"
112
146
  end
113
147
 
114
148
  # Find the index of a pattern in the read buffer, reading more data if needed.
@@ -330,25 +364,43 @@ module IO::Stream
330
364
 
331
365
  # Consumes at most `size` bytes from the buffer.
332
366
  # @parameter size [Integer | Nil] The amount of data to consume. If nil, consume entire buffer.
333
- def consume_read_buffer(size = nil)
367
+ # @parameter buffer [String | Nil] An optional buffer to fill with data instead of allocating a new string.
368
+ # @returns [String | Nil] The consumed data, or nil if no data available.
369
+ def consume_read_buffer(size = nil, buffer = nil)
334
370
  # If we are at done, and the read buffer is empty, we can't consume anything.
335
- return nil if @done && @read_buffer.empty?
371
+ if @done && @read_buffer.empty?
372
+ # Clear the buffer even when returning nil
373
+ if buffer
374
+ buffer.clear
375
+ buffer.force_encoding(Encoding::BINARY)
376
+ end
377
+ return nil
378
+ end
336
379
 
337
380
  result = nil
338
381
 
339
382
  if size.nil? or size >= @read_buffer.bytesize
340
383
  # Consume the entire read buffer:
341
- result = @read_buffer
384
+ if buffer
385
+ buffer.clear
386
+ buffer << @read_buffer
387
+ result = buffer
388
+ else
389
+ result = @read_buffer
390
+ end
342
391
  @read_buffer = StringBuffer.new
343
392
  else
344
- # This approach uses more memory.
345
- # result = @read_buffer.slice!(0, size)
346
-
347
393
  # We know that we are not going to reuse the original buffer.
348
394
  # But byteslice will generate a hidden copy. So let's freeze it first:
349
395
  @read_buffer.freeze
350
396
 
351
- result = @read_buffer.byteslice(0, size)
397
+ if buffer
398
+ # Use replace instead of clear + << for better performance
399
+ buffer.replace(@read_buffer.byteslice(0, size))
400
+ result = buffer
401
+ else
402
+ result = @read_buffer.byteslice(0, size)
403
+ end
352
404
  @read_buffer = @read_buffer.byteslice(size, @read_buffer.bytesize)
353
405
  end
354
406
 
@@ -4,5 +4,5 @@
4
4
  # Copyright, 2023-2024, by Samuel Williams.
5
5
 
6
6
  module IO::Stream
7
- VERSION = "0.8.0"
7
+ VERSION = "0.9.1"
8
8
  end
data/readme.md CHANGED
@@ -12,6 +12,14 @@ Please see the [project documentation](https://socketry.github.io/io-stream) for
12
12
 
13
13
  Please see the [project releases](https://socketry.github.io/io-streamreleases/index) for all releases.
14
14
 
15
+ ### v0.9.1
16
+
17
+ - Fix EOF behavior to match Ruby IO semantics: `read()` returns empty string `""` at EOF while `read(size)` returns `nil` at EOF.
18
+
19
+ ### v0.9.0
20
+
21
+ - Add support for `buffer` parameter in `read`, `read_exactly`, and `read_partial` methods to allow reading into a provided buffer.
22
+
15
23
  ### v0.8.0
16
24
 
17
25
  - On Ruby v3.3+, use `IO#write` directly instead of `IO#write_nonblock`, for better performance.
@@ -54,14 +62,6 @@ Please see the [project releases](https://socketry.github.io/io-streamreleases/i
54
62
  - Add `#to_io` method to `IO::Stream::Buffered` for better compatibility.
55
63
  - Modernize gem structure and dependencies.
56
64
 
57
- ### v0.4.0
58
-
59
- - Add convenient `IO.Stream()` constructor method for creating buffered streams.
60
-
61
- ### v0.3.0
62
-
63
- - Add support for timeouts with compatibility shims for various IO types.
64
-
65
65
  ## See Also
66
66
 
67
67
  - [async-io](https://github.com/socketry/async-io) — Where this implementation originally came from.
data/releases.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Releases
2
2
 
3
+ ## v0.9.1
4
+
5
+ - Fix EOF behavior to match Ruby IO semantics: `read()` returns empty string `""` at EOF while `read(size)` returns `nil` at EOF.
6
+
7
+ ## v0.9.0
8
+
9
+ - Add support for `buffer` parameter in `read`, `read_exactly`, and `read_partial` methods to allow reading into a provided buffer.
10
+
3
11
  ## v0.8.0
4
12
 
5
13
  - On Ruby v3.3+, use `IO#write` directly instead of `IO#write_nonblock`, for better performance.
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: io-stream
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
metadata.gz.sig CHANGED
Binary file