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(...@@ -923,10 +923,12 @@ pub fn sendFileHeader(
923 return n;923 return n;
924}924}
925925
926/// Asserts nonzero buffer capacity.926/// Asserts nonzero buffer capacity and nonzero `limit`.
927pub fn sendFileReading(w: *Writer, file_reader: *File.Reader, limit: Limit) FileReadingError!usize {927pub fn sendFileReading(w: *Writer, file_reader: *File.Reader, limit: Limit) FileReadingError!usize {
928 assert(limit != .nothing);
928 const dest = limit.slice(try w.writableSliceGreedy(1));929 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;
930 w.advance(n);932 w.advance(n);
931 return n;933 return n;
932}934}
...@@ -2778,7 +2780,8 @@ pub const Allocating = struct {...@@ -2778,7 +2780,8 @@ pub const Allocating = struct {
2778 if (additional == 0) return error.EndOfStream;2780 if (additional == 0) return error.EndOfStream;
2779 a.ensureUnusedCapacity(limit.minInt64(additional)) catch return error.WriteFailed;2781 a.ensureUnusedCapacity(limit.minInt64(additional)) catch return error.WriteFailed;
2780 const dest = limit.slice(a.writer.buffer[a.writer.end..]);2782 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;
2782 a.writer.end += n;2785 a.writer.end += n;
2783 return n;2786 return n;
2784 }2787 }
...@@ -2849,18 +2852,40 @@ test "allocating sendFile" {...@@ -2849,18 +2852,40 @@ test "allocating sendFile" {
28492852
2850 const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true });2853 const file = try tmp_dir.dir.createFile("input.txt", .{ .read = true });
2851 defer file.close();2854 defer file.close();
2852 var r_buffer: [256]u8 = undefined;2855 var r_buffer: [2]u8 = undefined;
2853 var file_writer: std.fs.File.Writer = .init(file, &r_buffer);2856 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");
2855 try file_writer.interface.flush();2858 try file_writer.interface.flush();
28562859
2857 var file_reader = file_writer.moveToReader();2860 var file_reader = file_writer.moveToReader();
2858 try file_reader.seekTo(0);2861 try file_reader.seekTo(0);
2862 try file_reader.interface.fill(2);
28592863
2860 var allocating: Writer.Allocating = .init(testing.allocator);2864 var allocating: Writer.Allocating = .init(testing.allocator);
2861 defer allocating.deinit();2865 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));
2864}2889}
28652890
2866test writeStruct {2891test writeStruct {
lib/std/fs/File.zig+93-93
...@@ -1269,13 +1269,15 @@ pub const Reader = struct {...@@ -1269,13 +1269,15 @@ pub const Reader = struct {
1269 },1269 },
1270 .positional_reading => {1270 .positional_reading => {
1271 const dest = limit.slice(try w.writableSliceGreedy(1));1271 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);
1273 w.advance(n);1274 w.advance(n);
1274 return n;1275 return n;
1275 },1276 },
1276 .streaming_reading => {1277 .streaming_reading => {
1277 const dest = limit.slice(try w.writableSliceGreedy(1));1278 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);
1279 w.advance(n);1281 w.advance(n);
1280 return n;1282 return n;
1281 },1283 },
...@@ -1286,92 +1288,98 @@ pub const Reader = struct {...@@ -1286,92 +1288,98 @@ pub const Reader = struct {
1286 fn readVec(io_reader: *std.Io.Reader, data: [][]u8) std.Io.Reader.Error!usize {1288 fn readVec(io_reader: *std.Io.Reader, data: [][]u8) std.Io.Reader.Error!usize {
1287 const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader));1289 const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader));
1288 switch (r.mode) {1290 switch (r.mode) {
1289 .positional, .positional_reading => {1291 .positional, .positional_reading => return readVecPositional(r, data),
1290 if (is_windows) {1292 .streaming, .streaming_reading => return readVecStreaming(r, data),
1291 // Unfortunately, `ReadFileScatter` cannot be used since it1293 .failure => return error.ReadFailed,
1292 // requires page alignment.1294 }
1293 if (io_reader.seek == io_reader.end) {1295 }
1294 io_reader.seek = 0;1296
1295 io_reader.end = 0;1297 fn readVecPositional(r: *Reader, data: [][]u8) std.Io.Reader.Error!usize {
1296 }1298 const io_reader = &r.interface;
1297 const first = data[0];1299 if (is_windows) {
1298 if (first.len >= io_reader.buffer.len - io_reader.end) {1300 // Unfortunately, `ReadFileScatter` cannot be used since it
1299 return readPositional(r, first);1301 // requires page alignment.
1300 } else {1302 if (io_reader.seek == io_reader.end) {
1301 io_reader.end += try readPositional(r, io_reader.buffer[io_reader.end..]);1303 io_reader.seek = 0;
1302 return 0;1304 io_reader.end = 0;
1303 }1305 }
1304 }1306 const first = data[0];
1305 var iovecs_buffer: [max_buffers_len]posix.iovec = undefined;1307 if (first.len >= io_reader.buffer.len - io_reader.end) {
1306 const dest_n, const data_size = try io_reader.writableVectorPosix(&iovecs_buffer, data);1308 return readPositional(r, first);
1307 const dest = iovecs_buffer[0..dest_n];1309 } else {
1308 assert(dest[0].len > 0);1310 io_reader.end += try readPositional(r, io_reader.buffer[io_reader.end..]);
1309 const n = posix.preadv(r.file.handle, dest, r.pos) catch |err| switch (err) {1311 return 0;
1310 error.Unseekable => {1312 }
1311 r.mode = r.mode.toStreaming();1313 }
1312 const pos = r.pos;1314 var iovecs_buffer: [max_buffers_len]posix.iovec = undefined;
1313 if (pos != 0) {1315 const dest_n, const data_size = try io_reader.writableVectorPosix(&iovecs_buffer, data);
1314 r.pos = 0;1316 const dest = iovecs_buffer[0..dest_n];
1315 r.seekBy(@intCast(pos)) catch {1317 assert(dest[0].len > 0);
1316 r.mode = .failure;1318 const n = posix.preadv(r.file.handle, dest, r.pos) catch |err| switch (err) {
1317 return error.ReadFailed;1319 error.Unseekable => {
1318 };1320 r.mode = r.mode.toStreaming();
1319 }1321 const pos = r.pos;
1320 return 0;1322 if (pos != 0) {
1321 },1323 r.pos = 0;
1322 else => |e| {1324 r.seekBy(@intCast(pos)) catch {
1323 r.err = e;1325 r.mode = .failure;
1324 return error.ReadFailed;1326 return error.ReadFailed;
1325 },1327 };
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;
1335 }1328 }
1336 return n;1329 return 0;
1337 },1330 },
1338 .streaming, .streaming_reading => {1331 else => |e| {
1339 if (is_windows) {1332 r.err = e;
1340 // Unfortunately, `ReadFileScatter` cannot be used since it1333 return error.ReadFailed;
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;
1372 },1334 },
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;
1374 }1344 }
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;
1375 }1383 }
13761384
1377 fn discard(io_reader: *std.Io.Reader, limit: std.Io.Limit) std.Io.Reader.Error!usize {1385 fn discard(io_reader: *std.Io.Reader, limit: std.Io.Limit) std.Io.Reader.Error!usize {
...@@ -1440,7 +1448,7 @@ pub const Reader = struct {...@@ -1440,7 +1448,7 @@ pub const Reader = struct {
1440 }1448 }
1441 }1449 }
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 {
1444 const n = r.file.pread(dest, r.pos) catch |err| switch (err) {1452 const n = r.file.pread(dest, r.pos) catch |err| switch (err) {
1445 error.Unseekable => {1453 error.Unseekable => {
1446 r.mode = r.mode.toStreaming();1454 r.mode = r.mode.toStreaming();
...@@ -1467,7 +1475,7 @@ pub const Reader = struct {...@@ -1467,7 +1475,7 @@ pub const Reader = struct {
1467 return n;1475 return n;
1468 }1476 }
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 {
1471 const n = r.file.read(dest) catch |err| {1479 const n = r.file.read(dest) catch |err| {
1472 r.err = err;1480 r.err = err;
1473 return error.ReadFailed;1481 return error.ReadFailed;
...@@ -1480,14 +1488,6 @@ pub const Reader = struct {...@@ -1480,14 +1488,6 @@ pub const Reader = struct {
1480 return n;1488 return n;
1481 }1489 }
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
1491 pub fn atEnd(r: *Reader) bool {1491 pub fn atEnd(r: *Reader) bool {
1492 // Even if stat fails, size is set when end is encountered.1492 // Even if stat fails, size is set when end is encountered.
1493 const size = r.size orelse return false;1493 const size = r.size orelse return false;
src/link/MappedFile.zig+2-1
...@@ -411,7 +411,8 @@ pub const Node = extern struct {...@@ -411,7 +411,8 @@ pub const Node = extern struct {
411 .failure,411 .failure,
412 => {412 => {
413 const dest = limit.slice(interface.unusedCapacitySlice());413 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;
415 interface.end += n;416 interface.end += n;
416 return n;417 return n;
417 },418 },