authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-16 02:54:45+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-07-16 02:54:45+02:00
logdbe0e0c1bc13f5bbb31b043174d278291df31f34
tree18526efea40278986fa2b3ddcd6384a961d22d3e
parent94e0c85b6bb253e6d47c964b7a9a389c547bb2ec
parent6d39c295646ac779e2a2beff43a5dd8bf42eb1db
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #24464 from ziglang/fixes

std.Io fixes, docs, and tests

2 files changed, 59 insertions(+), 4 deletions(-)

lib/std/Io/Reader.zig+14
......@@ -1303,6 +1303,13 @@ fn takeMultipleOf7Leb128(r: *Reader, comptime Result: type) TakeLeb128Error!Resu
13031303}
13041304
13051305/// Left-aligns data such that `r.seek` becomes zero.
1306///
1307/// If `r.seek` is not already zero then `buffer` is mutated, making it illegal
1308/// to call this function with a const-casted `buffer`, such as in the case of
1309/// `fixed`. This issue can be avoided:
1310/// * in implementations, by attempting a read before a rebase, in which
1311/// case the read will return `error.EndOfStream`, preventing the rebase.
1312/// * in usage, by copying into a mutable buffer before initializing `fixed`.
13061313pub fn rebase(r: *Reader) void {
13071314 if (r.seek == 0) return;
13081315 const data = r.buffer[r.seek..r.end];
......@@ -1315,6 +1322,13 @@ pub fn rebase(r: *Reader) void {
13151322/// if necessary.
13161323///
13171324/// Asserts `capacity` is within the buffer capacity.
1325///
1326/// If the rebase occurs then `buffer` is mutated, making it illegal to call
1327/// this function with a const-casted `buffer`, such as in the case of `fixed`.
1328/// This issue can be avoided:
1329/// * in implementations, by attempting a read before a rebase, in which
1330/// case the read will return `error.EndOfStream`, preventing the rebase.
1331/// * in usage, by copying into a mutable buffer before initializing `fixed`.
13181332pub fn rebaseCapacity(r: *Reader, capacity: usize) void {
13191333 if (r.end > r.buffer.len - capacity) rebase(r);
13201334}
lib/std/Io/Writer.zig+45-4
......@@ -2194,8 +2194,10 @@ pub const Discarding = struct {
21942194 const d: *Discarding = @alignCast(@fieldParentPtr("writer", w));
21952195 d.count += w.end;
21962196 w.end = 0;
2197 if (limit == .nothing) return 0;
21972198 if (file_reader.getSize()) |size| {
21982199 const n = limit.minInt64(size - file_reader.pos);
2200 if (n == 0) return error.EndOfStream;
21992201 file_reader.seekBy(@intCast(n)) catch return error.Unimplemented;
22002202 w.end = 0;
22012203 d.count += n;
......@@ -2489,18 +2491,17 @@ pub const Allocating = struct {
24892491
24902492 fn sendFile(w: *Writer, file_reader: *File.Reader, limit: std.io.Limit) FileError!usize {
24912493 if (File.Handle == void) return error.Unimplemented;
2494 if (limit == .nothing) return 0;
24922495 const a: *Allocating = @fieldParentPtr("writer", w);
24932496 const gpa = a.allocator;
24942497 var list = a.toArrayList();
24952498 defer setArrayList(a, list);
24962499 const pos = file_reader.pos;
24972500 const additional = if (file_reader.getSize()) |size| size - pos else |_| std.atomic.cache_line;
2501 if (additional == 0) return error.EndOfStream;
24982502 list.ensureUnusedCapacity(gpa, limit.minInt64(additional)) catch return error.WriteFailed;
24992503 const dest = limit.slice(list.unusedCapacitySlice());
2500 const n = file_reader.read(dest) catch |err| switch (err) {
2501 error.ReadFailed => return error.ReadFailed,
2502 error.EndOfStream => 0,
2503 };
2504 const n = try file_reader.read(dest);
25042505 list.items.len += n;
25052506 return n;
25062507 }
......@@ -2522,3 +2523,43 @@ pub const Allocating = struct {
25222523 try testing.expectEqualSlices(u8, "x: 42\ny: 1234\n", a.getWritten());
25232524 }
25242525};
2526
2527test "discarding sendFile" {
2528 var tmp_dir = testing.tmpDir(.{});
2529 defer tmp_dir.cleanup();
2530
2531 const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true });
2532 defer file.close();
2533 var r_buffer: [256]u8 = undefined;
2534 var file_writer: std.fs.File.Writer = .init(file, &r_buffer);
2535 try file_writer.interface.writeByte('h');
2536 try file_writer.interface.flush();
2537
2538 var file_reader = file_writer.moveToReader();
2539 try file_reader.seekTo(0);
2540
2541 var w_buffer: [256]u8 = undefined;
2542 var discarding: std.io.Writer.Discarding = .init(&w_buffer);
2543
2544 _ = try file_reader.interface.streamRemaining(&discarding.writer);
2545}
2546
2547test "allocating sendFile" {
2548 var tmp_dir = testing.tmpDir(.{});
2549 defer tmp_dir.cleanup();
2550
2551 const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true });
2552 defer file.close();
2553 var r_buffer: [256]u8 = undefined;
2554 var file_writer: std.fs.File.Writer = .init(file, &r_buffer);
2555 try file_writer.interface.writeByte('h');
2556 try file_writer.interface.flush();
2557
2558 var file_reader = file_writer.moveToReader();
2559 try file_reader.seekTo(0);
2560
2561 var allocating: std.io.Writer.Allocating = .init(std.testing.allocator);
2562 defer allocating.deinit();
2563
2564 _ = try file_reader.interface.streamRemaining(&allocating.writer);
2565}