authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-02-15 22:17:30-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-01 16:35:25-07:00
log1d0495678dc3729feb40939ddcaaef552efadbda
treeb38f415264e2ed5901c35318dbafbfb1f0d29691
parent35824e4822acbb1922a5890c5753b48627e3982f

fixes

writeFile should be split into two

3 files changed, 74 insertions(+), 31 deletions(-)

lib/std/fs/File.zig+25-5
...@@ -1611,18 +1611,38 @@ const interface = struct {...@@ -1611,18 +1611,38 @@ const interface = struct {
16111611
1612 fn writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize {1612 fn writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize {
1613 const file = opaqueToHandle(context);1613 const file = opaqueToHandle(context);
1614 var splat_buffer: [256]u8 = undefined;
1614 if (is_windows) {1615 if (is_windows) {
1615 if (data.len == 1 and splat == 0) return 0;1616 if (data.len == 1 and splat == 0) return 0;
1616 return windows.WriteFile(file, data[0], null);1617 return windows.WriteFile(file, data[0], null);
1617 }1618 }
1618 var iovecs_buffer: [max_buffers_len]std.posix.iovec_const = undefined;1619 var iovecs: [max_buffers_len]std.posix.iovec_const = undefined;
1619 const iovecs = iovecs_buffer[0..@min(iovecs_buffer.len, data.len)];1620 var len: usize = @min(iovecs.len, data.len);
1620 for (iovecs, data[0..iovecs.len]) |*v, d| v.* = .{1621 for (iovecs[0..len], data[0..len]) |*v, d| v.* = .{
1621 .base = if (d.len == 0) "" else d.ptr, // OS sadly checks ptr addr before length.1622 .base = if (d.len == 0) "" else d.ptr, // OS sadly checks ptr addr before length.
1622 .len = d.len,1623 .len = d.len,
1623 };1624 };
1624 const send_iovecs = if (splat == 0) iovecs[0 .. iovecs.len - 1] else iovecs;1625 switch (splat) {
1625 return std.posix.writev(file, send_iovecs);1626 0 => return std.posix.writev(file, iovecs[0 .. len - 1]),
1627 1 => return std.posix.writev(file, iovecs[0..len]),
1628 else => {
1629 const pattern = data[data.len - 1];
1630 if (pattern.len == 1) {
1631 const memset_len = @min(splat_buffer.len, splat);
1632 const buf = splat_buffer[0..memset_len];
1633 @memset(buf, pattern[0]);
1634 iovecs[len - 1] = .{ .base = buf.ptr, .len = buf.len };
1635 var remaining_splat = splat - buf.len;
1636 while (remaining_splat > 0 and len < iovecs.len) {
1637 iovecs[len] = .{ .base = &splat_buffer, .len = splat_buffer.len };
1638 remaining_splat -= splat_buffer.len;
1639 len += 1;
1640 }
1641 return std.posix.writev(file, iovecs[0..len]);
1642 }
1643 },
1644 }
1645 return std.posix.writev(file, iovecs[0..len]);
1626 }1646 }
16271647
1628 fn writeFile(1648 fn writeFile(
lib/std/io/BufferedWriter.zig+48-25
...@@ -76,7 +76,7 @@ pub fn flush(bw: *BufferedWriter) anyerror!void {...@@ -76,7 +76,7 @@ pub fn flush(bw: *BufferedWriter) anyerror!void {
7676
77/// The `data` parameter is mutable because this function needs to mutate the77/// The `data` parameter is mutable because this function needs to mutate the
78/// fields in order to handle partial writes from `Writer.VTable.writev`.78/// fields in order to handle partial writes from `Writer.VTable.writev`.
79pub fn writevAll(bw: *BufferedWriter, data: []const []const u8) anyerror!void {79pub fn writevAll(bw: *BufferedWriter, data: [][]const u8) anyerror!void {
80 var i: usize = 0;80 var i: usize = 0;
81 while (true) {81 while (true) {
82 var n = try passthru_writeSplat(bw, data[i..], 1);82 var n = try passthru_writeSplat(bw, data[i..], 1);
...@@ -164,7 +164,7 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi...@@ -164,7 +164,7 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi
164 @branchHint(.likely);164 @branchHint(.likely);
165 @memset(buffer[end..new_end], pattern[0]);165 @memset(buffer[end..new_end], pattern[0]);
166 bw.end = new_end;166 bw.end = new_end;
167 return end - start_end;167 return new_end - start_end;
168 }168 }
169 buffers[0] = buffer[0..end];169 buffers[0] = buffer[0..end];
170 buffers[1] = pattern;170 buffers[1] = pattern;
...@@ -186,8 +186,8 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi...@@ -186,8 +186,8 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi
186 while (end < new_end) : (end += pattern.len) {186 while (end < new_end) : (end += pattern.len) {
187 @memcpy(buffer[end..][0..pattern.len], pattern);187 @memcpy(buffer[end..][0..pattern.len], pattern);
188 }188 }
189 bw.end = end;189 bw.end = new_end;
190 return end - start_end;190 return new_end - start_end;
191 }191 }
192 buffers[0] = buffer[0..end];192 buffers[0] = buffer[0..end];
193 buffers[1] = pattern;193 buffers[1] = pattern;
...@@ -471,32 +471,55 @@ pub const WriteFileOptions = struct {...@@ -471,32 +471,55 @@ pub const WriteFileOptions = struct {
471pub fn writeFileAll(bw: *BufferedWriter, file: std.fs.File, options: WriteFileOptions) anyerror!void {471pub fn writeFileAll(bw: *BufferedWriter, file: std.fs.File, options: WriteFileOptions) anyerror!void {
472 const headers_and_trailers = options.headers_and_trailers;472 const headers_and_trailers = options.headers_and_trailers;
473 const headers = headers_and_trailers[0..options.headers_len];473 const headers = headers_and_trailers[0..options.headers_len];
474 var len = options.len;474 if (options.len == .zero) return writevAll(bw, headers_and_trailers);
475 var i: usize = 0;475 if (options.len == .entire_file) {
476 var offset = options.offset;476 // When reading the whole file, we cannot include the trailers in the
477 if (len == .zero) return writevAll(bw, headers_and_trailers[i..]);477 // call that reads from the file handle, because we have no way to
478 while (i < headers_and_trailers.len) {478 // determine whether a partial write is past the end of the file or
479 var n = try writeFile(bw, file, offset, len, headers_and_trailers[i..], headers.len - i);479 // not.
480 while (i < headers.len and n >= headers[i].len) {480 var i: usize = 0;
481 n -= headers[i].len;481 var offset = options.offset;
482 i += 1;482 while (true) {
483 }483 var n = try writeFile(bw, file, offset, .entire_file, headers[i..], headers.len - i);
484 if (i < headers.len) {484 while (i < headers.len and n >= headers[i].len) {
485 headers[i] = headers[i][n..];485 n -= headers[i].len;
486 continue;486 i += 1;
487 }
488 if (i < headers.len) {
489 headers[i] = headers[i][n..];
490 continue;
491 }
492 if (n == 0) break;
493 offset += n;
487 }494 }
488 if (n >= len.int()) {495 } else {
489 n -= len.int();496 var len = options.len.int();
490 while (n >= headers_and_trailers[i].len) {497 var i: usize = 0;
491 n -= headers_and_trailers[i].len;498 var offset = options.offset;
499 while (true) {
500 var n = try writeFile(bw, file, offset, .init(len), headers_and_trailers[i..], headers.len - i);
501 while (i < headers.len and n >= headers[i].len) {
502 n -= headers[i].len;
492 i += 1;503 i += 1;
504 }
505 if (i < headers.len) {
506 headers[i] = headers[i][n..];
507 continue;
508 }
509 if (n >= len) {
510 n -= len;
493 if (i >= headers_and_trailers.len) return;511 if (i >= headers_and_trailers.len) return;
512 while (n >= headers_and_trailers[i].len) {
513 n -= headers_and_trailers[i].len;
514 i += 1;
515 if (i >= headers_and_trailers.len) return;
516 }
517 headers_and_trailers[i] = headers_and_trailers[i][n..];
518 return writevAll(bw, headers_and_trailers[i..]);
494 }519 }
495 headers_and_trailers[i] = headers_and_trailers[i][n..];520 offset += n;
496 return writevAll(bw, headers_and_trailers[i..]);521 len -= n;
497 }522 }
498 offset += n;
499 len = if (len == .entire_file) .entire_file else .init(len.int() - n);
500 }523 }
501}524}
502525
lib/std/io/Writer.zig+1-1
...@@ -47,7 +47,7 @@ pub const VTable = struct {...@@ -47,7 +47,7 @@ pub const VTable = struct {
4747
48 pub fn init(integer: u64) FileLen {48 pub fn init(integer: u64) FileLen {
49 const result: FileLen = @enumFromInt(integer);49 const result: FileLen = @enumFromInt(integer);
50 assert(result != .none);50 assert(result != .entire_file);
51 return result;51 return result;
52 }52 }
5353