Module: IO::Stream::Readable
- Included in:
- Generic
- Defined in:
- lib/io/stream/readable.rb
Overview
A module providing readable stream functionality.
You must implement the ‘sysread` method to read data from the underlying IO.
Constant Summary collapse
- ASYNC_SAFE =
{ read: :readable, read_partial: :readable, read_exactly: :readable, read_until: :readable, peek: :readable, gets: :readable, getc: :readable, getbyte: :readable, readline: :readable, readlines: :readable, readable?: :readable, fill_read_buffer: :readable, eof?: :readable, finished?: :readable, }.freeze
Instance Attribute Summary collapse
-
#minimum_read_size ⇒ Object
Returns the value of attribute minimum_read_size.
Class Method Summary collapse
-
.async_safe?(method) ⇒ Boolean
Check if a method is async-safe.
Instance Method Summary collapse
-
#block_size ⇒ Object
Legacy accessor for backwards compatibility.
-
#block_size=(value) ⇒ Object
Legacy setter for backwards compatibility.
-
#close_read ⇒ Object
Close the read end of the stream.
-
#discard_until(pattern, offset = 0, limit: nil) ⇒ Object
Efficiently discard data from the stream until encountering pattern.
-
#finish! ⇒ Object
(also: #eof!)
Mark the stream as finished and raise ‘EOFError`.
-
#finished? ⇒ Boolean
(also: #eof?)
Determins if the stream has consumed all available data.
-
#gets(separator = $/, limit = nil, chomp: false) ⇒ Object
Read a line from the stream, similar to IO#gets.
-
#initialize(minimum_read_size: MINIMUM_READ_SIZE, maximum_read_size: MAXIMUM_READ_SIZE, block_size: nil, &block) ⇒ Object
Initialize readable stream functionality.
-
#peek(size = nil) ⇒ Object
Peek at data in the buffer without consuming it.
-
#read(size = nil, buffer = nil) ⇒ Object
Read data from the stream.
-
#read_exactly(size, buffer = nil, exception: EOFError) ⇒ Object
Read exactly the specified number of bytes.
-
#read_partial(size = nil, buffer = nil) ⇒ Object
Read at most ‘size` bytes from the stream.
-
#read_until(pattern, offset = 0, limit: nil, chomp: true) ⇒ Object
Efficiently read data from the stream until encountering pattern.
-
#readable? ⇒ Boolean
Whether there is a chance that a read operation will succeed or not.
-
#readpartial(size = nil, buffer = nil) ⇒ Object
This is a compatibility shim for existing code that uses ‘readpartial`.
Instance Attribute Details
#minimum_read_size ⇒ Object
Returns the value of attribute minimum_read_size.
70 71 72 |
# File 'lib/io/stream/readable.rb', line 70 def minimum_read_size @minimum_read_size end |
Class Method Details
.async_safe?(method) ⇒ Boolean
Check if a method is async-safe.
49 50 51 |
# File 'lib/io/stream/readable.rb', line 49 def self.async_safe?(method) ASYNC_SAFE.fetch(method, false) end |
Instance Method Details
#block_size ⇒ Object
Legacy accessor for backwards compatibility
74 75 76 |
# File 'lib/io/stream/readable.rb', line 74 def block_size @minimum_read_size end |
#block_size=(value) ⇒ Object
Legacy setter for backwards compatibility
80 81 82 |
# File 'lib/io/stream/readable.rb', line 80 def block_size=(value) @minimum_read_size = value end |
#close_read ⇒ Object
Close the read end of the stream.
355 356 |
# File 'lib/io/stream/readable.rb', line 355 def close_read end |
#discard_until(pattern, offset = 0, limit: nil) ⇒ Object
Efficiently discard data from the stream until encountering pattern.
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
# File 'lib/io/stream/readable.rb', line 228 def discard_until(pattern, offset = 0, limit: nil) if index = index_of(pattern, offset, limit, true) @read_buffer.freeze if limit and index >= limit @read_buffer = @read_buffer.byteslice(limit, @read_buffer.bytesize) return nil end matched = @read_buffer.byteslice(0, index+pattern.bytesize) @read_buffer = @read_buffer.byteslice(index+pattern.bytesize, @read_buffer.bytesize) return matched end end |
#finish! ⇒ Object Also known as: eof!
Mark the stream as finished and raise ‘EOFError`.
328 329 330 331 332 333 |
# File 'lib/io/stream/readable.rb', line 328 def finish! @read_buffer.clear @finished = true raise EOFError end |
#finished? ⇒ Boolean Also known as: eof?
Determins if the stream has consumed all available data. May block if the stream is not readable. See #readable? for a non-blocking alternative.
315 316 317 318 319 320 321 322 323 |
# File 'lib/io/stream/readable.rb', line 315 def finished? if !@read_buffer.empty? return false elsif @finished return true else return !self.fill_read_buffer end end |
#gets(separator = $/, limit = nil, chomp: false) ⇒ Object
Read a line from the stream, similar to IO#gets.
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 |
# File 'lib/io/stream/readable.rb', line 270 def gets(separator = $/, limit = nil, chomp: false) # Compatibility with IO#gets: if separator.is_a?(Integer) limit = separator separator = $/ end # We don't want to split in the middle of the separator, so we subtract the size of the separator from the start of the search: split_offset = separator.bytesize - 1 offset = 0 until index = @read_buffer.index(separator, offset) offset = @read_buffer.bytesize - split_offset offset = 0 if offset < 0 # If a limit was given, and the offset is beyond the limit, we should return up to the limit: if limit and offset >= limit # As we didn't find the separator, there is nothing to chomp either. return consume_read_buffer(limit) end # If we can't read any more data, we should return what we have: return consume_read_buffer unless fill_read_buffer end # If the index of the separator was beyond the limit: if limit and index >= limit # Return up to the limit: return consume_read_buffer(limit) end # Freeze the read buffer, as this enables us to use byteslice without generating a hidden copy: @read_buffer.freeze line = @read_buffer.byteslice(0, index+(chomp ? 0 : separator.bytesize)) @read_buffer = @read_buffer.byteslice(index+separator.bytesize, @read_buffer.bytesize) return line end |
#initialize(minimum_read_size: MINIMUM_READ_SIZE, maximum_read_size: MAXIMUM_READ_SIZE, block_size: nil, &block) ⇒ Object
Initialize readable stream functionality.
57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/io/stream/readable.rb', line 57 def initialize(minimum_read_size: MINIMUM_READ_SIZE, maximum_read_size: MAXIMUM_READ_SIZE, block_size: nil, **, &block) @finished = false @read_buffer = StringBuffer.new # Used as destination buffer for underlying reads. @input_buffer = StringBuffer.new # Support legacy block_size parameter for backwards compatibility @minimum_read_size = block_size || minimum_read_size @maximum_read_size = maximum_read_size super(**, &block) if defined?(super) end |
#peek(size = nil) ⇒ Object
Peek at data in the buffer without consuming it.
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
# File 'lib/io/stream/readable.rb', line 248 def peek(size = nil) if size until @finished or @read_buffer.bytesize >= size # Compute the amount of data we need to read from the underlying stream: read_size = size - @read_buffer.bytesize # Don't read less than @minimum_read_size to avoid lots of small reads: fill_read_buffer(read_size > @minimum_read_size ? read_size : @minimum_read_size) end return @read_buffer[..([size, @read_buffer.size].min - 1)] end until (block_given? && yield(@read_buffer)) or @finished fill_read_buffer end return @read_buffer end |
#read(size = nil, buffer = nil) ⇒ Object
Read data from the stream.
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/io/stream/readable.rb', line 88 def read(size = nil, buffer = nil) if size == 0 if buffer buffer.clear buffer.force_encoding(Encoding::BINARY) return buffer else return String.new(encoding: Encoding::BINARY) end end if size until @finished or @read_buffer.bytesize >= size # Compute the amount of data we need to read from the underlying stream: read_size = size - @read_buffer.bytesize # Don't read less than @minimum_read_size to avoid lots of small reads: fill_read_buffer(read_size > @minimum_read_size ? read_size : @minimum_read_size) end else until @finished fill_read_buffer end if buffer buffer.replace(@read_buffer) @read_buffer.clear else buffer = @read_buffer @read_buffer = StringBuffer.new end # Read without size always returns a non-nil value, even if it is an empty string. return buffer end return consume_read_buffer(size, buffer) end |
#read_exactly(size, buffer = nil, exception: EOFError) ⇒ Object
Read exactly the specified number of bytes.
153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/io/stream/readable.rb', line 153 def read_exactly(size, buffer = nil, exception: EOFError) if buffer = read(size, buffer) if buffer.bytesize != size raise exception, "Could not read enough data!" end return buffer end raise exception, "Stream finished before reading enough data!" end |
#read_partial(size = nil, buffer = nil) ⇒ Object
Read at most ‘size` bytes from the stream. Will avoid reading from the underlying stream if possible.
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/io/stream/readable.rb', line 131 def read_partial(size = nil, buffer = nil) if size == 0 if buffer buffer.clear buffer.force_encoding(Encoding::BINARY) return buffer else return String.new(encoding: Encoding::BINARY) end end if !@finished and @read_buffer.empty? fill_read_buffer end return consume_read_buffer(size, buffer) end |
#read_until(pattern, offset = 0, limit: nil, chomp: true) ⇒ Object
Efficiently read data from the stream until encountering pattern.
211 212 213 214 215 216 217 218 219 220 221 |
# File 'lib/io/stream/readable.rb', line 211 def read_until(pattern, offset = 0, limit: nil, chomp: true) if index = index_of(pattern, offset, limit) return nil if limit and index >= limit @read_buffer.freeze matched = @read_buffer.byteslice(0, index+(chomp ? 0 : pattern.bytesize)) @read_buffer = @read_buffer.byteslice(index+pattern.bytesize, @read_buffer.bytesize) return matched end end |
#readable? ⇒ Boolean
Whether there is a chance that a read operation will succeed or not.
339 340 341 342 343 344 345 346 347 348 349 350 351 352 |
# File 'lib/io/stream/readable.rb', line 339 def readable? # If we are at the end of the file, we can't read any more data: if @finished return false end # If the read buffer is not empty, we can read more data: if !@read_buffer.empty? return true end # If the underlying stream is readable, we can read more data: return !closed? end |
#readpartial(size = nil, buffer = nil) ⇒ Object
This is a compatibility shim for existing code that uses ‘readpartial`.
169 170 171 |
# File 'lib/io/stream/readable.rb', line 169 def readpartial(size = nil, buffer = nil) read_partial(size, buffer) or raise EOFError, "Stream finished before reading enough data!" end |