writeFile should be split into two
This commit is contained in:
Andrew Kelley 2025-02-15 22:17:30 -08:00
parent 35824e4822
commit 1d0495678d
3 changed files with 76 additions and 33 deletions

View File

@ -1611,18 +1611,38 @@ const interface = struct {
fn writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize {
const file = opaqueToHandle(context);
var splat_buffer: [256]u8 = undefined;
if (is_windows) {
if (data.len == 1 and splat == 0) return 0;
return windows.WriteFile(file, data[0], null);
}
var iovecs_buffer: [max_buffers_len]std.posix.iovec_const = undefined;
const iovecs = iovecs_buffer[0..@min(iovecs_buffer.len, data.len)];
for (iovecs, data[0..iovecs.len]) |*v, d| v.* = .{
var iovecs: [max_buffers_len]std.posix.iovec_const = undefined;
var len: usize = @min(iovecs.len, data.len);
for (iovecs[0..len], data[0..len]) |*v, d| v.* = .{
.base = if (d.len == 0) "" else d.ptr, // OS sadly checks ptr addr before length.
.len = d.len,
};
const send_iovecs = if (splat == 0) iovecs[0 .. iovecs.len - 1] else iovecs;
return std.posix.writev(file, send_iovecs);
switch (splat) {
0 => return std.posix.writev(file, iovecs[0 .. len - 1]),
1 => return std.posix.writev(file, iovecs[0..len]),
else => {
const pattern = data[data.len - 1];
if (pattern.len == 1) {
const memset_len = @min(splat_buffer.len, splat);
const buf = splat_buffer[0..memset_len];
@memset(buf, pattern[0]);
iovecs[len - 1] = .{ .base = buf.ptr, .len = buf.len };
var remaining_splat = splat - buf.len;
while (remaining_splat > 0 and len < iovecs.len) {
iovecs[len] = .{ .base = &splat_buffer, .len = splat_buffer.len };
remaining_splat -= splat_buffer.len;
len += 1;
}
return std.posix.writev(file, iovecs[0..len]);
}
},
}
return std.posix.writev(file, iovecs[0..len]);
}
fn writeFile(

View File

@ -76,7 +76,7 @@ pub fn flush(bw: *BufferedWriter) anyerror!void {
/// The `data` parameter is mutable because this function needs to mutate the
/// fields in order to handle partial writes from `Writer.VTable.writev`.
pub fn writevAll(bw: *BufferedWriter, data: []const []const u8) anyerror!void {
pub fn writevAll(bw: *BufferedWriter, data: [][]const u8) anyerror!void {
var i: usize = 0;
while (true) {
var n = try passthru_writeSplat(bw, data[i..], 1);
@ -164,7 +164,7 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi
@branchHint(.likely);
@memset(buffer[end..new_end], pattern[0]);
bw.end = new_end;
return end - start_end;
return new_end - start_end;
}
buffers[0] = buffer[0..end];
buffers[1] = pattern;
@ -186,8 +186,8 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi
while (end < new_end) : (end += pattern.len) {
@memcpy(buffer[end..][0..pattern.len], pattern);
}
bw.end = end;
return end - start_end;
bw.end = new_end;
return new_end - start_end;
}
buffers[0] = buffer[0..end];
buffers[1] = pattern;
@ -471,32 +471,55 @@ pub const WriteFileOptions = struct {
pub fn writeFileAll(bw: *BufferedWriter, file: std.fs.File, options: WriteFileOptions) anyerror!void {
const headers_and_trailers = options.headers_and_trailers;
const headers = headers_and_trailers[0..options.headers_len];
var len = options.len;
var i: usize = 0;
var offset = options.offset;
if (len == .zero) return writevAll(bw, headers_and_trailers[i..]);
while (i < headers_and_trailers.len) {
var n = try writeFile(bw, file, offset, len, headers_and_trailers[i..], headers.len - i);
while (i < headers.len and n >= headers[i].len) {
n -= headers[i].len;
i += 1;
}
if (i < headers.len) {
headers[i] = headers[i][n..];
continue;
}
if (n >= len.int()) {
n -= len.int();
while (n >= headers_and_trailers[i].len) {
n -= headers_and_trailers[i].len;
if (options.len == .zero) return writevAll(bw, headers_and_trailers);
if (options.len == .entire_file) {
// When reading the whole file, we cannot include the trailers in the
// call that reads from the file handle, because we have no way to
// determine whether a partial write is past the end of the file or
// not.
var i: usize = 0;
var offset = options.offset;
while (true) {
var n = try writeFile(bw, file, offset, .entire_file, headers[i..], headers.len - i);
while (i < headers.len and n >= headers[i].len) {
n -= headers[i].len;
i += 1;
if (i >= headers_and_trailers.len) return;
}
headers_and_trailers[i] = headers_and_trailers[i][n..];
return writevAll(bw, headers_and_trailers[i..]);
if (i < headers.len) {
headers[i] = headers[i][n..];
continue;
}
if (n == 0) break;
offset += n;
}
} else {
var len = options.len.int();
var i: usize = 0;
var offset = options.offset;
while (true) {
var n = try writeFile(bw, file, offset, .init(len), headers_and_trailers[i..], headers.len - i);
while (i < headers.len and n >= headers[i].len) {
n -= headers[i].len;
i += 1;
}
if (i < headers.len) {
headers[i] = headers[i][n..];
continue;
}
if (n >= len) {
n -= len;
if (i >= headers_and_trailers.len) return;
while (n >= headers_and_trailers[i].len) {
n -= headers_and_trailers[i].len;
i += 1;
if (i >= headers_and_trailers.len) return;
}
headers_and_trailers[i] = headers_and_trailers[i][n..];
return writevAll(bw, headers_and_trailers[i..]);
}
offset += n;
len -= n;
}
offset += n;
len = if (len == .entire_file) .entire_file else .init(len.int() - n);
}
}

View File

@ -47,7 +47,7 @@ pub const VTable = struct {
pub fn init(integer: u64) FileLen {
const result: FileLen = @enumFromInt(integer);
assert(result != .none);
assert(result != .entire_file);
return result;
}