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 4b94d2f29baa..b1a076eb5e5d 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 count &- slice.size if read_bytes == 0 + slice += read_bytes + end + count + end + # Reads the rest of this `IO` data as a `String`. # # ```