authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-06-30 12:17:47-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-07 22:43:51-07:00
log435ccf706d09e269db08e070b9fa957e7b40f30c
tree829f6c0d7a79150fb4ad9dd9cff5e122546ac52b
parent77e839e28326d635e6a00513e26bfaf0076f22ec

std.fs.File.Writer: fix drain implementation

it didn't account for data.len can no longer be zero

1 files changed, 36 insertions(+), 37 deletions(-)

lib/std/fs/File.zig+36-37
......@@ -1648,29 +1648,28 @@ pub const Writer = struct {
16481648 };
16491649 }
16501650
1651 pub fn drain(io_writer: *std.io.Writer, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {
1652 const w: *Writer = @fieldParentPtr("interface", io_writer);
1651 pub fn drain(io_w: *std.io.Writer, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {
1652 const w: *Writer = @fieldParentPtr("interface", io_w);
16531653 const handle = w.file.handle;
1654 const buffered = io_writer.buffered();
1655 var splat_buffer: [256]u8 = undefined;
1654 const buffered = io_w.buffered();
16561655 if (is_windows) {
16571656 var i: usize = 0;
16581657 while (i < buffered.len) {
16591658 const n = windows.WriteFile(handle, buffered[i..], null) catch |err| {
16601659 w.err = err;
16611660 w.pos += i;
1662 _ = io_writer.consume(i);
1661 _ = io_w.consume(i);
16631662 return error.WriteFailed;
16641663 };
16651664 i += n;
16661665 if (data.len > 0 and buffered.len - i < n) {
16671666 w.pos += i;
1668 return io_writer.consume(i);
1667 return io_w.consume(i);
16691668 }
16701669 }
16711670 if (i != 0 or data.len == 0 or (data.len == 1 and splat == 0)) {
16721671 w.pos += i;
1673 return io_writer.consume(i);
1672 return io_w.consume(i);
16741673 }
16751674 const n = windows.WriteFile(handle, data[0], null) catch |err| {
16761675 w.err = err;
......@@ -1679,19 +1678,6 @@ pub const Writer = struct {
16791678 w.pos += n;
16801679 return n;
16811680 }
1682 if (data.len == 0) {
1683 var i: usize = 0;
1684 while (i < buffered.len) {
1685 i += std.posix.write(handle, buffered) catch |err| {
1686 w.err = err;
1687 w.pos += i;
1688 _ = io_writer.consume(i);
1689 return error.WriteFailed;
1690 };
1691 }
1692 w.pos += i;
1693 return io_writer.consumeAll();
1694 }
16951681 var iovecs: [max_buffers_len]std.posix.iovec_const = undefined;
16961682 var len: usize = 0;
16971683 if (buffered.len > 0) {
......@@ -1700,30 +1686,43 @@ pub const Writer = struct {
17001686 }
17011687 for (data) |d| {
17021688 if (d.len == 0) continue;
1703 if (iovecs.len - len == 0) break;
17041689 iovecs[len] = .{ .base = d.ptr, .len = d.len };
17051690 len += 1;
1691 if (iovecs.len - len == 0) break;
17061692 }
1693 if (len == 0) return 0;
1694 const pattern = data[data.len - 1];
17071695 switch (splat) {
1708 0 => if (data[data.len - 1].len != 0) {
1696 0 => if (iovecs[len - 1].base == pattern.ptr) {
17091697 len -= 1;
17101698 },
17111699 1 => {},
1712 else => switch (data[data.len - 1].len) {
1700 else => switch (pattern.len) {
17131701 0 => {},
1714 1 => {
1702 1 => memset: {
1703 // Replace the 1-byte buffer with a bigger one.
1704 if (iovecs[len - 1].base == pattern.ptr) len -= 1;
1705 if (iovecs.len - len == 0) break :memset;
1706 const splat_buffer_candidate = io_w.buffer[io_w.end..];
1707 var backup_buffer: [64]u8 = undefined;
1708 const splat_buffer = if (splat_buffer_candidate.len >= backup_buffer.len)
1709 splat_buffer_candidate
1710 else
1711 &backup_buffer;
17151712 const memset_len = @min(splat_buffer.len, splat);
17161713 const buf = splat_buffer[0..memset_len];
1717 @memset(buf, data[data.len - 1][0]);
1718 iovecs[len - 1] = .{ .base = buf.ptr, .len = buf.len };
1714 @memset(buf, pattern[0]);
1715 iovecs[len] = .{ .base = buf.ptr, .len = buf.len };
1716 len += 1;
17191717 var remaining_splat = splat - buf.len;
1720 while (remaining_splat > splat_buffer.len and len < iovecs.len) {
1721 iovecs[len] = .{ .base = &splat_buffer, .len = splat_buffer.len };
1722 remaining_splat -= splat_buffer.len;
1718 while (remaining_splat > splat_buffer.len and iovecs.len - len != 0) {
1719 assert(buf.len == splat_buffer.len);
1720 iovecs[len] = .{ .base = splat_buffer.ptr, .len = splat_buffer.len };
17231721 len += 1;
1722 remaining_splat -= splat_buffer.len;
17241723 }
1725 if (remaining_splat > 0 and len < iovecs.len) {
1726 iovecs[len] = .{ .base = &splat_buffer, .len = remaining_splat };
1724 if (remaining_splat > 0 and iovecs.len - len != 0) {
1725 iovecs[len] = .{ .base = splat_buffer.ptr, .len = remaining_splat };
17271726 len += 1;
17281727 }
17291728 return std.posix.writev(handle, iovecs[0..len]) catch |err| {
......@@ -1733,7 +1732,7 @@ pub const Writer = struct {
17331732 },
17341733 else => for (0..splat - 1) |_| {
17351734 if (iovecs.len - len == 0) break;
1736 iovecs[len] = .{ .base = data[data.len - 1].ptr, .len = data[data.len - 1].len };
1735 iovecs[len] = .{ .base = pattern.ptr, .len = pattern.len };
17371736 len += 1;
17381737 },
17391738 },
......@@ -1743,15 +1742,15 @@ pub const Writer = struct {
17431742 return error.WriteFailed;
17441743 };
17451744 w.pos += n;
1746 return io_writer.consume(n);
1745 return io_w.consume(n);
17471746 }
17481747
17491748 pub fn sendFile(
1750 io_writer: *std.io.Writer,
1749 io_w: *std.io.Writer,
17511750 file_reader: *Reader,
17521751 limit: std.io.Limit,
17531752 ) std.io.Writer.FileError!usize {
1754 const w: *Writer = @fieldParentPtr("interface", io_writer);
1753 const w: *Writer = @fieldParentPtr("interface", io_w);
17551754 const out_fd = w.file.handle;
17561755 const in_fd = file_reader.file.handle;
17571756 // TODO try using copy_file_range on FreeBSD
......@@ -1762,7 +1761,7 @@ pub const Writer = struct {
17621761 if (w.sendfile_err != null) break :sf;
17631762 // Linux sendfile does not support headers.
17641763 const buffered = limit.slice(file_reader.interface.buffer);
1765 if (io_writer.end != 0 or buffered.len != 0) return drain(io_writer, &.{buffered}, 1);
1764 if (io_w.end != 0 or buffered.len != 0) return drain(io_w, &.{buffered}, 1);
17661765 const max_count = 0x7ffff000; // Avoid EINVAL.
17671766 var off: std.os.linux.off_t = undefined;
17681767 const off_ptr: ?*std.os.linux.off_t, const count: usize = switch (file_reader.mode) {
......@@ -1813,7 +1812,7 @@ pub const Writer = struct {
18131812 if (copy_file_range_fn) |copy_file_range| cfr: {
18141813 if (w.copy_file_range_err != null) break :cfr;
18151814 const buffered = limit.slice(file_reader.interface.buffer);
1816 if (io_writer.end != 0 or buffered.len != 0) return drain(io_writer, &.{buffered}, 1);
1815 if (io_w.end != 0 or buffered.len != 0) return drain(io_w, &.{buffered}, 1);
18171816 var off_in: i64 = undefined;
18181817 var off_out: i64 = undefined;
18191818 const off_in_ptr: ?*i64 = switch (file_reader.mode) {