Skip to content

Commit a8dc32e

Browse files
authored
Merge pull request #24493 from ziglang/readSliceShort
std.Io.Reader: fix readSliceShort with smaller buffer than Reader
2 parents b7d7446 + 155ab56 commit a8dc32e

File tree

3 files changed

+43
-66
lines changed

3 files changed

+43
-66
lines changed

lib/std/Io/Reader.zig

Lines changed: 35 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -593,48 +593,29 @@ pub fn readSliceAll(r: *Reader, buffer: []u8) Error!void {
593593
/// See also:
594594
/// * `readSliceAll`
595595
pub fn readSliceShort(r: *Reader, buffer: []u8) ShortError!usize {
596-
const in_buffer = r.buffer[r.seek..r.end];
597-
const copy_len = @min(buffer.len, in_buffer.len);
598-
@memcpy(buffer[0..copy_len], in_buffer[0..copy_len]);
599-
if (buffer.len - copy_len == 0) {
600-
r.seek += copy_len;
601-
return buffer.len;
602-
}
603-
var i: usize = copy_len;
604-
r.end = 0;
605-
r.seek = 0;
596+
var i: usize = 0;
606597
while (true) {
598+
const buffer_contents = r.buffer[r.seek..r.end];
599+
const dest = buffer[i..];
600+
const copy_len = @min(dest.len, buffer_contents.len);
601+
@memcpy(dest[0..copy_len], buffer_contents[0..copy_len]);
602+
if (dest.len - copy_len == 0) {
603+
@branchHint(.likely);
604+
r.seek += copy_len;
605+
return buffer.len;
606+
}
607+
i += copy_len;
608+
r.end = 0;
609+
r.seek = 0;
607610
const remaining = buffer[i..];
608-
var wrapper: Writer.VectorWrapper = .{
609-
.it = .{
610-
.first = remaining,
611-
.last = r.buffer,
612-
},
613-
.writer = .{
614-
.buffer = if (remaining.len >= r.buffer.len) remaining else r.buffer,
615-
.vtable = Writer.VectorWrapper.vtable,
616-
},
617-
};
618-
const n = r.vtable.stream(r, &wrapper.writer, .unlimited) catch |err| switch (err) {
619-
error.WriteFailed => {
620-
if (!wrapper.used) {
621-
assert(r.seek == 0);
622-
r.seek = remaining.len;
623-
r.end = wrapper.writer.end;
624-
@memcpy(remaining, r.buffer[0..remaining.len]);
625-
}
626-
return buffer.len;
627-
},
611+
const new_remaining_len = readVecInner(r, &.{}, remaining, remaining.len) catch |err| switch (err) {
628612
error.EndOfStream => return i,
629613
error.ReadFailed => return error.ReadFailed,
630614
};
631-
if (n < remaining.len) {
632-
i += n;
633-
continue;
634-
}
635-
r.end = n - remaining.len;
636-
return buffer.len;
615+
if (new_remaining_len == 0) return buffer.len;
616+
i += remaining.len - new_remaining_len;
637617
}
618+
return buffer.len;
638619
}
639620

640621
/// Fill `buffer` with the next `buffer.len` bytes from the stream, advancing
@@ -1640,6 +1621,19 @@ test readSliceShort {
16401621
try testing.expectEqual(0, try r.readSliceShort(&buf));
16411622
}
16421623

1624+
test "readSliceShort with smaller buffer than Reader" {
1625+
var reader_buf: [15]u8 = undefined;
1626+
const str = "This is a test";
1627+
var one_byte_stream: testing.Reader = .init(&reader_buf, &.{
1628+
.{ .buffer = str },
1629+
});
1630+
one_byte_stream.artificial_limit = .limited(1);
1631+
1632+
var buf: [14]u8 = undefined;
1633+
try testing.expectEqual(14, try one_byte_stream.interface.readSliceShort(&buf));
1634+
try testing.expectEqualStrings(str, &buf);
1635+
}
1636+
16431637
test readVec {
16441638
var r: Reader = .fixed(std.ascii.letters);
16451639
var flat_buffer: [52]u8 = undefined;
@@ -1702,33 +1696,13 @@ fn failingDiscard(r: *Reader, limit: Limit) Error!usize {
17021696
}
17031697

17041698
test "readAlloc when the backing reader provides one byte at a time" {
1705-
const OneByteReader = struct {
1706-
str: []const u8,
1707-
i: usize,
1708-
reader: Reader,
1709-
1710-
fn stream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
1711-
assert(@intFromEnum(limit) >= 1);
1712-
const self: *@This() = @fieldParentPtr("reader", r);
1713-
if (self.str.len - self.i == 0) return error.EndOfStream;
1714-
try w.writeByte(self.str[self.i]);
1715-
self.i += 1;
1716-
return 1;
1717-
}
1718-
};
17191699
const str = "This is a test";
17201700
var tiny_buffer: [1]u8 = undefined;
1721-
var one_byte_stream: OneByteReader = .{
1722-
.str = str,
1723-
.i = 0,
1724-
.reader = .{
1725-
.buffer = &tiny_buffer,
1726-
.vtable = &.{ .stream = OneByteReader.stream },
1727-
.seek = 0,
1728-
.end = 0,
1729-
},
1730-
};
1731-
const res = try one_byte_stream.reader.allocRemaining(std.testing.allocator, .unlimited);
1701+
var one_byte_stream: testing.Reader = .init(&tiny_buffer, &.{
1702+
.{ .buffer = str },
1703+
});
1704+
one_byte_stream.artificial_limit = .limited(1);
1705+
const res = try one_byte_stream.interface.allocRemaining(std.testing.allocator, .unlimited);
17321706
defer std.testing.allocator.free(res);
17331707
try std.testing.expectEqualStrings(str, res);
17341708
}

lib/std/testing.zig

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,12 +1207,14 @@ pub inline fn fuzz(
12071207
return @import("root").fuzz(context, testOne, options);
12081208
}
12091209

1210-
/// A `std.io.Reader` that writes a predetermined list of buffers during `stream`.
1210+
/// A `std.Io.Reader` that writes a predetermined list of buffers during `stream`.
12111211
pub const Reader = struct {
12121212
calls: []const Call,
1213-
interface: std.io.Reader,
1213+
interface: std.Io.Reader,
12141214
next_call_index: usize,
12151215
next_offset: usize,
1216+
/// Further reduces how many bytes are written in each `stream` call.
1217+
artificial_limit: std.Io.Limit = .unlimited,
12161218

12171219
pub const Call = struct {
12181220
buffer: []const u8,
@@ -1232,11 +1234,11 @@ pub const Reader = struct {
12321234
};
12331235
}
12341236

1235-
fn stream(io_r: *std.io.Reader, w: *std.io.Writer, limit: std.io.Limit) std.io.Reader.StreamError!usize {
1237+
fn stream(io_r: *std.Io.Reader, w: *std.Io.Writer, limit: std.Io.Limit) std.Io.Reader.StreamError!usize {
12361238
const r: *Reader = @alignCast(@fieldParentPtr("interface", io_r));
12371239
if (r.calls.len - r.next_call_index == 0) return error.EndOfStream;
12381240
const call = r.calls[r.next_call_index];
1239-
const buffer = limit.sliceConst(call.buffer[r.next_offset..]);
1241+
const buffer = r.artificial_limit.sliceConst(limit.sliceConst(call.buffer[r.next_offset..]));
12401242
const n = try w.write(buffer);
12411243
r.next_offset += n;
12421244
if (call.buffer.len - r.next_offset == 0) {

lib/std/zig.zig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,8 @@ pub fn readSourceFileToEndAlloc(gpa: Allocator, file_reader: *std.fs.File.Reader
536536

537537
if (file_reader.getSize()) |size| {
538538
const casted_size = std.math.cast(u32, size) orelse return error.StreamTooLong;
539-
try buffer.ensureTotalCapacityPrecise(gpa, casted_size);
539+
// +1 to avoid resizing for the null byte added in toOwnedSliceSentinel below.
540+
try buffer.ensureTotalCapacityPrecise(gpa, casted_size + 1);
540541
} else |_| {}
541542

542543
try file_reader.interface.appendRemaining(gpa, .@"2", &buffer, .limited(max_src_size));

0 commit comments

Comments
 (0)