Skip to content

Commit

Permalink
Add BufferedRead.peek
Browse files Browse the repository at this point in the history
This allows fetching of bytes without consuming the byte from the
underlying buffer.

Changelog: added
  • Loading branch information
yorickpeterse committed Aug 18, 2024
1 parent aea733d commit d22a556
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 6 deletions.
46 changes: 40 additions & 6 deletions std/src/std/io.inko
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ trait pub Seek {
# A `Read` type using an internal buffer, allowing more efficient reading and
# additional operations.
trait pub BufferedRead: Read {
# Fills the internal buffer by reading from the underlying iterator.
# Fills the internal buffer by reading from the underlying stream.
#
# Upon success, this method returns `Ok(n)` where `n` is the number of bytes
# remaining in the buffer. If the underlying read fails, an `Error` is
Expand All @@ -493,16 +493,35 @@ trait pub BufferedRead: Read {
# # Examples
#
# ```inko
# import std.fs.file (ReadOnlyFile)
# import std.io (BufferedReader)
# import std.io (Buffer, BufferedReader)
#
# let file = ReadOnlyFile.new('README.md').get
# let reader = BufferedReader.new(file)
# let buffer = Buffer.new('hello')
# let reader = BufferedReader.new(buffer)
#
# reader.read_byte # => Result.Ok(Option.Some(35))
# reader.read_byte # => Result.Ok(Option.Some(104))
# reader.read_byte # => Result.Ok(Option.Some(101))
# ```
fn pub mut read_byte -> Result[Option[Int], Error]

# Reads the current byte from the buffer, without consuming it from the
# underlying buffer.
#
# If a byte is read, `Ok(Some(n))` is returned where `n` is the byte. A
# `Ok(None)` indicates the end of the input.
#
# # Examples
#
# ```inko
# import std.io (Buffer, BufferedReader)
#
# let buffer = Buffer.new('hello')
# let reader = BufferedReader.new(buffer)
#
# reader.peek # => Result.Ok(Option.Some(104))
# reader.peek # => Result.Ok(Option.Some(104))
# ```
fn pub mut peek -> Result[Option[Int], Error]

# Read bytes into `into` up to and including the byte specified in the `byte`
# argument.
#
Expand Down Expand Up @@ -704,6 +723,13 @@ impl BufferedRead for BufferedReader {
case _ -> Result.Ok(Option.Some(@buffer.get(@offset := @offset + 1)))
}
}

fn pub mut peek -> Result[Option[Int], Error] {
match try fill_buffer {
case 0 -> Result.Ok(Option.None)
case _ -> Result.Ok(Option.Some(@buffer.get(@offset)))
}
}
}

impl Read for BufferedReader {
Expand Down Expand Up @@ -798,6 +824,14 @@ impl BufferedRead for Buffer {
Result.Ok(Option.None)
}
}

fn pub mut peek -> Result[Option[Int], Error] {
if @offset < @bytes.size {
Result.Ok(Option.Some(@bytes.byte(@offset)))
} else {
Result.Ok(Option.None)
}
}
}

impl Seek for Buffer {
Expand Down
43 changes: 43 additions & 0 deletions std/test/std/test_io.inko
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,29 @@ fn pub tests(t: mut Tests) {
t.equal(err.read_byte, Result.Error(Error.TimedOut))
})

t.test('BufferedReader.peek', fn (t) {
let ok = BufferedReader.new(Reader.new)
let err = BufferedReader.new(ErrorReader())

t.equal(ok.peek, Result.Ok(Option.Some(1)))
t.equal(ok.peek, Result.Ok(Option.Some(1)))
t.equal(ok.read_byte, Result.Ok(Option.Some(1)))

t.equal(ok.peek, Result.Ok(Option.Some(2)))
t.equal(ok.peek, Result.Ok(Option.Some(2)))
t.equal(ok.read_byte, Result.Ok(Option.Some(2)))

t.equal(ok.peek, Result.Ok(Option.Some(3)))
t.equal(ok.peek, Result.Ok(Option.Some(3)))
t.equal(ok.read_byte, Result.Ok(Option.Some(3)))

t.equal(ok.peek, Result.Ok(Option.None))
t.equal(ok.peek, Result.Ok(Option.None))
t.equal(ok.read_byte, Result.Ok(Option.None))

t.equal(err.peek, Result.Error(Error.TimedOut))
})

t.test('BufferedReader.read with a small read size', fn (t) {
let reader = BufferedReader.new(Reader.new)
let bytes = ByteArray.new
Expand Down Expand Up @@ -420,6 +443,26 @@ fn pub tests(t: mut Tests) {
t.equal(buffer.read_byte, Result.Ok(Option.None))
})

t.test('Buffer.peek', fn (t) {
let buffer = Buffer.new('abc')

t.equal(buffer.peek, Result.Ok(Option.Some(97)))
t.equal(buffer.peek, Result.Ok(Option.Some(97)))
t.equal(buffer.read_byte, Result.Ok(Option.Some(97)))

t.equal(buffer.peek, Result.Ok(Option.Some(98)))
t.equal(buffer.peek, Result.Ok(Option.Some(98)))
t.equal(buffer.read_byte, Result.Ok(Option.Some(98)))

t.equal(buffer.peek, Result.Ok(Option.Some(99)))
t.equal(buffer.peek, Result.Ok(Option.Some(99)))
t.equal(buffer.read_byte, Result.Ok(Option.Some(99)))

t.equal(buffer.peek, Result.Ok(Option.None))
t.equal(buffer.peek, Result.Ok(Option.None))
t.equal(buffer.read_byte, Result.Ok(Option.None))
})

t.test('Buffer.seek', fn (t) {
let buffer = Buffer.new('abc')

Expand Down

0 comments on commit d22a556

Please sign in to comment.