Skip to content

ReadLine & Peek #13439

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion lib/std/compress/deflate/compressor_test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ test "very long sparse chunk" {
const Self = @This();
const Error = error{};

pub const Reader = io.Reader(*Self, Error, read);
pub const Reader = io.Reader(*Self, Error, read, peek);

pub fn reader(self: *Self) Reader {
return .{ .context = self };
Expand Down Expand Up @@ -216,6 +216,28 @@ test "very long sparse chunk" {
s.cur = cur;
return n;
}

fn peek(s: *Self, b: []u8) Error!usize {
var n: usize = 0; // amount read

if (s.cur >= s.l) {
return 0;
}
n = b.len;
var cur = s.cur + n;
if (cur > s.l) {
n -= cur - s.l;
cur = s.l;
}
for (b[0..n]) |_, i| {
if (s.cur + i >= s.l -| (1 << 16)) {
b[i] = 1;
} else {
b[i] = 0;
}
}
return n;
}
};

var comp = try compressor(
Expand Down
2 changes: 1 addition & 1 deletion lib/std/compress/deflate/decompressor.zig
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ pub fn Decompressor(comptime ReaderType: type) type {
error{EndOfStream} ||
InflateError ||
Allocator.Error;
pub const Reader = io.Reader(*Self, Error, read);
pub const Reader = io.Reader(*Self, Error, read, null);

allocator: Allocator,

Expand Down
27 changes: 26 additions & 1 deletion lib/std/compress/gzip.zig
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub fn GzipStream(comptime ReaderType: type) type {
pub const Error = ReaderType.Error ||
deflate.Decompressor(ReaderType).Error ||
error{ CorruptedData, WrongChecksum };
pub const Reader = io.Reader(*Self, Error, read);
pub const Reader = io.Reader(*Self, Error, read, peek);

allocator: mem.Allocator,
inflater: deflate.Decompressor(ReaderType),
Expand Down Expand Up @@ -140,6 +140,31 @@ pub fn GzipStream(comptime ReaderType: type) type {
return 0;
}

// Implements the io.Reader interface
pub fn peek(self: *Self, buffer: []u8) Error!usize {
if (buffer.len == 0)
return 0;

// Read from the compressed stream and update the computed checksum
const r = try self.inflater.read(buffer);
if (r != 0) {
self.hasher.update(buffer[0..r]);
return r;
}

// We've reached the end of stream, check if the checksum matches
const hash = try self.in_reader.readIntLittle(u32);
if (hash != self.hasher.final())
return error.WrongChecksum;

// The ISIZE field is the size of the uncompressed input modulo 2^32
const input_size = try self.in_reader.readIntLittle(u32);
if (self.read_amt & 0xffffffff != input_size)
return error.CorruptedData;

return 0;
}

pub fn reader(self: *Self) Reader {
return .{ .context = self };
}
Expand Down
2 changes: 1 addition & 1 deletion lib/std/compress/zlib.zig
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub fn ZlibStream(comptime ReaderType: type) type {
pub const Error = ReaderType.Error ||
deflate.Decompressor(ReaderType).Error ||
error{ WrongChecksum, Unsupported };
pub const Reader = io.Reader(*Self, Error, read);
pub const Reader = io.Reader(*Self, Error, read, null);

allocator: mem.Allocator,
inflater: deflate.Decompressor(ReaderType),
Expand Down
75 changes: 74 additions & 1 deletion lib/std/fifo.zig
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pub fn LinearFifo(
count: usize,

const Self = @This();
pub const Reader = std.io.Reader(*Self, error{}, readFn);
pub const Reader = std.io.Reader(*Self, error{}, readFn, peekFn);
pub const Writer = std.io.Writer(*Self, error{OutOfMemory}, appendWrite);

// Type of Self argument for slice operations.
Expand Down Expand Up @@ -205,6 +205,14 @@ pub fn LinearFifo(
return c;
}

/// Peek the next item from the fifo
pub fn peekItemNext(self: *Self) ?T {
if (self.count == 0) return null;

const c = self.buf[self.head];
return c;
}

/// Read data from the fifo into `dst`, returns number of items copied.
pub fn read(self: *Self, dst: []T) usize {
var dst_left = dst;
Expand All @@ -221,12 +229,32 @@ pub fn LinearFifo(
return dst.len - dst_left.len;
}

pub fn peek(self: *Self, dst: []T) usize {
var dst_left = dst;

var off: usize = 0;
while (dst_left.len > 0) {
const slice = self.readableSlice(off);
if (slice.len == 0) break;
const n = math.min(slice.len, dst_left.len);
mem.copy(T, dst_left, slice[off..n]);
off += n;
dst_left = dst_left[n..];
}

return dst.len - dst_left.len;
}

/// Same as `read` except it returns an error union
/// The purpose of this function existing is to match `std.io.Reader` API.
fn readFn(self: *Self, dest: []u8) error{}!usize {
return self.read(dest);
}

fn peekFn(self: *Self, dest: []u8) error{}!usize {
return self.peek(dest);
}

pub fn reader(self: *Self) Reader {
return .{ .context = self };
}
Expand Down Expand Up @@ -521,3 +549,48 @@ test "LinearFifo" {
}
}
}

test "LinearFifo Peek" {
inline for ([_]type{ u1, u8, u16, u64 }) |T| {
inline for ([_]LinearFifoBufferType{ LinearFifoBufferType{ .Static = 32 }, .Slice, .Dynamic }) |bt| {
const FifoType = LinearFifo(T, bt);
var buf: if (bt == .Slice) [32]T else void = undefined;
var fifo = switch (bt) {
.Static => FifoType.init(),
.Slice => FifoType.init(buf[0..]),
.Dynamic => FifoType.init(testing.allocator),
};
defer fifo.deinit();

try fifo.write(&[_]T{ 0, 1, 1, 0, 1 });
try testing.expectEqual(@as(usize, 5), fifo.readableLength());

{
var a = fifo.peekItemNext().?;
var b = fifo.readItem().?;

try testing.expectEqual(a, b);
try testing.expectEqual(@as(usize, 4), fifo.readableLength());

var c = fifo.peekItemNext().?;
try testing.expectEqual(@as(T, 1), c);
try testing.expectEqual(@as(usize, 4), fifo.readableLength());

fifo.discard(fifo.readableLength());
}

{
try fifo.writeItem(1);
try fifo.writeItem(1);
try fifo.writeItem(1);
try testing.expectEqual(@as(usize, 3), fifo.readableLength());
}

{
var readBuf: [3]T = undefined;
const n = fifo.read(&readBuf);
try testing.expectEqual(@as(usize, 3), n); // NOTE: It should be the number of items.
}
}
}
}
18 changes: 17 additions & 1 deletion lib/std/fs/file.zig
Original file line number Diff line number Diff line change
Expand Up @@ -977,6 +977,22 @@ pub const File = struct {
}
}

pub fn peek(self: File, buffer: []u8) !usize {
if (is_windows) {
var size = windows.ReadFile(self.handle, buffer, null, self.intended_io_mode) catch |e| {
return e;
};
os.lseek_CUR(self.handle, -@bitCast(isize, size)) catch unreachable;
return size;
}

var size = os.read(self.handle, buffer) catch |e| {
return e;
};
os.lseek_CUR(self.handle, -@bitCast(isize, size)) catch unreachable;
return size;
}

/// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
/// means the file reached the end. Reaching the end of a file is not an error condition.
pub fn readAll(self: File, buffer: []u8) ReadError!usize {
Expand Down Expand Up @@ -1374,7 +1390,7 @@ pub const File = struct {
}
}

pub const Reader = io.Reader(File, ReadError, read);
pub const Reader = io.Reader(File, ReadError, read, peek);

pub fn reader(file: File) Reader {
return .{ .context = file };
Expand Down
2 changes: 1 addition & 1 deletion lib/std/io/bit_reader.zig
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub fn BitReader(comptime endian: std.builtin.Endian, comptime ReaderType: type)
bit_count: u3,

pub const Error = ReaderType.Error;
pub const Reader = io.Reader(*Self, Error, read);
pub const Reader = io.Reader(*Self, Error, read, null);

const Self = @This();
const u8_bit_count = @bitSizeOf(u8);
Expand Down
25 changes: 23 additions & 2 deletions lib/std/io/buffered_reader.zig
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub fn BufferedReader(comptime buffer_size: usize, comptime ReaderType: type) ty
fifo: FifoType = FifoType.init(),

pub const Error = ReaderType.Error;
pub const Reader = io.Reader(*Self, Error, read);
pub const Reader = io.Reader(*Self, Error, read, peek);

const Self = @This();
const FifoType = std.fifo.LinearFifo(u8, std.fifo.LinearFifoBufferType{ .Static = buffer_size });
Expand All @@ -35,6 +35,27 @@ pub fn BufferedReader(comptime buffer_size: usize, comptime ReaderType: type) ty
return dest.len;
}

fn peek(self: *Self, dest: []u8) Error!usize {
var dest_index: usize = 0;
while (dest_index < dest.len) {
const written = self.fifo.peek(dest[dest_index..]);
if (written == 0) {
// fifo empty, fill it
const writable = self.fifo.writableSlice(0);
assert(writable.len > 0);
const n = try self.unbuffered_reader.peek(writable);
if (n == 0) {
// reading from the unbuffered stream returned nothing
// so we have nothing left to read.
return dest_index;
}
self.fifo.update(n);
}
dest_index += written;
}
return dest.len;
}

pub fn reader(self: *Self) Reader {
return .{ .context = self };
}
Expand All @@ -52,7 +73,7 @@ test "io.BufferedReader" {

const Error = error{NoError};
const Self = @This();
const Reader = io.Reader(*Self, Error, read);
const Reader = io.Reader(*Self, Error, read, null);

fn init(str: []const u8) Self {
return Self{
Expand Down
7 changes: 6 additions & 1 deletion lib/std/io/counting_reader.zig
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,19 @@ pub fn CountingReader(comptime ReaderType: anytype) type {
bytes_read: u64 = 0,

pub const Error = ReaderType.Error;
pub const Reader = io.Reader(*@This(), Error, read);
pub const Reader = io.Reader(*@This(), Error, read, peek);

pub fn read(self: *@This(), buf: []u8) Error!usize {
const amt = try self.child_reader.read(buf);
self.bytes_read += amt;
return amt;
}

pub fn peek(self: *@This(), buf: []u8) Error!usize {
const amt = try self.child_reader.read(buf);
return amt;
}

pub fn reader(self: *@This()) Reader {
return .{ .context = self };
}
Expand Down
11 changes: 10 additions & 1 deletion lib/std/io/fixed_buffer_stream.zig
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub fn FixedBufferStream(comptime Buffer: type) type {
pub const SeekError = error{};
pub const GetSeekPosError = error{};

pub const Reader = io.Reader(*Self, ReadError, read);
pub const Reader = io.Reader(*Self, ReadError, read, peek);
pub const Writer = io.Writer(*Self, WriteError, write);

pub const SeekableStream = io.SeekableStream(
Expand Down Expand Up @@ -54,6 +54,15 @@ pub fn FixedBufferStream(comptime Buffer: type) type {
return size;
}

pub fn peek(self: *Self, dest: []u8) ReadError!usize {
const size = std.math.min(dest.len, self.buffer.len - self.pos);
const end = self.pos + size;

mem.copy(u8, dest[0..size], self.buffer[self.pos..end]);

return size;
}

/// If the returned number of bytes written is less than requested, the
/// buffer is full. Returns `error.NoSpaceLeft` when no bytes would be written.
/// Note: `error.NoSpaceLeft` matches the corresponding error from
Expand Down
8 changes: 7 additions & 1 deletion lib/std/io/limited_reader.zig
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub fn LimitedReader(comptime ReaderType: type) type {
bytes_left: u64,

pub const Error = ReaderType.Error;
pub const Reader = io.Reader(*Self, Error, read);
pub const Reader = io.Reader(*Self, Error, read, peek);

const Self = @This();

Expand All @@ -20,6 +20,12 @@ pub fn LimitedReader(comptime ReaderType: type) type {
return n;
}

pub fn peek(self: *Self, dest: []u8) Error!usize {
const max_read = std.math.min(self.bytes_left, dest.len);
const n = try self.inner_reader.read(dest[0..max_read]);
return n;
}

pub fn reader(self: *Self) Reader {
return .{ .context = self };
}
Expand Down
2 changes: 1 addition & 1 deletion lib/std/io/peek_stream.zig
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub fn PeekStream(
fifo: FifoType,

pub const Error = ReaderType.Error;
pub const Reader = io.Reader(*Self, Error, read);
pub const Reader = io.Reader(*Self, Error, read, null);

const Self = @This();
const FifoType = std.fifo.LinearFifo(u8, buffer_type);
Expand Down
Loading