Skip to content

Commit ede4175

Browse files
committed
std.fs.File.writeFileAll: support unseekable files
With this commit, the function tries to use more efficient syscalls, and then falls back to non-positional reads. The motivating use case for this change is to support something like the following: try io.getStdOut().writeFileAll(dest_file, .{});
1 parent 37bc6ee commit ede4175

File tree

1 file changed

+46
-2
lines changed

1 file changed

+46
-2
lines changed

lib/std/fs/file.zig

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -690,10 +690,54 @@ pub const File = struct {
690690
header_count: usize = 0,
691691
};
692692

693-
pub const WriteFileError = os.SendFileError;
693+
pub const WriteFileError = ReadError || WriteError;
694694

695-
/// TODO integrate with async I/O
696695
pub fn writeFileAll(self: File, in_file: File, args: WriteFileOptions) WriteFileError!void {
696+
return self.writeFileAllSendfile(in_file, args) catch |err| switch (err) {
697+
error.Unseekable,
698+
error.FastOpenAlreadyInProgress,
699+
error.MessageTooBig,
700+
error.FileDescriptorNotASocket,
701+
=> return self.writeFileAllUnseekable(in_file, args),
702+
703+
else => |e| return e,
704+
};
705+
}
706+
707+
/// Does not try seeking in either of the File parameters.
708+
/// See `writeFileAll` as an alternative to calling this.
709+
pub fn writeFileAllUnseekable(self: File, in_file: File, args: WriteFileOptions) WriteFileError!void {
710+
const headers = args.headers_and_trailers[0..args.header_count];
711+
const trailers = args.headers_and_trailers[args.header_count..];
712+
713+
try self.writevAll(headers);
714+
715+
var buffer: [4096]u8 = undefined;
716+
{
717+
var index: usize = 0;
718+
// Skip in_offset bytes.
719+
while (index < args.in_offset) {
720+
const ask = math.min(buffer.len, args.in_offset - index);
721+
const amt = try in_file.read(buffer[0..ask]);
722+
index += amt;
723+
}
724+
}
725+
const in_len = args.in_len orelse math.maxInt(u64);
726+
var index: usize = 0;
727+
while (index < in_len) {
728+
const ask = math.min(buffer.len, in_len - index);
729+
const amt = try in_file.read(buffer[0..ask]);
730+
if (amt == 0) break;
731+
index += try self.write(buffer[0..amt]);
732+
}
733+
734+
try self.writevAll(trailers);
735+
}
736+
737+
/// Low level function which can fail for OS-specific reasons.
738+
/// See `writeFileAll` as an alternative to calling this.
739+
/// TODO integrate with async I/O
740+
fn writeFileAllSendfile(self: File, in_file: File, args: WriteFileOptions) os.SendFileError!void {
697741
const count = blk: {
698742
if (args.in_len) |l| {
699743
if (l == 0) {

0 commit comments

Comments
 (0)