io-stream 0.13.2 → 0.14.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
  SHA256:
3
- metadata.gz: ef7273d29f71bdc5f4bcc11b9ae175b6f77e8e554a950c298b646499bfc0b595
4
- data.tar.gz: f3c65d0b5507081da4e7e5b9c5ef2a2fe70dcee885495116249ed5b9921dd407
3
+ metadata.gz: 62f5684554e47e754ddc99d25d193a8ef69b369dc8acc49ec75b0995d58e6720
4
+ data.tar.gz: 0f66daa91f71b6dfd98008eea2cd820d52ae4a87845b888e1eaffdfd45b28955
5
5
  SHA512:
6
- metadata.gz: e3bc5163f033021ce9580a9c45d8d711a12558017bfd68118149838eef8807aadcf8d5e0b980479f6af7ef741b6034e85d118e08c73f832f19a1ea6b2313e22f
7
- data.tar.gz: aab392c94675167955776438debb0f3439dcb87403e9ff75c688722be94b7e78be84e57b5eb32bfae3328a4ec66b3d122e8a4547018c5899b14328bf85fd3d99
6
+ metadata.gz: 0501cc4d95af72fc42dcd55c7a121c3070630f34ff922f3e65aaad2cb22c02c6d78150e82d277dfca6295354746c67663c200bc340341400677a6323321b7819
7
+ data.tar.gz: 6d6084eddbe9717ce8e899b5af4db52fdb377535341b0096fc340bf96116eed5a7a412c7409ba8849a92dd05c758166b2b1d4a05952aa99428b1daa5bc49cd38
checksums.yaml.gz.sig CHANGED
Binary file
@@ -6,6 +6,7 @@
6
6
  require_relative "generic"
7
7
  require_relative "connection_reset_error"
8
8
 
9
+ # Provides buffered IO streams with consistent read, write, and transport semantics.
9
10
  module IO::Stream
10
11
  # A buffered stream implementation that wraps an underlying IO object to provide efficient buffered reading and writing.
11
12
  class Buffered < Generic
@@ -104,6 +105,11 @@ module IO::Stream
104
105
  return @io.write(buffer)
105
106
  end
106
107
 
108
+ # Attempts to read data from the underlying stream without blocking.
109
+ def sysread_nonblock(size, buffer)
110
+ return @io.read_nonblock(size, buffer, exception: false)
111
+ end
112
+
107
113
  # Reads data from the underlying stream as efficiently as possible.
108
114
  def sysread(size, buffer)
109
115
  # Come on Ruby, why couldn't this just return `nil`? EOF is not exceptional. Every file has one.
@@ -23,7 +23,7 @@ module IO::Stream
23
23
 
24
24
  # A module providing readable stream functionality.
25
25
  #
26
- # You must implement the `sysread` method to read data from the underlying IO.
26
+ # You must implement the `sysread` method to read data from the underlying IO. You may implement `sysread_nonblock` to support non-blocking partial peeks.
27
27
  module Readable
28
28
  ASYNC_SAFE = {
29
29
  read: :readable,
@@ -31,6 +31,7 @@ module IO::Stream
31
31
  read_exactly: :readable,
32
32
  read_until: :readable,
33
33
  peek: :readable,
34
+ peek_partial: :readable,
34
35
  gets: :readable,
35
36
  getc: :readable,
36
37
  getbyte: :readable,
@@ -246,6 +247,10 @@ module IO::Stream
246
247
  # @parameter size [Integer | Nil] The number of bytes to peek at. If nil, peek at all available data.
247
248
  # @returns [String] The data in the buffer without consuming it.
248
249
  def peek(size = nil)
250
+ if size == 0
251
+ return String.new(encoding: Encoding::BINARY)
252
+ end
253
+
249
254
  if size
250
255
  until @finished or @read_buffer.bytesize >= size
251
256
  # Compute the amount of data we need to read from the underlying stream:
@@ -265,6 +270,41 @@ module IO::Stream
265
270
  return @read_buffer
266
271
  end
267
272
 
273
+ # Peek at data without consuming it, making at most one non-blocking read attempt.
274
+ #
275
+ # Any data read from the underlying stream is preserved in the read buffer. If
276
+ # the read would block or the stream is at EOF, this method returns `nil`.
277
+ #
278
+ # After this method returns `nil`, {readable?} indicates whether the read would
279
+ # block or EOF was observed.
280
+ #
281
+ # @parameter size [Integer] The maximum number of bytes to peek at.
282
+ # @returns [String | Nil] The immediately available data, or nil if no data can be read without blocking.
283
+ def peek_partial(size = @minimum_read_size)
284
+ if size == 0
285
+ return String.new(encoding: Encoding::BINARY)
286
+ end
287
+
288
+ if @read_buffer.empty?
289
+ if @finished
290
+ return nil
291
+ end
292
+
293
+ read_size = [size, @maximum_read_size].min
294
+
295
+ result = sysread_nonblock(read_size, @read_buffer)
296
+ case result
297
+ when :wait_readable, :wait_writable
298
+ return nil
299
+ when nil
300
+ @finished = true
301
+ return nil
302
+ end
303
+ end
304
+
305
+ return @read_buffer.byteslice(0, [size, @read_buffer.bytesize].min)
306
+ end
307
+
268
308
  # Read a line from the stream, similar to IO#gets.
269
309
  # @parameter separator [String] The line separator to search for.
270
310
  # @parameter limit [Integer | Nil] The maximum number of bytes to read.
@@ -360,6 +400,12 @@ module IO::Stream
360
400
 
361
401
  private
362
402
 
403
+ # Attempts to read data from the underlying stream without blocking.
404
+ # Implementations may override this method when non-blocking reads are supported.
405
+ def sysread_nonblock(size, buffer)
406
+ return :wait_readable
407
+ end
408
+
363
409
  # Fills the buffer from the underlying stream.
364
410
  def fill_read_buffer(size = @minimum_read_size)
365
411
  # Limit the read size to avoid exceeding SSIZE_MAX and to manage memory usage.
@@ -4,5 +4,5 @@
4
4
  # Copyright, 2023-2026, by Samuel Williams.
5
5
 
6
6
  module IO::Stream
7
- VERSION = "0.13.2"
7
+ VERSION = "0.14.0"
8
8
  end
data/readme.md CHANGED
@@ -22,6 +22,10 @@ Please see the [project documentation](https://socketry.github.io/io-stream/) fo
22
22
 
23
23
  Please see the [project releases](https://socketry.github.io/io-stream/releases/index) for all releases.
24
24
 
25
+ ### v0.14.0
26
+
27
+ - Add `IO::Stream::Readable#peek_partial` to peek through layered transports without blocking or consuming application data.
28
+
25
29
  ### v0.13.1
26
30
 
27
31
  - Set minimum Ruby verison to 3.3.6 to avoid hanging `close` issue in older Ruby versions.
@@ -65,10 +69,6 @@ Please see the [project releases](https://socketry.github.io/io-stream/releases/
65
69
  - Remove unused timeout shim functionality.
66
70
  - 100% documentation coverage.
67
71
 
68
- ### v0.6.1
69
-
70
- - Fix compatibility with Ruby v3.3.0 - v3.3.6 where broken `@io.close` could hang.
71
-
72
72
  ## See Also
73
73
 
74
74
  - [async-io](https://github.com/socketry/async-io) — Where this implementation originally came from.
data/releases.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Releases
2
2
 
3
+ ## v0.14.0
4
+
5
+ - Add `IO::Stream::Readable#peek_partial` to peek through layered transports without blocking or consuming application data.
6
+
3
7
  ## v0.13.1
4
8
 
5
9
  - Set minimum Ruby verison to 3.3.6 to avoid hanging `close` issue in older Ruby versions.
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.13.2
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
metadata.gz.sig CHANGED
Binary file