authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-08-05 11:26:00-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-08-06 21:23:06-07:00
log04fe1bfe3ceabd632183a85101cddeeec11f0745
treeae10e13312f60a6010ffad760d62a617e28de060
parente17a050bc695f7d117b89adb1d258813593ca111

std.Io.Reader: use readVec for fill functions

readVec has two updated responsibilities: 1. it must respect any existing already buffered data. 2. it must write to the buffer if data is empty

2 files changed, 120 insertions(+), 62 deletions(-)

lib/std/Io/Reader.zig+100-60
...@@ -70,13 +70,14 @@ pub const VTable = struct {...@@ -70,13 +70,14 @@ pub const VTable = struct {
7070
71 /// Returns number of bytes written to `data`.71 /// Returns number of bytes written to `data`.
72 ///72 ///
73 /// `data` may not have nonzero length.73 /// `data` must have nonzero length. `data[0]` may have zero length, in
74 /// which case the implementation must write to `Reader.buffer`.
74 ///75 ///
75 /// `data` may not contain an alias to `Reader.buffer`.76 /// `data` may not contain an alias to `Reader.buffer`.
76 ///77 ///
77 /// `data` is mutable because the implementation may to temporarily modify78 /// `data` is mutable because the implementation may temporarily modify the
78 /// the fields in order to handle partial reads. Implementations must79 /// fields in order to handle partial reads. Implementations must restore
79 /// restore the original value before returning.80 /// the original value before returning.
80 ///81 ///
81 /// Implementations may ignore `data`, writing directly to `Reader.buffer`,82 /// Implementations may ignore `data`, writing directly to `Reader.buffer`,
82 /// modifying `seek` and `end` accordingly, and returning 0 from this83 /// modifying `seek` and `end` accordingly, and returning 0 from this
...@@ -421,23 +422,29 @@ pub fn readVec(r: *Reader, data: [][]u8) Error!usize {...@@ -421,23 +422,29 @@ pub fn readVec(r: *Reader, data: [][]u8) Error!usize {
421422
422/// Writes to `Reader.buffer` or `data`, whichever has larger capacity.423/// Writes to `Reader.buffer` or `data`, whichever has larger capacity.
423pub fn defaultReadVec(r: *Reader, data: [][]u8) Error!usize {424pub fn defaultReadVec(r: *Reader, data: [][]u8) Error!usize {
424 assert(r.seek == r.end);
425 r.seek = 0;
426 r.end = 0;
427 const first = data[0];425 const first = data[0];
428 const direct = first.len >= r.buffer.len;426 if (r.seek == r.end and first.len >= r.buffer.len) {
427 var writer: Writer = .{
428 .buffer = first,
429 .end = 0,
430 .vtable = &.{ .drain = Writer.fixedDrain },
431 };
432 const limit: Limit = .limited(writer.buffer.len - writer.end);
433 return r.vtable.stream(r, &writer, limit) catch |err| switch (err) {
434 error.WriteFailed => unreachable,
435 else => |e| return e,
436 };
437 }
429 var writer: Writer = .{438 var writer: Writer = .{
430 .buffer = if (direct) first else r.buffer,439 .buffer = r.buffer,
431 .end = 0,440 .end = r.end,
432 .vtable = &.{ .drain = Writer.fixedDrain },441 .vtable = &.{ .drain = Writer.fixedDrain },
433 };442 };
434 const limit: Limit = .limited(writer.buffer.len - writer.end);443 const limit: Limit = .limited(writer.buffer.len - writer.end);
435 const n = r.vtable.stream(r, &writer, limit) catch |err| switch (err) {444 r.end += r.vtable.stream(r, &writer, limit) catch |err| switch (err) {
436 error.WriteFailed => unreachable,445 error.WriteFailed => unreachable,
437 else => |e| return e,446 else => |e| return e,
438 };447 };
439 if (direct) return n;
440 r.end += n;
441 return 0;448 return 0;
442}449}
443450
...@@ -1059,17 +1066,8 @@ pub fn fill(r: *Reader, n: usize) Error!void {...@@ -1059,17 +1066,8 @@ pub fn fill(r: *Reader, n: usize) Error!void {
1059/// increasing by a factor of 5 or more.1066/// increasing by a factor of 5 or more.
1060fn fillUnbuffered(r: *Reader, n: usize) Error!void {1067fn fillUnbuffered(r: *Reader, n: usize) Error!void {
1061 try rebase(r, n);1068 try rebase(r, n);
1062 var writer: Writer = .{1069 var bufs: [1][]u8 = .{""};
1063 .buffer = r.buffer,1070 while (r.end < r.seek + n) _ = try r.vtable.readVec(r, &bufs);
1064 .vtable = &.{ .drain = Writer.fixedDrain },
1065 };
1066 while (r.end < r.seek + n) {
1067 writer.end = r.end;
1068 r.end += r.vtable.stream(r, &writer, .limited(r.buffer.len - r.end)) catch |err| switch (err) {
1069 error.WriteFailed => unreachable,
1070 error.ReadFailed, error.EndOfStream => |e| return e,
1071 };
1072 }
1073}1071}
10741072
1075/// Without advancing the seek position, does exactly one underlying read, filling the buffer as1073/// Without advancing the seek position, does exactly one underlying read, filling the buffer as
...@@ -1079,15 +1077,8 @@ fn fillUnbuffered(r: *Reader, n: usize) Error!void {...@@ -1079,15 +1077,8 @@ fn fillUnbuffered(r: *Reader, n: usize) Error!void {
1079/// Asserts buffer capacity is at least 1.1077/// Asserts buffer capacity is at least 1.
1080pub fn fillMore(r: *Reader) Error!void {1078pub fn fillMore(r: *Reader) Error!void {
1081 try rebase(r, 1);1079 try rebase(r, 1);
1082 var writer: Writer = .{1080 var bufs: [1][]u8 = .{""};
1083 .buffer = r.buffer,1081 _ = try r.vtable.readVec(r, &bufs);
1084 .end = r.end,
1085 .vtable = &.{ .drain = Writer.fixedDrain },
1086 };
1087 r.end += r.vtable.stream(r, &writer, .limited(r.buffer.len - r.end)) catch |err| switch (err) {
1088 error.WriteFailed => unreachable,
1089 else => |e| return e,
1090 };
1091}1082}
10921083
1093/// Returns the next byte from the stream or returns `error.EndOfStream`.1084/// Returns the next byte from the stream or returns `error.EndOfStream`.
...@@ -1796,18 +1787,26 @@ pub fn Hashed(comptime Hasher: type) type {...@@ -1796,18 +1787,26 @@ pub fn Hashed(comptime Hasher: type) type {
17961787
1797 fn readVec(r: *Reader, data: [][]u8) Error!usize {1788 fn readVec(r: *Reader, data: [][]u8) Error!usize {
1798 const this: *@This() = @alignCast(@fieldParentPtr("reader", r));1789 const this: *@This() = @alignCast(@fieldParentPtr("reader", r));
1799 const n = try this.in.readVec(data);1790 var vecs: [8][]u8 = undefined; // Arbitrarily chosen amount.
1791 const dest_n, const data_size = try r.writableVector(&vecs, data);
1792 const dest = vecs[0..dest_n];
1793 const n = try this.in.readVec(dest);
1800 var remaining: usize = n;1794 var remaining: usize = n;
1801 for (data) |slice| {1795 for (dest) |slice| {
1802 if (remaining < slice.len) {1796 if (remaining < slice.len) {
1803 this.hasher.update(slice[0..remaining]);1797 this.hasher.update(slice[0..remaining]);
1804 return n;1798 remaining = 0;
1799 break;
1805 } else {1800 } else {
1806 remaining -= slice.len;1801 remaining -= slice.len;
1807 this.hasher.update(slice);1802 this.hasher.update(slice);
1808 }1803 }
1809 }1804 }
1810 assert(remaining == 0);1805 assert(remaining == 0);
1806 if (n > data_size) {
1807 r.end += n - data_size;
1808 return data_size;
1809 }
1811 return n;1810 return n;
1812 }1811 }
18131812
...@@ -1824,17 +1823,24 @@ pub fn Hashed(comptime Hasher: type) type {...@@ -1824,17 +1823,24 @@ pub fn Hashed(comptime Hasher: type) type {
1824pub fn writableVectorPosix(r: *Reader, buffer: []std.posix.iovec, data: []const []u8) Error!struct { usize, usize } {1823pub fn writableVectorPosix(r: *Reader, buffer: []std.posix.iovec, data: []const []u8) Error!struct { usize, usize } {
1825 var i: usize = 0;1824 var i: usize = 0;
1826 var n: usize = 0;1825 var n: usize = 0;
1827 for (data) |buf| {1826 if (r.seek == r.end) {
1828 if (buffer.len - i == 0) return .{ i, n };1827 for (data) |buf| {
1828 if (buffer.len - i == 0) return .{ i, n };
1829 if (buf.len != 0) {
1830 buffer[i] = .{ .base = buf.ptr, .len = buf.len };
1831 i += 1;
1832 n += buf.len;
1833 }
1834 }
1835 const buf = r.buffer;
1829 if (buf.len != 0) {1836 if (buf.len != 0) {
1837 r.seek = 0;
1838 r.end = 0;
1830 buffer[i] = .{ .base = buf.ptr, .len = buf.len };1839 buffer[i] = .{ .base = buf.ptr, .len = buf.len };
1831 i += 1;1840 i += 1;
1832 n += buf.len;
1833 }1841 }
1834 }1842 } else {
1835 assert(r.seek == r.end);1843 const buf = r.buffer[r.end..];
1836 const buf = r.buffer;
1837 if (buf.len != 0) {
1838 buffer[i] = .{ .base = buf.ptr, .len = buf.len };1844 buffer[i] = .{ .base = buf.ptr, .len = buf.len };
1839 i += 1;1845 i += 1;
1840 }1846 }
...@@ -1848,28 +1854,62 @@ pub fn writableVectorWsa(...@@ -1848,28 +1854,62 @@ pub fn writableVectorWsa(
1848) Error!struct { usize, usize } {1854) Error!struct { usize, usize } {
1849 var i: usize = 0;1855 var i: usize = 0;
1850 var n: usize = 0;1856 var n: usize = 0;
1851 for (data) |buf| {1857 if (r.seek == r.end) {
1852 if (buffer.len - i == 0) return .{ i, n };1858 for (data) |buf| {
1853 if (buf.len == 0) continue;1859 if (buffer.len - i == 0) return .{ i, n };
1854 if (std.math.cast(u32, buf.len)) |len| {1860 if (buf.len == 0) continue;
1855 buffer[i] = .{ .buf = buf.ptr, .len = len };1861 if (std.math.cast(u32, buf.len)) |len| {
1862 buffer[i] = .{ .buf = buf.ptr, .len = len };
1863 i += 1;
1864 n += len;
1865 continue;
1866 }
1867 buffer[i] = .{ .buf = buf.ptr, .len = std.math.maxInt(u32) };
1868 i += 1;
1869 n += std.math.maxInt(u32);
1870 return .{ i, n };
1871 }
1872 const buf = r.buffer;
1873 if (buf.len != 0) {
1874 r.seek = 0;
1875 r.end = 0;
1876 if (std.math.cast(u32, buf.len)) |len| {
1877 buffer[i] = .{ .buf = buf.ptr, .len = len };
1878 } else {
1879 buffer[i] = .{ .buf = buf.ptr, .len = std.math.maxInt(u32) };
1880 }
1856 i += 1;1881 i += 1;
1857 n += len;
1858 continue;
1859 }1882 }
1860 buffer[i] = .{ .buf = buf.ptr, .len = std.math.maxInt(u32) };1883 } else {
1884 buffer[i] = .{
1885 .buf = r.buffer.ptr + r.end,
1886 .len = @min(std.math.maxInt(u32), r.buffer.len - r.end),
1887 };
1861 i += 1;1888 i += 1;
1862 n += std.math.maxInt(u32);
1863 return .{ i, n };
1864 }1889 }
1865 assert(r.seek == r.end);1890 return .{ i, n };
1866 const buf = r.buffer;1891}
1867 if (buf.len != 0) {1892
1868 if (std.math.cast(u32, buf.len)) |len| {1893pub fn writableVector(r: *Reader, buffer: [][]u8, data: []const []u8) Error!struct { usize, usize } {
1869 buffer[i] = .{ .buf = buf.ptr, .len = len };1894 var i: usize = 0;
1870 } else {1895 var n: usize = 0;
1871 buffer[i] = .{ .buf = buf.ptr, .len = std.math.maxInt(u32) };1896 if (r.seek == r.end) {
1897 for (data) |buf| {
1898 if (buffer.len - i == 0) return .{ i, n };
1899 if (buf.len != 0) {
1900 buffer[i] = buf;
1901 i += 1;
1902 n += buf.len;
1903 }
1904 }
1905 if (r.buffer.len != 0) {
1906 r.seek = 0;
1907 r.end = 0;
1908 buffer[i] = r.buffer;
1909 i += 1;
1872 }1910 }
1911 } else {
1912 buffer[i] = r.buffer[r.end..];
1873 i += 1;1913 i += 1;
1874 }1914 }
1875 return .{ i, n };1915 return .{ i, n };
lib/std/fs/File.zig+20-2
...@@ -1312,7 +1312,16 @@ pub const Reader = struct {...@@ -1312,7 +1312,16 @@ pub const Reader = struct {
1312 if (is_windows) {1312 if (is_windows) {
1313 // Unfortunately, `ReadFileScatter` cannot be used since it1313 // Unfortunately, `ReadFileScatter` cannot be used since it
1314 // requires page alignment.1314 // requires page alignment.
1315 return readPositional(r, data[0]);1315 assert(io_reader.seek == io_reader.end);
1316 io_reader.seek = 0;
1317 io_reader.end = 0;
1318 const first = data[0];
1319 if (first.len >= io_reader.buffer.len) {
1320 return readPositional(r, first);
1321 } else {
1322 io_reader.end += try readPositional(r, io_reader.buffer);
1323 return 0;
1324 }
1316 }1325 }
1317 var iovecs_buffer: [max_buffers_len]posix.iovec = undefined;1326 var iovecs_buffer: [max_buffers_len]posix.iovec = undefined;
1318 const dest_n, const data_size = try io_reader.writableVectorPosix(&iovecs_buffer, data);1327 const dest_n, const data_size = try io_reader.writableVectorPosix(&iovecs_buffer, data);
...@@ -1352,7 +1361,16 @@ pub const Reader = struct {...@@ -1352,7 +1361,16 @@ pub const Reader = struct {
1352 if (is_windows) {1361 if (is_windows) {
1353 // Unfortunately, `ReadFileScatter` cannot be used since it1362 // Unfortunately, `ReadFileScatter` cannot be used since it
1354 // requires page alignment.1363 // requires page alignment.
1355 return readStreaming(r, data[0]);1364 assert(io_reader.seek == io_reader.end);
1365 io_reader.seek = 0;
1366 io_reader.end = 0;
1367 const first = data[0];
1368 if (first.len >= io_reader.buffer.len) {
1369 return readStreaming(r, first);
1370 } else {
1371 io_reader.end += try readStreaming(r, io_reader.buffer);
1372 return 0;
1373 }
1356 }1374 }
1357 var iovecs_buffer: [max_buffers_len]posix.iovec = undefined;1375 var iovecs_buffer: [max_buffers_len]posix.iovec = undefined;
1358 const dest_n, const data_size = try io_reader.writableVectorPosix(&iovecs_buffer, data);1376 const dest_n, const data_size = try io_reader.writableVectorPosix(&iovecs_buffer, data);