diff --git a/spec/std/io/io_spec.cr b/spec/std/io/io_spec.cr index 82fc36648490..56a786614a1c 100644 --- a/spec/std/io/io_spec.cr +++ b/spec/std/io/io_spec.cr @@ -275,6 +275,21 @@ describe IO do io = SimpleIOMemory.new("foo\nbar\nbaz\n") io.gets.should eq("foo") io.gets_to_end.should eq("bar\nbaz\n") + io.gets_to_end.should eq("") + end + + it "reads all remaining content as bytes" do + io = SimpleIOMemory.new(Bytes[0, 1, 3, 6, 10, 15]) + io.getb_to_end.should eq(Bytes[0, 1, 3, 6, 10, 15]) + io.getb_to_end.should eq(Bytes.new(0)) + io.rewind + bytes = io.getb_to_end + bytes.should eq(Bytes[0, 1, 3, 6, 10, 15]) + bytes.read_only?.should be_false + + io.rewind + io.write(Bytes[2, 4, 5]) + bytes.should eq(Bytes[0, 1, 3, 6, 10, 15]) end it "reads char" do diff --git a/spec/std/io/memory_spec.cr b/spec/std/io/memory_spec.cr index 0a9724aac4f9..db2276a9c963 100644 --- a/spec/std/io/memory_spec.cr +++ b/spec/std/io/memory_spec.cr @@ -358,6 +358,23 @@ describe IO::Memory do io.gets_to_end.should eq("") end + it "consumes with getb_to_end" do + io = IO::Memory.new(Bytes[0, 1, 3, 6, 10, 15]) + io.getb_to_end.should eq(Bytes[0, 1, 3, 6, 10, 15]) + io.getb_to_end.should eq(Bytes.new(0)) + io.seek(3) + bytes = io.getb_to_end + bytes.should eq(Bytes[6, 10, 15]) + bytes.read_only?.should be_false + + io.seek(3) + io.write(Bytes[2, 4, 5]) + bytes.should eq(Bytes[6, 10, 15]) + + io.seek(10) + io.getb_to_end.should eq(Bytes.new(0)) + end + it "peeks" do str = "hello world" io = IO::Memory.new(str) diff --git a/src/io.cr b/src/io.cr index 61f28dcc1f3b..782f0eb31b5a 100644 --- a/src/io.cr +++ b/src/io.cr @@ -49,7 +49,7 @@ require "c/errno" # An `IO` can be set an encoding with the `#set_encoding` method. When this is # set, all string operations (`gets`, `gets_to_end`, `read_char`, `<<`, `print`, `puts` # `printf`) will write in the given encoding, and read from the given encoding. -# Byte operations (`read`, `write`, `read_byte`, `write_byte`) never do +# Byte operations (`read`, `write`, `read_byte`, `write_byte`, `getb_to_end`) never do # encoding/decoding operations. # # If an encoding is not set, the default one is UTF-8. @@ -546,6 +546,7 @@ abstract class IO # ``` # io = IO::Memory.new "hello world" # io.gets_to_end # => "hello world" + # io.gets_to_end # => "" # ``` def gets_to_end : String String.build do |str| @@ -565,6 +566,19 @@ abstract class IO end end + # Reads the rest of this `IO` data as a writable `Bytes`. + # + # ``` + # io = IO::Memory.new Bytes[0, 1, 3, 6, 10, 15] + # io.getb_to_end # => Bytes[0, 1, 3, 6, 10, 15] + # io.getb_to_end # => Bytes[] + # ``` + def getb_to_end : Bytes + io = IO::Memory.new + IO.copy(self, io) + io.to_slice + end + # Reads a line from this `IO`. A line is terminated by the `\n` character. # Returns `nil` if called at the end of this `IO`. # diff --git a/src/io/memory.cr b/src/io/memory.cr index fe235d9e0afe..8bab757aa181 100644 --- a/src/io/memory.cr +++ b/src/io/memory.cr @@ -207,19 +207,31 @@ class IO::Memory < IO @pos = @bytesize end + # :inherit: def gets_to_end : String return super if @encoding check_open - pos = Math.min(@pos, @bytesize) - - if pos == @bytesize + if @pos >= @bytesize "" else - String.new(@buffer + @pos, @bytesize - @pos).tap do - @pos = @bytesize - end + str = String.new(@buffer + @pos, @bytesize - @pos) + @pos = @bytesize + str + end + end + + # :inherit: + def getb_to_end : Bytes + check_open + + if @pos >= @bytesize + Bytes.new(0) + else + bytes = Slice.new(@buffer + @pos, @bytesize - @pos).dup + @pos = @bytesize + bytes end end