authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-08 16:35:32-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-08 16:45:38-07:00
log889942a8b74d563e51923d9d14a45e58445aa38f
tree614d002e7b462b3e45001c9c930a5d7e168ad0e5
parent4c27eeff8e25d59648680696a35235af2e067933

std: fix sendFileReading not accounting for buffer

Related to 1d764c1fdf04829cec5974d82cec901825a80e49 Test case provided by: Co-authored-by: Kendall Condon <goon.pri.low@gmail.com>

3 files changed, 126 insertions(+), 100 deletions(-)

lib/std/Io/Writer.zig+31-6
......@@ -923,10 +923,12 @@ pub fn sendFileHeader(
923923 return n;
924924}
925925
926/// Asserts nonzero buffer capacity.
926/// Asserts nonzero buffer capacity and nonzero `limit`.
927927pub fn sendFileReading(w: *Writer, file_reader: *File.Reader, limit: Limit) FileReadingError!usize {
928 assert(limit != .nothing);
928929 const dest = limit.slice(try w.writableSliceGreedy(1));
929 const n = try file_reader.read(dest);
930 const n = try file_reader.interface.readSliceShort(dest);
931 if (n == 0) return error.EndOfStream;
930932 w.advance(n);
931933 return n;
932934}
......@@ -2778,7 +2780,8 @@ pub const Allocating = struct {
27782780 if (additional == 0) return error.EndOfStream;
27792781 a.ensureUnusedCapacity(limit.minInt64(additional)) catch return error.WriteFailed;
27802782 const dest = limit.slice(a.writer.buffer[a.writer.end..]);
2781 const n = try file_reader.read(dest);
2783 const n = try file_reader.interface.readSliceShort(dest);
2784 if (n == 0) return error.EndOfStream;
27822785 a.writer.end += n;
27832786 return n;
27842787 }
......@@ -2849,18 +2852,40 @@ test "allocating sendFile" {
28492852
28502853 const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true });
28512854 defer file.close();
2852 var r_buffer: [256]u8 = undefined;
2855 var r_buffer: [2]u8 = undefined;
28532856 var file_writer: std.fs.File.Writer = .init(file, &r_buffer);
2854 try file_writer.interface.writeByte('h');
2857 try file_writer.interface.writeAll("abcd");
28552858 try file_writer.interface.flush();
28562859
28572860 var file_reader = file_writer.moveToReader();
28582861 try file_reader.seekTo(0);
2862 try file_reader.interface.fill(2);
28592863
28602864 var allocating: Writer.Allocating = .init(testing.allocator);
28612865 defer allocating.deinit();
2866 try allocating.ensureUnusedCapacity(1);
2867 try testing.expectEqual(4, allocating.writer.sendFileAll(&file_reader, .unlimited));
2868 try testing.expectEqualStrings("abcd", allocating.writer.buffered());
2869}
2870
2871test sendFileReading {
2872 var tmp_dir = testing.tmpDir(.{});
2873 defer tmp_dir.cleanup();
2874
2875 const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true });
2876 defer file.close();
2877 var r_buffer: [2]u8 = undefined;
2878 var file_writer: std.fs.File.Writer = .init(file, &r_buffer);
2879 try file_writer.interface.writeAll("abcd");
2880 try file_writer.interface.flush();
28622881
2863 _ = try file_reader.interface.streamRemaining(&allocating.writer);
2882 var file_reader = file_writer.moveToReader();
2883 try file_reader.seekTo(0);
2884 try file_reader.interface.fill(2);
2885
2886 var w_buffer: [1]u8 = undefined;
2887 var discarding: Writer.Discarding = .init(&w_buffer);
2888 try testing.expectEqual(4, discarding.writer.sendFileReadingAll(&file_reader, .unlimited));
28642889}
28652890
28662891test writeStruct {
lib/std/fs/File.zig+93-93
......@@ -1269,13 +1269,15 @@ pub const Reader = struct {
12691269 },
12701270 .positional_reading => {
12711271 const dest = limit.slice(try w.writableSliceGreedy(1));
1272 const n = try readPositional(r, dest);
1272 var data: [1][]u8 = .{dest};
1273 const n = try readVecPositional(r, &data);
12731274 w.advance(n);
12741275 return n;
12751276 },
12761277 .streaming_reading => {
12771278 const dest = limit.slice(try w.writableSliceGreedy(1));
1278 const n = try readStreaming(r, dest);
1279 var data: [1][]u8 = .{dest};
1280 const n = try readVecStreaming(r, &data);
12791281 w.advance(n);
12801282 return n;
12811283 },
......@@ -1286,92 +1288,98 @@ pub const Reader = struct {
12861288 fn readVec(io_reader: *std.Io.Reader, data: [][]u8) std.Io.Reader.Error!usize {
12871289 const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader));
12881290 switch (r.mode) {
1289 .positional, .positional_reading => {
1290 if (is_windows) {
1291 // Unfortunately, `ReadFileScatter` cannot be used since it
1292 // requires page alignment.
1293 if (io_reader.seek == io_reader.end) {
1294 io_reader.seek = 0;
1295 io_reader.end = 0;
1296 }
1297 const first = data[0];
1298 if (first.len >= io_reader.buffer.len - io_reader.end) {
1299 return readPositional(r, first);
1300 } else {
1301 io_reader.end += try readPositional(r, io_reader.buffer[io_reader.end..]);
1302 return 0;
1303 }
1304 }
1305 var iovecs_buffer: [max_buffers_len]posix.iovec = undefined;
1306 const dest_n, const data_size = try io_reader.writableVectorPosix(&iovecs_buffer, data);
1307 const dest = iovecs_buffer[0..dest_n];
1308 assert(dest[0].len > 0);
1309 const n = posix.preadv(r.file.handle, dest, r.pos) catch |err| switch (err) {
1310 error.Unseekable => {
1311 r.mode = r.mode.toStreaming();
1312 const pos = r.pos;
1313 if (pos != 0) {
1314 r.pos = 0;
1315 r.seekBy(@intCast(pos)) catch {
1316 r.mode = .failure;
1317 return error.ReadFailed;
1318 };
1319 }
1320 return 0;
1321 },
1322 else => |e| {
1323 r.err = e;
1291 .positional, .positional_reading => return readVecPositional(r, data),
1292 .streaming, .streaming_reading => return readVecStreaming(r, data),
1293 .failure => return error.ReadFailed,
1294 }
1295 }
1296
1297 fn readVecPositional(r: *Reader, data: [][]u8) std.Io.Reader.Error!usize {
1298 const io_reader = &r.interface;
1299 if (is_windows) {
1300 // Unfortunately, `ReadFileScatter` cannot be used since it
1301 // requires page alignment.
1302 if (io_reader.seek == io_reader.end) {
1303 io_reader.seek = 0;
1304 io_reader.end = 0;
1305 }
1306 const first = data[0];
1307 if (first.len >= io_reader.buffer.len - io_reader.end) {
1308 return readPositional(r, first);
1309 } else {
1310 io_reader.end += try readPositional(r, io_reader.buffer[io_reader.end..]);
1311 return 0;
1312 }
1313 }
1314 var iovecs_buffer: [max_buffers_len]posix.iovec = undefined;
1315 const dest_n, const data_size = try io_reader.writableVectorPosix(&iovecs_buffer, data);
1316 const dest = iovecs_buffer[0..dest_n];
1317 assert(dest[0].len > 0);
1318 const n = posix.preadv(r.file.handle, dest, r.pos) catch |err| switch (err) {
1319 error.Unseekable => {
1320 r.mode = r.mode.toStreaming();
1321 const pos = r.pos;
1322 if (pos != 0) {
1323 r.pos = 0;
1324 r.seekBy(@intCast(pos)) catch {
1325 r.mode = .failure;
13241326 return error.ReadFailed;
1325 },
1326 };
1327 if (n == 0) {
1328 r.size = r.pos;
1329 return error.EndOfStream;
1330 }
1331 r.pos += n;
1332 if (n > data_size) {
1333 io_reader.end += n - data_size;
1334 return data_size;
1327 };
13351328 }
1336 return n;
1329 return 0;
13371330 },
1338 .streaming, .streaming_reading => {
1339 if (is_windows) {
1340 // Unfortunately, `ReadFileScatter` cannot be used since it
1341 // requires page alignment.
1342 if (io_reader.seek == io_reader.end) {
1343 io_reader.seek = 0;
1344 io_reader.end = 0;
1345 }
1346 const first = data[0];
1347 if (first.len >= io_reader.buffer.len - io_reader.end) {
1348 return readStreaming(r, first);
1349 } else {
1350 io_reader.end += try readStreaming(r, io_reader.buffer[io_reader.end..]);
1351 return 0;
1352 }
1353 }
1354 var iovecs_buffer: [max_buffers_len]posix.iovec = undefined;
1355 const dest_n, const data_size = try io_reader.writableVectorPosix(&iovecs_buffer, data);
1356 const dest = iovecs_buffer[0..dest_n];
1357 assert(dest[0].len > 0);
1358 const n = posix.readv(r.file.handle, dest) catch |err| {
1359 r.err = err;
1360 return error.ReadFailed;
1361 };
1362 if (n == 0) {
1363 r.size = r.pos;
1364 return error.EndOfStream;
1365 }
1366 r.pos += n;
1367 if (n > data_size) {
1368 io_reader.end += n - data_size;
1369 return data_size;
1370 }
1371 return n;
1331 else => |e| {
1332 r.err = e;
1333 return error.ReadFailed;
13721334 },
1373 .failure => return error.ReadFailed,
1335 };
1336 if (n == 0) {
1337 r.size = r.pos;
1338 return error.EndOfStream;
1339 }
1340 r.pos += n;
1341 if (n > data_size) {
1342 io_reader.end += n - data_size;
1343 return data_size;
13741344 }
1345 return n;
1346 }
1347
1348 fn readVecStreaming(r: *Reader, data: [][]u8) std.Io.Reader.Error!usize {
1349 const io_reader = &r.interface;
1350 if (is_windows) {
1351 // Unfortunately, `ReadFileScatter` cannot be used since it
1352 // requires page alignment.
1353 if (io_reader.seek == io_reader.end) {
1354 io_reader.seek = 0;
1355 io_reader.end = 0;
1356 }
1357 const first = data[0];
1358 if (first.len >= io_reader.buffer.len - io_reader.end) {
1359 return readStreaming(r, first);
1360 } else {
1361 io_reader.end += try readStreaming(r, io_reader.buffer[io_reader.end..]);
1362 return 0;
1363 }
1364 }
1365 var iovecs_buffer: [max_buffers_len]posix.iovec = undefined;
1366 const dest_n, const data_size = try io_reader.writableVectorPosix(&iovecs_buffer, data);
1367 const dest = iovecs_buffer[0..dest_n];
1368 assert(dest[0].len > 0);
1369 const n = posix.readv(r.file.handle, dest) catch |err| {
1370 r.err = err;
1371 return error.ReadFailed;
1372 };
1373 if (n == 0) {
1374 r.size = r.pos;
1375 return error.EndOfStream;
1376 }
1377 r.pos += n;
1378 if (n > data_size) {
1379 io_reader.end += n - data_size;
1380 return data_size;
1381 }
1382 return n;
13751383 }
13761384
13771385 fn discard(io_reader: *std.Io.Reader, limit: std.Io.Limit) std.Io.Reader.Error!usize {
......@@ -1440,7 +1448,7 @@ pub const Reader = struct {
14401448 }
14411449 }
14421450
1443 pub fn readPositional(r: *Reader, dest: []u8) std.Io.Reader.Error!usize {
1451 fn readPositional(r: *Reader, dest: []u8) std.Io.Reader.Error!usize {
14441452 const n = r.file.pread(dest, r.pos) catch |err| switch (err) {
14451453 error.Unseekable => {
14461454 r.mode = r.mode.toStreaming();
......@@ -1467,7 +1475,7 @@ pub const Reader = struct {
14671475 return n;
14681476 }
14691477
1470 pub fn readStreaming(r: *Reader, dest: []u8) std.Io.Reader.Error!usize {
1478 fn readStreaming(r: *Reader, dest: []u8) std.Io.Reader.Error!usize {
14711479 const n = r.file.read(dest) catch |err| {
14721480 r.err = err;
14731481 return error.ReadFailed;
......@@ -1480,14 +1488,6 @@ pub const Reader = struct {
14801488 return n;
14811489 }
14821490
1483 pub fn read(r: *Reader, dest: []u8) std.Io.Reader.Error!usize {
1484 switch (r.mode) {
1485 .positional, .positional_reading => return readPositional(r, dest),
1486 .streaming, .streaming_reading => return readStreaming(r, dest),
1487 .failure => return error.ReadFailed,
1488 }
1489 }
1490
14911491 pub fn atEnd(r: *Reader) bool {
14921492 // Even if stat fails, size is set when end is encountered.
14931493 const size = r.size orelse return false;
src/link/MappedFile.zig+2-1
......@@ -411,7 +411,8 @@ pub const Node = extern struct {
411411 .failure,
412412 => {
413413 const dest = limit.slice(interface.unusedCapacitySlice());
414 const n = try file_reader.read(dest);
414 const n = try file_reader.interface.readSliceShort(dest);
415 if (n == 0) return error.EndOfStream;
415416 interface.end += n;
416417 return n;
417418 },