Skip to content

Commit 23fd68c

Browse files
Add IO#read_greedy (#16535)
Co-authored-by: Johannes Müller <straightshoota@gmail.com>
1 parent 388b48e commit 23fd68c

File tree

2 files changed

+39
-2
lines changed

2 files changed

+39
-2
lines changed

spec/std/io/io_spec.cr

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,17 @@ describe IO do
486486
IO.same_content?(io1, io2).should be_false
487487
end
488488
end
489+
490+
it "combines multiple reads using #read_greedy" do
491+
bytes = Bytes.new 7
492+
493+
io = SimpleIOMemory.new("Hello World", max_read: 2)
494+
io.read_greedy(bytes).should eq(7)
495+
bytes.should eq("Hello W".to_slice)
496+
io.read_greedy(bytes).should eq(4)
497+
bytes[0, 4].should eq("orld".to_slice)
498+
io.read_greedy(bytes).should eq(0)
499+
end
489500
end
490501

491502
describe "write operations" do

src/io.cr

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,9 @@ abstract class IO
523523
# slice # => Bytes[49, 50, 51, 52, 53]
524524
# io.read_fully(slice) # raises IO::EOFError
525525
# ```
526+
#
527+
# `#read_greedy` also tries to fill the entire buffer if possible,
528+
# but still allows the partially filled slice to be used if an early EOF was reached.
526529
def read_fully(slice : Bytes) : Int32
527530
read_fully?(slice) || raise(EOFError.new)
528531
end
@@ -538,11 +541,34 @@ abstract class IO
538541
# slice # => Bytes[49, 50, 51, 52, 53]
539542
# io.read_fully?(slice) # => nil
540543
# ```
544+
#
545+
# `#read_greedy` also tries to fill the entire buffer if possible,
546+
# but still allows the partially filled slice to be used if an early EOF was reached.
541547
def read_fully?(slice : Bytes) : Int32?
548+
count = read_greedy(slice)
549+
return nil if count != slice.size
550+
count
551+
end
552+
553+
# Similar to `#read`, but with the additional guarantee that either
554+
# the buffer will be entirely filled or the EOF will be reached while trying.
555+
#
556+
# Calling this method may result in multiple calls to `#read` if necessary.
557+
#
558+
# ```
559+
# io = IO::Memory.new "123451234"
560+
# slice = Bytes.new(5)
561+
# io.read_greedy(slice) # => 5
562+
# slice # => Bytes[49, 50, 51, 52, 53]
563+
# io.read_greedy(slice) # => 4
564+
# ```
565+
#
566+
# `#read_fully` and `#read_fully?` also try to fill the entire buffer but error on unexpected EOF.
567+
def read_greedy(slice : Bytes) : Int32
542568
count = slice.size
543569
while slice.size > 0
544-
read_bytes = read slice
545-
return nil if read_bytes == 0
570+
read_bytes = read(slice)
571+
return count &- slice.size if read_bytes == 0
546572
slice += read_bytes
547573
end
548574
count

0 commit comments

Comments
 (0)