From d8c59a292b86a77d71fad708d758d1e340447b55 Mon Sep 17 00:00:00 2001 From: David Keller Date: Fri, 26 Dec 2025 19:55:16 +0100 Subject: [PATCH 1/2] feat: add #read_greedy method --- src/io.cr | 22 ++++++++++++++++++++++ src/io/memory.cr | 5 +++++ 2 files changed, 27 insertions(+) diff --git a/src/io.cr b/src/io.cr index 4b94d2f29baa..4193a823ea76 100644 --- a/src/io.cr +++ b/src/io.cr @@ -548,6 +548,28 @@ abstract class IO count end + # Similar to `#read`, but with the additional guarantee that either + # the buffer will be entirely filled or the EOF will be reached while trying. + # + # Calling this method may result in multiple read calls if necessary. + # + # ``` + # io = IO::Memory.new "123451234" + # slice = Bytes.new(5) + # io.read_greedy(slice) # => 5 + # slice # => Bytes[49, 50, 51, 52, 53] + # io.read_greedy(slice) # => 4 + # ``` + def read_greedy(slice : Bytes) : Int32 + count = slice.size + while slice.size > 0 + read_bytes = read(slice) + return slice_total_size &- slice.size if read_bytes == 0 + slice += read_bytes + end + count + end + # Reads the rest of this `IO` data as a `String`. # # ``` diff --git a/src/io/memory.cr b/src/io/memory.cr index 493db2de3cd7..9678c241fc47 100644 --- a/src/io/memory.cr +++ b/src/io/memory.cr @@ -175,6 +175,11 @@ class IO::Memory < IO end end + def read_greedy(slice : Bytes) : Int32 + # IO::Memory is always greedy + read(slice) + end + def peek : Bytes check_open From 4e9f608d1d460c5775c76a52435855103d95eefd Mon Sep 17 00:00:00 2001 From: David Keller Date: Mon, 5 Jan 2026 19:39:08 +0100 Subject: [PATCH 2/2] add specs, fix pr issues --- spec/std/io/io_spec.cr | 11 +++++++++++ src/io.cr | 2 +- src/io/memory.cr | 5 ----- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/spec/std/io/io_spec.cr b/spec/std/io/io_spec.cr index abff1cef0a7e..dfe944e98000 100644 --- a/spec/std/io/io_spec.cr +++ b/spec/std/io/io_spec.cr @@ -486,6 +486,17 @@ describe IO do IO.same_content?(io1, io2).should be_false end end + + it "combines multiple reads using #read_greedy" do + bytes = Bytes.new 7 + + io = SimpleIOMemory.new("Hello World", max_read: 2) + io.read_greedy(bytes).should eq(7) + bytes.should eq("Hello W".to_slice) + io.read_greedy(bytes).should eq(4) + bytes[0, 4].should eq("orld".to_slice) + io.read_greedy(bytes).should eq(0) + end end describe "write operations" do diff --git a/src/io.cr b/src/io.cr index 4193a823ea76..b1a076eb5e5d 100644 --- a/src/io.cr +++ b/src/io.cr @@ -564,7 +564,7 @@ abstract class IO count = slice.size while slice.size > 0 read_bytes = read(slice) - return slice_total_size &- slice.size if read_bytes == 0 + return count &- slice.size if read_bytes == 0 slice += read_bytes end count diff --git a/src/io/memory.cr b/src/io/memory.cr index 9678c241fc47..493db2de3cd7 100644 --- a/src/io/memory.cr +++ b/src/io/memory.cr @@ -175,11 +175,6 @@ class IO::Memory < IO end end - def read_greedy(slice : Bytes) : Int32 - # IO::Memory is always greedy - read(slice) - end - def peek : Bytes check_open