Commit ede41759be

Andrew Kelley <andrew@ziglang.org>
2020-11-03 02:46:17
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
Changed files (1)
lib
std
lib/std/fs/file.zig
@@ -690,10 +690,54 @@ pub const File = struct {
         header_count: usize = 0,
     };
 
-    pub const WriteFileError = os.SendFileError;
+    pub const WriteFileError = ReadError || WriteError;
 
-    /// TODO integrate with async I/O
     pub fn writeFileAll(self: File, in_file: File, args: WriteFileOptions) WriteFileError!void {
+        return self.writeFileAllSendfile(in_file, args) catch |err| switch (err) {
+            error.Unseekable,
+            error.FastOpenAlreadyInProgress,
+            error.MessageTooBig,
+            error.FileDescriptorNotASocket,
+            => return self.writeFileAllUnseekable(in_file, args),
+
+            else => |e| return e,
+        };
+    }
+
+    /// Does not try seeking in either of the File parameters.
+    /// See `writeFileAll` as an alternative to calling this.
+    pub fn writeFileAllUnseekable(self: File, in_file: File, args: WriteFileOptions) WriteFileError!void {
+        const headers = args.headers_and_trailers[0..args.header_count];
+        const trailers = args.headers_and_trailers[args.header_count..];
+
+        try self.writevAll(headers);
+
+        var buffer: [4096]u8 = undefined;
+        {
+            var index: usize = 0;
+            // Skip in_offset bytes.
+            while (index < args.in_offset) {
+                const ask = math.min(buffer.len, args.in_offset - index);
+                const amt = try in_file.read(buffer[0..ask]);
+                index += amt;
+            }
+        }
+        const in_len = args.in_len orelse math.maxInt(u64);
+        var index: usize = 0;
+        while (index < in_len) {
+            const ask = math.min(buffer.len, in_len - index);
+            const amt = try in_file.read(buffer[0..ask]);
+            if (amt == 0) break;
+            index += try self.write(buffer[0..amt]);
+        }
+
+        try self.writevAll(trailers);
+    }
+
+    /// Low level function which can fail for OS-specific reasons.
+    /// See `writeFileAll` as an alternative to calling this.
+    /// TODO integrate with async I/O
+    fn writeFileAllSendfile(self: File, in_file: File, args: WriteFileOptions) os.SendFileError!void {
         const count = blk: {
             if (args.in_len) |l| {
                 if (l == 0) {