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 {...@@ -1648,29 +1648,28 @@ pub const Writer = struct {
1648 };1648 };
1649 }1649 }
16501650
1651 pub fn drain(io_writer: *std.io.Writer, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {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_writer);1652 const w: *Writer = @fieldParentPtr("interface", io_w);
1653 const handle = w.file.handle;1653 const handle = w.file.handle;
1654 const buffered = io_writer.buffered();1654 const buffered = io_w.buffered();
1655 var splat_buffer: [256]u8 = undefined;
1656 if (is_windows) {1655 if (is_windows) {
1657 var i: usize = 0;1656 var i: usize = 0;
1658 while (i < buffered.len) {1657 while (i < buffered.len) {
1659 const n = windows.WriteFile(handle, buffered[i..], null) catch |err| {1658 const n = windows.WriteFile(handle, buffered[i..], null) catch |err| {
1660 w.err = err;1659 w.err = err;
1661 w.pos += i;1660 w.pos += i;
1662 _ = io_writer.consume(i);1661 _ = io_w.consume(i);
1663 return error.WriteFailed;1662 return error.WriteFailed;
1664 };1663 };
1665 i += n;1664 i += n;
1666 if (data.len > 0 and buffered.len - i < n) {1665 if (data.len > 0 and buffered.len - i < n) {
1667 w.pos += i;1666 w.pos += i;
1668 return io_writer.consume(i);1667 return io_w.consume(i);
1669 }1668 }
1670 }1669 }
1671 if (i != 0 or data.len == 0 or (data.len == 1 and splat == 0)) {1670 if (i != 0 or data.len == 0 or (data.len == 1 and splat == 0)) {
1672 w.pos += i;1671 w.pos += i;
1673 return io_writer.consume(i);1672 return io_w.consume(i);
1674 }1673 }
1675 const n = windows.WriteFile(handle, data[0], null) catch |err| {1674 const n = windows.WriteFile(handle, data[0], null) catch |err| {
1676 w.err = err;1675 w.err = err;
...@@ -1679,19 +1678,6 @@ pub const Writer = struct {...@@ -1679,19 +1678,6 @@ pub const Writer = struct {
1679 w.pos += n;1678 w.pos += n;
1680 return n;1679 return n;
1681 }1680 }
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 }
1695 var iovecs: [max_buffers_len]std.posix.iovec_const = undefined;1681 var iovecs: [max_buffers_len]std.posix.iovec_const = undefined;
1696 var len: usize = 0;1682 var len: usize = 0;
1697 if (buffered.len > 0) {1683 if (buffered.len > 0) {
...@@ -1700,30 +1686,43 @@ pub const Writer = struct {...@@ -1700,30 +1686,43 @@ pub const Writer = struct {
1700 }1686 }
1701 for (data) |d| {1687 for (data) |d| {
1702 if (d.len == 0) continue;1688 if (d.len == 0) continue;
1703 if (iovecs.len - len == 0) break;
1704 iovecs[len] = .{ .base = d.ptr, .len = d.len };1689 iovecs[len] = .{ .base = d.ptr, .len = d.len };
1705 len += 1;1690 len += 1;
1691 if (iovecs.len - len == 0) break;
1706 }1692 }
1693 if (len == 0) return 0;
1694 const pattern = data[data.len - 1];
1707 switch (splat) {1695 switch (splat) {
1708 0 => if (data[data.len - 1].len != 0) {1696 0 => if (iovecs[len - 1].base == pattern.ptr) {
1709 len -= 1;1697 len -= 1;
1710 },1698 },
1711 1 => {},1699 1 => {},
1712 else => switch (data[data.len - 1].len) {1700 else => switch (pattern.len) {
1713 0 => {},1701 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;
1715 const memset_len = @min(splat_buffer.len, splat);1712 const memset_len = @min(splat_buffer.len, splat);
1716 const buf = splat_buffer[0..memset_len];1713 const buf = splat_buffer[0..memset_len];
1717 @memset(buf, data[data.len - 1][0]);1714 @memset(buf, pattern[0]);
1718 iovecs[len - 1] = .{ .base = buf.ptr, .len = buf.len };1715 iovecs[len] = .{ .base = buf.ptr, .len = buf.len };
1716 len += 1;
1719 var remaining_splat = splat - buf.len;1717 var remaining_splat = splat - buf.len;
1720 while (remaining_splat > splat_buffer.len and len < iovecs.len) {1718 while (remaining_splat > splat_buffer.len and iovecs.len - len != 0) {
1721 iovecs[len] = .{ .base = &splat_buffer, .len = splat_buffer.len };1719 assert(buf.len == splat_buffer.len);
1722 remaining_splat -= splat_buffer.len;1720 iovecs[len] = .{ .base = splat_buffer.ptr, .len = splat_buffer.len };
1723 len += 1;1721 len += 1;
1722 remaining_splat -= splat_buffer.len;
1724 }1723 }
1725 if (remaining_splat > 0 and len < iovecs.len) {1724 if (remaining_splat > 0 and iovecs.len - len != 0) {
1726 iovecs[len] = .{ .base = &splat_buffer, .len = remaining_splat };1725 iovecs[len] = .{ .base = splat_buffer.ptr, .len = remaining_splat };
1727 len += 1;1726 len += 1;
1728 }1727 }
1729 return std.posix.writev(handle, iovecs[0..len]) catch |err| {1728 return std.posix.writev(handle, iovecs[0..len]) catch |err| {
...@@ -1733,7 +1732,7 @@ pub const Writer = struct {...@@ -1733,7 +1732,7 @@ pub const Writer = struct {
1733 },1732 },
1734 else => for (0..splat - 1) |_| {1733 else => for (0..splat - 1) |_| {
1735 if (iovecs.len - len == 0) break;1734 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 };
1737 len += 1;1736 len += 1;
1738 },1737 },
1739 },1738 },
...@@ -1743,15 +1742,15 @@ pub const Writer = struct {...@@ -1743,15 +1742,15 @@ pub const Writer = struct {
1743 return error.WriteFailed;1742 return error.WriteFailed;
1744 };1743 };
1745 w.pos += n;1744 w.pos += n;
1746 return io_writer.consume(n);1745 return io_w.consume(n);
1747 }1746 }
17481747
1749 pub fn sendFile(1748 pub fn sendFile(
1750 io_writer: *std.io.Writer,1749 io_w: *std.io.Writer,
1751 file_reader: *Reader,1750 file_reader: *Reader,
1752 limit: std.io.Limit,1751 limit: std.io.Limit,
1753 ) std.io.Writer.FileError!usize {1752 ) std.io.Writer.FileError!usize {
1754 const w: *Writer = @fieldParentPtr("interface", io_writer);1753 const w: *Writer = @fieldParentPtr("interface", io_w);
1755 const out_fd = w.file.handle;1754 const out_fd = w.file.handle;
1756 const in_fd = file_reader.file.handle;1755 const in_fd = file_reader.file.handle;
1757 // TODO try using copy_file_range on FreeBSD1756 // TODO try using copy_file_range on FreeBSD
...@@ -1762,7 +1761,7 @@ pub const Writer = struct {...@@ -1762,7 +1761,7 @@ pub const Writer = struct {
1762 if (w.sendfile_err != null) break :sf;1761 if (w.sendfile_err != null) break :sf;
1763 // Linux sendfile does not support headers.1762 // Linux sendfile does not support headers.
1764 const buffered = limit.slice(file_reader.interface.buffer);1763 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);
1766 const max_count = 0x7ffff000; // Avoid EINVAL.1765 const max_count = 0x7ffff000; // Avoid EINVAL.
1767 var off: std.os.linux.off_t = undefined;1766 var off: std.os.linux.off_t = undefined;
1768 const off_ptr: ?*std.os.linux.off_t, const count: usize = switch (file_reader.mode) {1767 const off_ptr: ?*std.os.linux.off_t, const count: usize = switch (file_reader.mode) {
...@@ -1813,7 +1812,7 @@ pub const Writer = struct {...@@ -1813,7 +1812,7 @@ pub const Writer = struct {
1813 if (copy_file_range_fn) |copy_file_range| cfr: {1812 if (copy_file_range_fn) |copy_file_range| cfr: {
1814 if (w.copy_file_range_err != null) break :cfr;1813 if (w.copy_file_range_err != null) break :cfr;
1815 const buffered = limit.slice(file_reader.interface.buffer);1814 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);
1817 var off_in: i64 = undefined;1816 var off_in: i64 = undefined;
1818 var off_out: i64 = undefined;1817 var off_out: i64 = undefined;
1819 const off_in_ptr: ?*i64 = switch (file_reader.mode) {1818 const off_in_ptr: ?*i64 = switch (file_reader.mode) {