Skip to content

std: merge io.PeekStream and io.BufferedInStreamCustom #4505

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
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
127 changes: 47 additions & 80 deletions lib/std/io.zig
Original file line number Diff line number Diff line change
Expand Up @@ -109,30 +109,63 @@ pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 {
}

pub fn BufferedInStream(comptime Error: type) type {
return BufferedInStreamCustom(mem.page_size, Error);
return BufferedInStreamCustom(.{ .Static = mem.page_size }, Error);
}

pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) type {
/// Creates a readable stream which buffers data
/// It also supports 'un-reading' data, so that it can be read again.
/// This makes look-ahead style parsing much easier.
pub fn BufferedInStreamCustom(comptime buffer_type: std.fifo.LinearFifoBufferType, comptime InStreamError: type) type {
return struct {
const Self = @This();
const Stream = InStream(Error);
pub const Error = InStreamError;
pub const Stream = InStream(Error);

stream: Stream,
base: *Stream,

unbuffered_in_stream: *Stream,

const FifoType = std.fifo.LinearFifo(u8, std.fifo.LinearFifoBufferType{ .Static = buffer_size });
const FifoType = std.fifo.LinearFifo(u8, buffer_type);
fifo: FifoType,

pub fn init(unbuffered_in_stream: *Stream) Self {
return Self{
.unbuffered_in_stream = unbuffered_in_stream,
.fifo = FifoType.init(),
.stream = Stream{ .readFn = readFn },
};
pub usingnamespace switch (buffer_type) {
.Static => struct {
pub fn init(base: *Stream) Self {
return .{
.base = base,
.fifo = FifoType.init(),
.stream = Stream{ .readFn = readFn },
};
}
},
.Slice => struct {
pub fn init(base: *Stream, buf: []u8) Self {
return .{
.base = base,
.fifo = FifoType.init(buf),
.stream = Stream{ .readFn = readFn },
};
}
},
.Dynamic => struct {
pub fn init(base: *Stream, allocator: *mem.Allocator) Self {
return .{
.base = base,
.fifo = FifoType.init(allocator),
.stream = Stream{ .readFn = readFn },
};
}
},
};

pub fn putBackByte(self: *Self, byte: u8) !void {
try self.putBack(&[_]u8{byte});
}

pub fn putBack(self: *Self, bytes: []const u8) !void {
try self.fifo.unget(bytes);
}

fn readFn(in_stream: *Stream, dest: []u8) !usize {
fn readFn(in_stream: *Stream, dest: []u8) Error!usize {
const self = @fieldParentPtr(Self, "stream", in_stream);
var dest_index: usize = 0;
while (dest_index < dest.len) {
Expand All @@ -141,7 +174,7 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type)
// fifo empty, fill it
const writable = self.fifo.writableSlice(0);
assert(writable.len > 0);
const n = try self.unbuffered_in_stream.read(writable);
const n = try self.base.read(writable);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add a test case to catch the bug here, where it does not leave any reserved buffer space, and putBack would fail here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand the issue. Which bug are you referring to?

if (n == 0) {
// reading from the unbuffered stream returned nothing
// so we have nothing left to read.
Expand Down Expand Up @@ -194,72 +227,6 @@ test "io.BufferedInStream" {
testing.expectEqualSlices(u8, str, res);
}

/// Creates a stream which supports 'un-reading' data, so that it can be read again.
/// This makes look-ahead style parsing much easier.
pub fn PeekStream(comptime buffer_type: std.fifo.LinearFifoBufferType, comptime InStreamError: type) type {
return struct {
const Self = @This();
pub const Error = InStreamError;
pub const Stream = InStream(Error);

stream: Stream,
base: *Stream,

const FifoType = std.fifo.LinearFifo(u8, buffer_type);
fifo: FifoType,

pub usingnamespace switch (buffer_type) {
.Static => struct {
pub fn init(base: *Stream) Self {
return .{
.base = base,
.fifo = FifoType.init(),
.stream = Stream{ .readFn = readFn },
};
}
},
.Slice => struct {
pub fn init(base: *Stream, buf: []u8) Self {
return .{
.base = base,
.fifo = FifoType.init(buf),
.stream = Stream{ .readFn = readFn },
};
}
},
.Dynamic => struct {
pub fn init(base: *Stream, allocator: *mem.Allocator) Self {
return .{
.base = base,
.fifo = FifoType.init(allocator),
.stream = Stream{ .readFn = readFn },
};
}
},
};

pub fn putBackByte(self: *Self, byte: u8) !void {
try self.putBack(&[_]u8{byte});
}

pub fn putBack(self: *Self, bytes: []const u8) !void {
try self.fifo.unget(bytes);
}

fn readFn(in_stream: *Stream, dest: []u8) Error!usize {
const self = @fieldParentPtr(Self, "stream", in_stream);

// copy over anything putBack()'d
var dest_index = self.fifo.read(dest);
if (dest_index == dest.len) return dest_index;

// ask the backing stream for more
dest_index += try self.base.read(dest[dest_index..]);
return dest_index;
}
};
}

pub const SliceInStream = struct {
const Self = @This();
pub const Error = error{};
Expand Down
4 changes: 2 additions & 2 deletions lib/std/io/test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,10 @@ test "SliceInStream" {
expect(read == 0);
}

test "PeekStream" {
test "BufferedInStream putBack" {
const bytes = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
var ss = io.SliceInStream.init(&bytes);
var ps = io.PeekStream(.{ .Static = 2 }, io.SliceInStream.Error).init(&ss.stream);
var ps = io.BufferedInStreamCustom(.{ .Static = 2 }, io.SliceInStream.Error).init(&ss.stream);

var dest: [4]u8 = undefined;

Expand Down