mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
This commit is non-breaking. std.io is deprecated in favor of std.Io, in preparation for that namespace becoming an interface.
202 lines
6.6 KiB
Zig
202 lines
6.6 KiB
Zig
const std = @import("../std.zig");
|
|
const io = std.io;
|
|
const mem = std.mem;
|
|
const assert = std.debug.assert;
|
|
const testing = std.testing;
|
|
|
|
pub fn BufferedReader(comptime buffer_size: usize, comptime ReaderType: type) type {
|
|
return struct {
|
|
unbuffered_reader: ReaderType,
|
|
buf: [buffer_size]u8 = undefined,
|
|
start: usize = 0,
|
|
end: usize = 0,
|
|
|
|
pub const Error = ReaderType.Error;
|
|
pub const Reader = io.GenericReader(*Self, Error, read);
|
|
|
|
const Self = @This();
|
|
|
|
pub fn read(self: *Self, dest: []u8) Error!usize {
|
|
// First try reading from the already buffered data onto the destination.
|
|
const current = self.buf[self.start..self.end];
|
|
if (current.len != 0) {
|
|
const to_transfer = @min(current.len, dest.len);
|
|
@memcpy(dest[0..to_transfer], current[0..to_transfer]);
|
|
self.start += to_transfer;
|
|
return to_transfer;
|
|
}
|
|
|
|
// If dest is large, read from the unbuffered reader directly into the destination.
|
|
if (dest.len >= buffer_size) {
|
|
return self.unbuffered_reader.read(dest);
|
|
}
|
|
|
|
// If dest is small, read from the unbuffered reader into our own internal buffer,
|
|
// and then transfer to destination.
|
|
self.end = try self.unbuffered_reader.read(&self.buf);
|
|
const to_transfer = @min(self.end, dest.len);
|
|
@memcpy(dest[0..to_transfer], self.buf[0..to_transfer]);
|
|
self.start = to_transfer;
|
|
return to_transfer;
|
|
}
|
|
|
|
pub fn reader(self: *Self) Reader {
|
|
return .{ .context = self };
|
|
}
|
|
};
|
|
}
|
|
|
|
pub fn bufferedReader(reader: anytype) BufferedReader(4096, @TypeOf(reader)) {
|
|
return .{ .unbuffered_reader = reader };
|
|
}
|
|
|
|
pub fn bufferedReaderSize(comptime size: usize, reader: anytype) BufferedReader(size, @TypeOf(reader)) {
|
|
return .{ .unbuffered_reader = reader };
|
|
}
|
|
|
|
test "OneByte" {
|
|
const OneByteReadReader = struct {
|
|
str: []const u8,
|
|
curr: usize,
|
|
|
|
const Error = error{NoError};
|
|
const Self = @This();
|
|
const Reader = io.GenericReader(*Self, Error, read);
|
|
|
|
fn init(str: []const u8) Self {
|
|
return Self{
|
|
.str = str,
|
|
.curr = 0,
|
|
};
|
|
}
|
|
|
|
fn read(self: *Self, dest: []u8) Error!usize {
|
|
if (self.str.len <= self.curr or dest.len == 0)
|
|
return 0;
|
|
|
|
dest[0] = self.str[self.curr];
|
|
self.curr += 1;
|
|
return 1;
|
|
}
|
|
|
|
fn reader(self: *Self) Reader {
|
|
return .{ .context = self };
|
|
}
|
|
};
|
|
|
|
const str = "This is a test";
|
|
var one_byte_stream = OneByteReadReader.init(str);
|
|
var buf_reader = bufferedReader(one_byte_stream.reader());
|
|
const stream = buf_reader.reader();
|
|
|
|
const res = try stream.readAllAlloc(testing.allocator, str.len + 1);
|
|
defer testing.allocator.free(res);
|
|
try testing.expectEqualSlices(u8, str, res);
|
|
}
|
|
|
|
fn smallBufferedReader(underlying_stream: anytype) BufferedReader(8, @TypeOf(underlying_stream)) {
|
|
return .{ .unbuffered_reader = underlying_stream };
|
|
}
|
|
test "Block" {
|
|
const BlockReader = struct {
|
|
block: []const u8,
|
|
reads_allowed: usize,
|
|
curr_read: usize,
|
|
|
|
const Error = error{NoError};
|
|
const Self = @This();
|
|
const Reader = io.GenericReader(*Self, Error, read);
|
|
|
|
fn init(block: []const u8, reads_allowed: usize) Self {
|
|
return Self{
|
|
.block = block,
|
|
.reads_allowed = reads_allowed,
|
|
.curr_read = 0,
|
|
};
|
|
}
|
|
|
|
fn read(self: *Self, dest: []u8) Error!usize {
|
|
if (self.curr_read >= self.reads_allowed) return 0;
|
|
@memcpy(dest[0..self.block.len], self.block);
|
|
|
|
self.curr_read += 1;
|
|
return self.block.len;
|
|
}
|
|
|
|
fn reader(self: *Self) Reader {
|
|
return .{ .context = self };
|
|
}
|
|
};
|
|
|
|
const block = "0123";
|
|
|
|
// len out == block
|
|
{
|
|
var test_buf_reader: BufferedReader(4, BlockReader) = .{
|
|
.unbuffered_reader = BlockReader.init(block, 2),
|
|
};
|
|
const reader = test_buf_reader.reader();
|
|
var out_buf: [4]u8 = undefined;
|
|
_ = try reader.readAll(&out_buf);
|
|
try testing.expectEqualSlices(u8, &out_buf, block);
|
|
_ = try reader.readAll(&out_buf);
|
|
try testing.expectEqualSlices(u8, &out_buf, block);
|
|
try testing.expectEqual(try reader.readAll(&out_buf), 0);
|
|
}
|
|
|
|
// len out < block
|
|
{
|
|
var test_buf_reader: BufferedReader(4, BlockReader) = .{
|
|
.unbuffered_reader = BlockReader.init(block, 2),
|
|
};
|
|
const reader = test_buf_reader.reader();
|
|
var out_buf: [3]u8 = undefined;
|
|
_ = try reader.readAll(&out_buf);
|
|
try testing.expectEqualSlices(u8, &out_buf, "012");
|
|
_ = try reader.readAll(&out_buf);
|
|
try testing.expectEqualSlices(u8, &out_buf, "301");
|
|
const n = try reader.readAll(&out_buf);
|
|
try testing.expectEqualSlices(u8, out_buf[0..n], "23");
|
|
try testing.expectEqual(try reader.readAll(&out_buf), 0);
|
|
}
|
|
|
|
// len out > block
|
|
{
|
|
var test_buf_reader: BufferedReader(4, BlockReader) = .{
|
|
.unbuffered_reader = BlockReader.init(block, 2),
|
|
};
|
|
const reader = test_buf_reader.reader();
|
|
var out_buf: [5]u8 = undefined;
|
|
_ = try reader.readAll(&out_buf);
|
|
try testing.expectEqualSlices(u8, &out_buf, "01230");
|
|
const n = try reader.readAll(&out_buf);
|
|
try testing.expectEqualSlices(u8, out_buf[0..n], "123");
|
|
try testing.expectEqual(try reader.readAll(&out_buf), 0);
|
|
}
|
|
|
|
// len out == 0
|
|
{
|
|
var test_buf_reader: BufferedReader(4, BlockReader) = .{
|
|
.unbuffered_reader = BlockReader.init(block, 2),
|
|
};
|
|
const reader = test_buf_reader.reader();
|
|
var out_buf: [0]u8 = undefined;
|
|
_ = try reader.readAll(&out_buf);
|
|
try testing.expectEqualSlices(u8, &out_buf, "");
|
|
}
|
|
|
|
// len bufreader buf > block
|
|
{
|
|
var test_buf_reader: BufferedReader(5, BlockReader) = .{
|
|
.unbuffered_reader = BlockReader.init(block, 2),
|
|
};
|
|
const reader = test_buf_reader.reader();
|
|
var out_buf: [4]u8 = undefined;
|
|
_ = try reader.readAll(&out_buf);
|
|
try testing.expectEqualSlices(u8, &out_buf, block);
|
|
_ = try reader.readAll(&out_buf);
|
|
try testing.expectEqualSlices(u8, &out_buf, block);
|
|
try testing.expectEqual(try reader.readAll(&out_buf), 0);
|
|
}
|
|
}
|