diff --git a/lib/std/Io/Reader.zig b/lib/std/Io/Reader.zig index 1bf9e9bde3..e569c36773 100644 --- a/lib/std/Io/Reader.zig +++ b/lib/std/Io/Reader.zig @@ -1303,6 +1303,13 @@ fn takeMultipleOf7Leb128(r: *Reader, comptime Result: type) TakeLeb128Error!Resu } /// Left-aligns data such that `r.seek` becomes zero. +/// +/// If `r.seek` is not already zero then `buffer` is mutated, making it illegal +/// to call this function with a const-casted `buffer`, such as in the case of +/// `fixed`. This issue can be avoided: +/// * in implementations, by attempting a read before a rebase, in which +/// case the read will return `error.EndOfStream`, preventing the rebase. +/// * in usage, by copying into a mutable buffer before initializing `fixed`. pub fn rebase(r: *Reader) void { if (r.seek == 0) return; const data = r.buffer[r.seek..r.end]; @@ -1315,6 +1322,13 @@ pub fn rebase(r: *Reader) void { /// if necessary. /// /// Asserts `capacity` is within the buffer capacity. +/// +/// If the rebase occurs then `buffer` is mutated, making it illegal to call +/// this function with a const-casted `buffer`, such as in the case of `fixed`. +/// This issue can be avoided: +/// * in implementations, by attempting a read before a rebase, in which +/// case the read will return `error.EndOfStream`, preventing the rebase. +/// * in usage, by copying into a mutable buffer before initializing `fixed`. pub fn rebaseCapacity(r: *Reader, capacity: usize) void { if (r.end > r.buffer.len - capacity) rebase(r); } diff --git a/lib/std/Io/Writer.zig b/lib/std/Io/Writer.zig index 916dfbe798..036c35b9e4 100644 --- a/lib/std/Io/Writer.zig +++ b/lib/std/Io/Writer.zig @@ -2194,8 +2194,10 @@ pub const Discarding = struct { const d: *Discarding = @alignCast(@fieldParentPtr("writer", w)); d.count += w.end; w.end = 0; + if (limit == .nothing) return 0; if (file_reader.getSize()) |size| { const n = limit.minInt64(size - file_reader.pos); + if (n == 0) return error.EndOfStream; file_reader.seekBy(@intCast(n)) catch return error.Unimplemented; w.end = 0; d.count += n; @@ -2489,18 +2491,17 @@ pub const Allocating = struct { fn sendFile(w: *Writer, file_reader: *File.Reader, limit: std.io.Limit) FileError!usize { if (File.Handle == void) return error.Unimplemented; + if (limit == .nothing) return 0; const a: *Allocating = @fieldParentPtr("writer", w); const gpa = a.allocator; var list = a.toArrayList(); defer setArrayList(a, list); const pos = file_reader.pos; const additional = if (file_reader.getSize()) |size| size - pos else |_| std.atomic.cache_line; + if (additional == 0) return error.EndOfStream; list.ensureUnusedCapacity(gpa, limit.minInt64(additional)) catch return error.WriteFailed; const dest = limit.slice(list.unusedCapacitySlice()); - const n = file_reader.read(dest) catch |err| switch (err) { - error.ReadFailed => return error.ReadFailed, - error.EndOfStream => 0, - }; + const n = try file_reader.read(dest); list.items.len += n; return n; } @@ -2522,3 +2523,43 @@ pub const Allocating = struct { try testing.expectEqualSlices(u8, "x: 42\ny: 1234\n", a.getWritten()); } }; + +test "discarding sendFile" { + var tmp_dir = testing.tmpDir(.{}); + defer tmp_dir.cleanup(); + + const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true }); + defer file.close(); + var r_buffer: [256]u8 = undefined; + var file_writer: std.fs.File.Writer = .init(file, &r_buffer); + try file_writer.interface.writeByte('h'); + try file_writer.interface.flush(); + + var file_reader = file_writer.moveToReader(); + try file_reader.seekTo(0); + + var w_buffer: [256]u8 = undefined; + var discarding: std.io.Writer.Discarding = .init(&w_buffer); + + _ = try file_reader.interface.streamRemaining(&discarding.writer); +} + +test "allocating sendFile" { + var tmp_dir = testing.tmpDir(.{}); + defer tmp_dir.cleanup(); + + const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true }); + defer file.close(); + var r_buffer: [256]u8 = undefined; + var file_writer: std.fs.File.Writer = .init(file, &r_buffer); + try file_writer.interface.writeByte('h'); + try file_writer.interface.flush(); + + var file_reader = file_writer.moveToReader(); + try file_reader.seekTo(0); + + var allocating: std.io.Writer.Allocating = .init(std.testing.allocator); + defer allocating.deinit(); + + _ = try file_reader.interface.streamRemaining(&allocating.writer); +}