authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-06-24 02:39:48-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-01 16:35:30-07:00
log7926081ec35761028c87cdb82204f177311866be
treec46f9d181593d0763641f8409b7bffbf4af38ef8
parent837f2bfc697c604b0d16d789b4ca2752f58b0ed5

fix std.fs.File.Reader


3 files changed, 109 insertions(+), 27 deletions(-)

lib/std/fs/File.zig+78-25
...@@ -969,6 +969,14 @@ pub const Reader = struct {...@@ -969,6 +969,14 @@ pub const Reader = struct {
969 };969 };
970 }970 }
971971
972 pub fn initMode(file: File, buffer: []u8, init_mode: Reader.Mode) Reader {
973 return .{
974 .file = file,
975 .interface = initInterface(buffer),
976 .mode = init_mode,
977 };
978 }
979
972 pub fn getSize(r: *Reader) GetEndPosError!u64 {980 pub fn getSize(r: *Reader) GetEndPosError!u64 {
973 return r.size orelse {981 return r.size orelse {
974 if (r.size_err) |err| return err;982 if (r.size_err) |err| return err;
...@@ -1038,7 +1046,7 @@ pub const Reader = struct {...@@ -1038,7 +1046,7 @@ pub const Reader = struct {
1038 fn stream(io_reader: *std.io.Reader, w: *std.io.Writer, limit: std.io.Limit) std.io.Reader.StreamError!usize {1046 fn stream(io_reader: *std.io.Reader, w: *std.io.Writer, limit: std.io.Limit) std.io.Reader.StreamError!usize {
1039 const r: *Reader = @fieldParentPtr("interface", io_reader);1047 const r: *Reader = @fieldParentPtr("interface", io_reader);
1040 switch (r.mode) {1048 switch (r.mode) {
1041 .positional, .streaming => return w.writeFile(r, limit, &.{}, 0) catch |write_err| switch (write_err) {1049 .positional, .streaming => return w.sendFile(r, limit) catch |write_err| switch (write_err) {
1042 error.ReadFailed => return error.ReadFailed,1050 error.ReadFailed => return error.ReadFailed,
1043 error.WriteFailed => return error.WriteFailed,1051 error.WriteFailed => return error.WriteFailed,
1044 error.Unimplemented => {1052 error.Unimplemented => {
...@@ -1056,7 +1064,7 @@ pub const Reader = struct {...@@ -1056,7 +1064,7 @@ pub const Reader = struct {
1056 return n;1064 return n;
1057 }1065 }
1058 var iovecs_buffer: [max_buffers_len]posix.iovec = undefined;1066 var iovecs_buffer: [max_buffers_len]posix.iovec = undefined;
1059 const dest = w.writableVectorPosix(&iovecs_buffer, limit);1067 const dest = try w.writableVectorPosix(&iovecs_buffer, limit);
1060 assert(dest[0].len > 0);1068 assert(dest[0].len > 0);
1061 const n = posix.preadv(r.file.handle, dest, r.pos) catch |err| switch (err) {1069 const n = posix.preadv(r.file.handle, dest, r.pos) catch |err| switch (err) {
1062 error.Unseekable => {1070 error.Unseekable => {
...@@ -1089,9 +1097,10 @@ pub const Reader = struct {...@@ -1089,9 +1097,10 @@ pub const Reader = struct {
1089 return n;1097 return n;
1090 }1098 }
1091 var iovecs_buffer: [max_buffers_len]posix.iovec = undefined;1099 var iovecs_buffer: [max_buffers_len]posix.iovec = undefined;
1092 const dest = w.writableVectorPosix(&iovecs_buffer, limit);1100 const dest = try w.writableVectorPosix(&iovecs_buffer, limit);
1093 assert(dest[0].len > 0);1101 assert(dest[0].len > 0);
1094 const n = posix.pread(r.file.handle, dest) catch |err| {1102 // TODO also add buffer at the end
1103 const n = posix.readv(r.file.handle, dest) catch |err| {
1095 r.err = err;1104 r.err = err;
1096 return error.ReadFailed;1105 return error.ReadFailed;
1097 };1106 };
...@@ -1296,33 +1305,70 @@ pub const Writer = struct {...@@ -1296,33 +1305,70 @@ pub const Writer = struct {
1296 pub fn drain(io_writer: *std.io.Writer, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {1305 pub fn drain(io_writer: *std.io.Writer, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {
1297 const w: *Writer = @fieldParentPtr("interface", io_writer);1306 const w: *Writer = @fieldParentPtr("interface", io_writer);
1298 const handle = w.file.handle;1307 const handle = w.file.handle;
1299 if (true) @panic("update to check for buffered data");1308 const buffered = io_writer.buffered();
1300 var splat_buffer: [256]u8 = undefined;1309 var splat_buffer: [256]u8 = undefined;
1301 if (is_windows) {1310 if (is_windows) {
1302 if (data.len == 1 and splat == 0) return 0;1311 var i: usize = 0;
1303 return windows.WriteFile(handle, data[0], null);1312 while (i < buffered.len) {
1313 const n = windows.WriteFile(handle, buffered[i..], null) catch |err| {
1314 w.err = err;
1315 w.pos += i;
1316 _ = io_writer.consume(i);
1317 return error.WriteFailed;
1318 };
1319 i += n;
1320 if (data.len > 0 and buffered.len - i < n) {
1321 w.pos += i;
1322 return io_writer.consume(i);
1323 }
1324 }
1325 if (i != 0 or data.len == 0 or (data.len == 1 and splat == 0)) {
1326 w.pos += i;
1327 return io_writer.consume(i);
1328 }
1329 const n = windows.WriteFile(handle, data[0], null) catch |err| {
1330 w.err = err;
1331 return 0;
1332 };
1333 w.pos += n;
1334 return n;
1335 }
1336 if (data.len == 0) {
1337 var i: usize = 0;
1338 while (i < buffered.len) {
1339 i += std.posix.write(handle, buffered) catch |err| {
1340 w.err = err;
1341 w.pos += i;
1342 _ = io_writer.consume(i);
1343 return error.WriteFailed;
1344 };
1345 }
1346 w.pos += i;
1347 return io_writer.consumeAll();
1304 }1348 }
1305 var iovecs: [max_buffers_len]std.posix.iovec_const = undefined;1349 var iovecs: [max_buffers_len]std.posix.iovec_const = undefined;
1306 var len: usize = @min(iovecs.len, data.len);1350 var len: usize = 0;
1307 for (iovecs[0..len], data[0..len]) |*v, d| v.* = .{1351 if (buffered.len > 0) {
1308 .base = if (d.len == 0) "" else d.ptr, // OS sadly checks ptr addr before length.1352 iovecs[len] = .{ .base = buffered.ptr, .len = buffered.len };
1309 .len = d.len,1353 len += 1;
1310 };1354 }
1355 for (data) |d| {
1356 if (d.len == 0) continue;
1357 if (iovecs.len - len == 0) break;
1358 iovecs[len] = .{ .base = d.ptr, .len = d.len };
1359 len += 1;
1360 }
1311 switch (splat) {1361 switch (splat) {
1312 0 => return std.posix.writev(handle, iovecs[0 .. len - 1]) catch |err| {1362 0 => if (data[data.len - 1].len != 0) {
1313 w.err = err;1363 len -= 1;
1314 return error.WriteFailed;
1315 },
1316 1 => return std.posix.writev(handle, iovecs[0..len]) catch |err| {
1317 w.err = err;
1318 return error.WriteFailed;
1319 },1364 },
1320 else => {1365 1 => {},
1321 const pattern = data[data.len - 1];1366 else => switch (data[data.len - 1].len) {
1322 if (pattern.len == 1) {1367 0 => {},
1368 1 => {
1323 const memset_len = @min(splat_buffer.len, splat);1369 const memset_len = @min(splat_buffer.len, splat);
1324 const buf = splat_buffer[0..memset_len];1370 const buf = splat_buffer[0..memset_len];
1325 @memset(buf, pattern[0]);1371 @memset(buf, data[data.len - 1][0]);
1326 iovecs[len - 1] = .{ .base = buf.ptr, .len = buf.len };1372 iovecs[len - 1] = .{ .base = buf.ptr, .len = buf.len };
1327 var remaining_splat = splat - buf.len;1373 var remaining_splat = splat - buf.len;
1328 while (remaining_splat > splat_buffer.len and len < iovecs.len) {1374 while (remaining_splat > splat_buffer.len and len < iovecs.len) {
...@@ -1338,13 +1384,20 @@ pub const Writer = struct {...@@ -1338,13 +1384,20 @@ pub const Writer = struct {
1338 w.err = err;1384 w.err = err;
1339 return error.WriteFailed;1385 return error.WriteFailed;
1340 };1386 };
1341 }1387 },
1388 else => for (0..splat - 1) |_| {
1389 if (iovecs.len - len == 0) break;
1390 iovecs[len] = .{ .base = data[data.len - 1].ptr, .len = data[data.len - 1].len };
1391 len += 1;
1392 },
1342 },1393 },
1343 }1394 }
1344 return std.posix.writev(handle, iovecs[0..len]) catch |err| {1395 const n = std.posix.writev(handle, iovecs[0..len]) catch |err| {
1345 w.err = err;1396 w.err = err;
1346 return error.WriteFailed;1397 return error.WriteFailed;
1347 };1398 };
1399 w.pos += n;
1400 return io_writer.consume(n);
1348 }1401 }
13491402
1350 pub fn sendFile(1403 pub fn sendFile(
lib/std/io/Reader.zig-1
...@@ -329,7 +329,6 @@ pub fn readVecLimit(r: *Reader, data: []const []u8, limit: Limit) Error!usize {...@@ -329,7 +329,6 @@ pub fn readVecLimit(r: *Reader, data: []const []u8, limit: Limit) Error!usize {
329 },329 },
330 else => |e| return e,330 else => |e| return e,
331 };331 };
332 assert(n == wrapper.writer.end);
333 if (wrapper.writer.buffer.ptr != first.ptr) {332 if (wrapper.writer.buffer.ptr != first.ptr) {
334 r.end = n;333 r.end = n;
335 break;334 break;
lib/std/io/Writer.zig+31-1
...@@ -326,13 +326,29 @@ pub const VectorWrapper = struct {...@@ -326,13 +326,29 @@ pub const VectorWrapper = struct {
326};326};
327327
328pub fn writableVectorIterator(w: *Writer) Error!WritableVectorIterator {328pub fn writableVectorIterator(w: *Writer) Error!WritableVectorIterator {
329 if (w.context == &VectorWrapper.unique_address) {329 if (@as(*u8, @ptrCast(w.context)) == &VectorWrapper.unique_address) {
330 const wrapper: *VectorWrapper = @fieldParentPtr("writer", w);330 const wrapper: *VectorWrapper = @fieldParentPtr("writer", w);
331 return wrapper.it;331 return wrapper.it;
332 }332 }
333 return .{ .first = try writableSliceGreedy(w, 1) };333 return .{ .first = try writableSliceGreedy(w, 1) };
334}334}
335335
336pub fn writableVectorPosix(w: *Writer, buffer: []std.posix.iovec, limit: Limit) Error![]std.posix.iovec {
337 var it = try writableVectorIterator(w);
338 var i: usize = 0;
339 var remaining = limit;
340 while (it.next()) |full_buffer| {
341 if (!remaining.nonzero()) break;
342 if (buffer.len - i == 0) break;
343 const buf = remaining.slice(full_buffer);
344 if (buf.len == 0) continue;
345 buffer[i] = .{ .base = buf.ptr, .len = buf.len };
346 i += 1;
347 remaining = remaining.subtract(buf.len).?;
348 }
349 return buffer[0..i];
350}
351
336pub fn ensureUnusedCapacity(w: *Writer, n: usize) Error!void {352pub fn ensureUnusedCapacity(w: *Writer, n: usize) Error!void {
337 _ = try writableSliceGreedy(w, n);353 _ = try writableSliceGreedy(w, n);
338}354}
...@@ -353,6 +369,13 @@ pub fn advance(w: *Writer, n: usize) void {...@@ -353,6 +369,13 @@ pub fn advance(w: *Writer, n: usize) void {
353 w.count += n;369 w.count += n;
354}370}
355371
372/// After calling `writableVector`, this function tracks how many bytes were
373/// written to it.
374pub fn advanceVector(w: *Writer, n: usize) usize {
375 w.count += n;
376 return consume(w, n);
377}
378
356/// The `data` parameter is mutable because this function needs to mutate the379/// The `data` parameter is mutable because this function needs to mutate the
357/// fields in order to handle partial writes from `VTable.writeSplat`.380/// fields in order to handle partial writes from `VTable.writeSplat`.
358pub fn writeVecAll(w: *Writer, data: [][]const u8) Error!void {381pub fn writeVecAll(w: *Writer, data: [][]const u8) Error!void {
...@@ -1830,6 +1853,13 @@ pub fn consume(w: *Writer, n: usize) usize {...@@ -1830,6 +1853,13 @@ pub fn consume(w: *Writer, n: usize) usize {
1830 return n - w.end;1853 return n - w.end;
1831}1854}
18321855
1856/// Shortcut for setting `end` to zero and returning zero. Equivalent to
1857/// calling `consume` with `end`.
1858pub fn consumeAll(w: *Writer) usize {
1859 w.end = 0;
1860 return 0;
1861}
1862
1833/// For use when the `Writer` implementation can cannot offer a more efficient1863/// For use when the `Writer` implementation can cannot offer a more efficient
1834/// implementation than a basic read/write loop on the file.1864/// implementation than a basic read/write loop on the file.
1835pub fn unimplementedSendFile(w: *Writer, file_reader: *File.Reader, limit: Limit) FileError!usize {1865pub fn unimplementedSendFile(w: *Writer, file_reader: *File.Reader, limit: Limit) FileError!usize {