Improve efficiency of buffered_reader. (#21256)

The previous implementation of buffered_reader always reads from the
unbuffered reader into the internal buffer, and then dumps the data onto
the destination. This is inefficient, as sometimes it's possible to read
directly into the destination. The previous strategy generates more
memory copies and unbuffered reads than necessary.
This commit is contained in:
Lucas Santos
2024-09-23 21:20:27 -03:00
committed by GitHub
parent d1901c744c
commit b19d0fb0fd

View File

@@ -17,26 +17,27 @@ pub fn BufferedReader(comptime buffer_size: usize, comptime ReaderType: type) ty
const Self = @This();
pub fn read(self: *Self, dest: []u8) Error!usize {
var dest_index: usize = 0;
while (dest_index < dest.len) {
const written = @min(dest.len - dest_index, self.end - self.start);
@memcpy(dest[dest_index..][0..written], self.buf[self.start..][0..written]);
if (written == 0) {
// buf empty, fill it
const n = try self.unbuffered_reader.read(self.buf[0..]);
if (n == 0) {
// reading from the unbuffered stream returned nothing
// so we have nothing left to read.
return dest_index;
}
self.start = 0;
self.end = n;
}
self.start += written;
dest_index += written;
// 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;
}
return dest.len;
// 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 {
@@ -134,12 +135,13 @@ test "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 test_buf_reader.read(&out_buf);
_ = try reader.readAll(&out_buf);
try testing.expectEqualSlices(u8, &out_buf, block);
_ = try test_buf_reader.read(&out_buf);
_ = try reader.readAll(&out_buf);
try testing.expectEqualSlices(u8, &out_buf, block);
try testing.expectEqual(try test_buf_reader.read(&out_buf), 0);
try testing.expectEqual(try reader.readAll(&out_buf), 0);
}
// len out < block
@@ -147,14 +149,15 @@ test "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 test_buf_reader.read(&out_buf);
_ = try reader.readAll(&out_buf);
try testing.expectEqualSlices(u8, &out_buf, "012");
_ = try test_buf_reader.read(&out_buf);
_ = try reader.readAll(&out_buf);
try testing.expectEqualSlices(u8, &out_buf, "301");
const n = try test_buf_reader.read(&out_buf);
const n = try reader.readAll(&out_buf);
try testing.expectEqualSlices(u8, out_buf[0..n], "23");
try testing.expectEqual(try test_buf_reader.read(&out_buf), 0);
try testing.expectEqual(try reader.readAll(&out_buf), 0);
}
// len out > block
@@ -162,12 +165,13 @@ test "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 test_buf_reader.read(&out_buf);
_ = try reader.readAll(&out_buf);
try testing.expectEqualSlices(u8, &out_buf, "01230");
const n = try test_buf_reader.read(&out_buf);
const n = try reader.readAll(&out_buf);
try testing.expectEqualSlices(u8, out_buf[0..n], "123");
try testing.expectEqual(try test_buf_reader.read(&out_buf), 0);
try testing.expectEqual(try reader.readAll(&out_buf), 0);
}
// len out == 0
@@ -175,8 +179,9 @@ test "Block" {
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 test_buf_reader.read(&out_buf);
_ = try reader.readAll(&out_buf);
try testing.expectEqualSlices(u8, &out_buf, "");
}
@@ -185,11 +190,12 @@ test "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 test_buf_reader.read(&out_buf);
_ = try reader.readAll(&out_buf);
try testing.expectEqualSlices(u8, &out_buf, block);
_ = try test_buf_reader.read(&out_buf);
_ = try reader.readAll(&out_buf);
try testing.expectEqualSlices(u8, &out_buf, block);
try testing.expectEqual(try test_buf_reader.read(&out_buf), 0);
try testing.expectEqual(try reader.readAll(&out_buf), 0);
}
}