feature. See also
. The project being documented here (as the example) is the Zig library itself.
Threaded.fileWriteFileStreaming
fn fileWriteFileStreaming(
userdata: ?*anyopaque,
file: File,
header: []const u8,
file_reader: *File.Reader,
limit: Io.Limit,
) File.Writer.WriteFileError!usize
File
Code
fn fileWriteFileStreaming(
userdata: ?*anyopaque,
file: File,
header: []const u8,
file_reader: *File.Reader,
limit: Io.Limit,
) File.Writer.WriteFileError!usize {
const t: *Threaded = @ptrCast(@alignCast(userdata));
const reader_buffered = file_reader.interface.buffered();
if (reader_buffered.len >= @backingInt(limit)) {
const n = try fileWriteStreaming(t, file, header, &.{limit.slice(reader_buffered)}, 1);
file_reader.interface.toss(n -| header.len);
return n;
}
const file_limit = @backingInt(limit) - reader_buffered.len;
const out_fd = file.handle;
const in_fd = file_reader.file.handle;
if (file_reader.size) |size| {
if (size - file_reader.pos == 0) {
if (reader_buffered.len != 0) {
const n = try fileWriteStreaming(t, file, header, &.{limit.slice(reader_buffered)}, 1);
file_reader.interface.toss(n -| header.len);
return n;
} else {
return error.EndOfStream;
}
}
}
if (native_os == .freebsd) sf: {
if (@atomicLoad(UseSendfile, &t.use_sendfile, .monotonic) == .disabled) break :sf;
const offset = std.math.cast(std.c.off_t, file_reader.pos) orelse break :sf;
var hdtr_data: std.c.sf_hdtr = undefined;
var headers: [2]posix.iovec_const = undefined;
var headers_i: u8 = 0;
if (header.len != 0) {
headers[headers_i] = .{ .base = header.ptr, .len = header.len };
headers_i += 1;
}
if (reader_buffered.len != 0) {
headers[headers_i] = .{ .base = reader_buffered.ptr, .len = reader_buffered.len };
headers_i += 1;
}
const hdtr: ?*std.c.sf_hdtr = if (headers_i == 0) null else b: {
hdtr_data = .{
.headers = &headers,
.hdr_cnt = headers_i,
.trailers = null,
.trl_cnt = 0,
};
break :b &hdtr_data;
};
var sbytes: std.c.off_t = 0;
const nbytes: usize = @min(file_limit, std.math.maxInt(usize));
const flags = 0;
const syscall: Syscall = try .start();
while (true) {
switch (posix.errno(std.c.sendfile(in_fd, out_fd, offset, nbytes, hdtr, &sbytes, flags))) {
.SUCCESS => {
syscall.finish();
break;
},
.INVAL, .OPNOTSUPP, .NOTSOCK, .NOSYS => {
// something else.
syscall.finish();
@atomicStore(UseSendfile, &t.use_sendfile, .disabled, .monotonic);
return 0;
},
.INTR, .BUSY => {
if (sbytes == 0) {
try syscall.checkCancel();
continue;
} else {
// effects, so it is better to report those side
// effects to the caller.
syscall.finish();
break;
}
},
.AGAIN => {
syscall.finish();
if (sbytes == 0) return error.WouldBlock;
break;
},
else => |e| {
syscall.finish();
assert(error.Unexpected == switch (e) {
.NOTCONN => return error.BrokenPipe,
.IO => return error.InputOutput,
.PIPE => return error.BrokenPipe,
.NOBUFS => return error.SystemResources,
.BADF => |err| errnoBug(err),
.FAULT => |err| errnoBug(err),
else => |err| posix.unexpectedErrno(err),
});
// something else.
@atomicStore(UseSendfile, &t.use_sendfile, .disabled, .monotonic);
return 0;
},
}
}
if (sbytes == 0) {
file_reader.size = file_reader.pos;
return error.EndOfStream;
}
const ubytes: usize = @intCast(sbytes);
file_reader.interface.toss(ubytes -| header.len);
return ubytes;
}
if (is_darwin) sf: {
if (@atomicLoad(UseSendfile, &t.use_sendfile, .monotonic) == .disabled) break :sf;
const offset = std.math.cast(std.c.off_t, file_reader.pos) orelse break :sf;
var hdtr_data: std.c.sf_hdtr = undefined;
var headers: [2]posix.iovec_const = undefined;
var headers_i: u8 = 0;
if (header.len != 0) {
headers[headers_i] = .{ .base = header.ptr, .len = header.len };
headers_i += 1;
}
if (reader_buffered.len != 0) {
headers[headers_i] = .{ .base = reader_buffered.ptr, .len = reader_buffered.len };
headers_i += 1;
}
const hdtr: ?*std.c.sf_hdtr = if (headers_i == 0) null else b: {
hdtr_data = .{
.headers = &headers,
.hdr_cnt = headers_i,
.trailers = null,
.trl_cnt = 0,
};
break :b &hdtr_data;
};
const max_count = std.math.maxInt(i32);
var len: std.c.off_t = @min(file_limit, max_count);
const flags = 0;
const syscall: Syscall = try .start();
while (true) {
switch (posix.errno(std.c.sendfile(in_fd, out_fd, offset, &len, hdtr, flags))) {
.SUCCESS => {
syscall.finish();
break;
},
.OPNOTSUPP, .NOTSOCK, .NOSYS => {
// something else.
syscall.finish();
@atomicStore(UseSendfile, &t.use_sendfile, .disabled, .monotonic);
return 0;
},
.INTR => {
if (len == 0) {
try syscall.checkCancel();
continue;
} else {
// effects, so it is better to report those side
// effects to the caller.
syscall.finish();
break;
}
},
.AGAIN => {
syscall.finish();
if (len == 0) return error.WouldBlock;
break;
},
else => |e| {
syscall.finish();
assert(error.Unexpected == switch (e) {
.NOTCONN => return error.BrokenPipe,
.IO => return error.InputOutput,
.PIPE => return error.BrokenPipe,
.BADF => |err| errnoBug(err),
.FAULT => |err| errnoBug(err),
.INVAL => |err| errnoBug(err),
else => |err| posix.unexpectedErrno(err),
});
// something else.
@atomicStore(UseSendfile, &t.use_sendfile, .disabled, .monotonic);
return 0;
},
}
}
if (len == 0) {
file_reader.size = file_reader.pos;
return error.EndOfStream;
}
const u_len: usize = @bitCast(len);
file_reader.interface.toss(u_len -| header.len);
return u_len;
}
if (native_os == .linux) sf: {
if (@atomicLoad(UseSendfile, &t.use_sendfile, .monotonic) == .disabled) break :sf;
if (header.len != 0 or reader_buffered.len != 0) {
const n = try fileWriteStreaming(t, file, header, &.{limit.slice(reader_buffered)}, 1);
file_reader.interface.toss(n -| header.len);
return n;
}
const max_count = 0x7ffff000;
var off: std.os.linux.off_t = undefined;
const off_ptr: ?*std.os.linux.off_t, const count: usize = switch (file_reader.mode) {
.positional => o: {
const size = file_reader.getSize() catch return 0;
off = std.math.cast(std.os.linux.off_t, file_reader.pos) orelse return error.ReadFailed;
break :o .{ &off, @min(@backingInt(limit), size - file_reader.pos, max_count) };
},
.streaming => .{ null, limit.minInt(max_count) },
.streaming_simple, .positional_simple => break :sf,
.failure => return error.ReadFailed,
};
const syscall: Syscall = try .start();
const n: usize = while (true) {
const rc = sendfile_sym(out_fd, in_fd, off_ptr, count);
switch (posix.errno(rc)) {
.SUCCESS => {
syscall.finish();
break @intCast(rc);
},
.NOSYS, .INVAL => {
// something else.
syscall.finish();
@atomicStore(UseSendfile, &t.use_sendfile, .disabled, .monotonic);
return 0;
},
.INTR => {
try syscall.checkCancel();
continue;
},
else => |e| {
syscall.finish();
assert(error.Unexpected == switch (e) {
.NOTCONN => return error.BrokenPipe,
.AGAIN => return error.WouldBlock,
.IO => return error.InputOutput,
.PIPE => return error.BrokenPipe,
.NOMEM => return error.SystemResources,
.NXIO, .SPIPE => {
file_reader.mode = file_reader.mode.toStreaming();
const pos = file_reader.pos;
if (pos != 0) {
file_reader.pos = 0;
file_reader.seekBy(@intCast(pos)) catch {
file_reader.mode = .failure;
return error.ReadFailed;
};
}
return 0;
},
.BADF => |err| errnoBug(err),
.FAULT => |err| errnoBug(err),
.OVERFLOW => |err| errnoBug(err),
else => |err| posix.unexpectedErrno(err),
});
// something else.
@atomicStore(UseSendfile, &t.use_sendfile, .disabled, .monotonic);
return 0;
},
}
};
if (n == 0) {
file_reader.size = file_reader.pos;
return error.EndOfStream;
}
file_reader.pos += n;
return n;
}
if (have_copy_file_range) cfr: {
if (@atomicLoad(UseCopyFileRange, &t.use_copy_file_range, .monotonic) == .disabled) break :cfr;
if (header.len != 0 or reader_buffered.len != 0) {
const n = try fileWriteStreaming(t, file, header, &.{limit.slice(reader_buffered)}, 1);
file_reader.interface.toss(n -| header.len);
return n;
}
var len: usize = @backingInt(limit);
var off_in: i64 = undefined;
const off_in_ptr: ?*i64 = switch (file_reader.mode) {
.positional_simple, .streaming_simple => return error.Unimplemented,
.positional => p: {
len = @min(len, std.math.maxInt(usize) - file_reader.pos);
off_in = @intCast(file_reader.pos);
break :p &off_in;
},
.streaming => null,
.failure => return error.ReadFailed,
};
const n: usize = switch (native_os) {
.linux => n: {
const syscall: Syscall = try .start();
while (true) {
const rc = linux_copy_file_range_sys.copy_file_range(in_fd, off_in_ptr, out_fd, null, len, 0);
switch (linux_copy_file_range_sys.errno(rc)) {
.SUCCESS => {
syscall.finish();
break :n @intCast(rc);
},
.INTR => {
try syscall.checkCancel();
continue;
},
.OPNOTSUPP, .INVAL, .NOSYS => {
// something else.
syscall.finish();
@atomicStore(UseCopyFileRange, &t.use_copy_file_range, .disabled, .monotonic);
return 0;
},
else => |e| {
syscall.finish();
assert(error.Unexpected == switch (e) {
.FBIG => return error.FileTooBig,
.IO => return error.InputOutput,
.NOMEM => return error.SystemResources,
.NOSPC => return error.NoSpaceLeft,
.OVERFLOW => |err| errnoBug(err),
.PERM => return error.PermissionDenied,
.BUSY => return error.DeviceBusy,
.TXTBSY => return error.FileBusy,
// this pair of file descriptors.
.XDEV => return error.Unimplemented,
.ISDIR => |err| errnoBug(err),
.BADF => |err| errnoBug(err),
else => |err| posix.unexpectedErrno(err),
});
@atomicStore(UseCopyFileRange, &t.use_copy_file_range, .disabled, .monotonic);
return 0;
},
}
}
},
.freebsd => n: {
const syscall: Syscall = try .start();
while (true) {
const rc = std.c.copy_file_range(in_fd, off_in_ptr, out_fd, null, @backingInt(limit), 0);
switch (std.c.errno(rc)) {
.SUCCESS => {
syscall.finish();
break :n @intCast(rc);
},
.INTR => {
try syscall.checkCancel();
continue;
},
.OPNOTSUPP, .INVAL, .NOSYS => {
// something else.
syscall.finish();
@atomicStore(UseCopyFileRange, &t.use_copy_file_range, .disabled, .monotonic);
return 0;
},
else => |e| {
syscall.finish();
assert(error.Unexpected == switch (e) {
.FBIG => return error.FileTooBig,
.IO => return error.InputOutput,
.INTEGRITY => return error.CorruptedData,
.NOSPC => return error.NoSpaceLeft,
.ISDIR => |err| errnoBug(err),
.BADF => |err| errnoBug(err),
else => |err| posix.unexpectedErrno(err),
});
@atomicStore(UseCopyFileRange, &t.use_copy_file_range, .disabled, .monotonic);
return 0;
},
}
}
},
else => comptime unreachable,
};
if (n == 0) {
file_reader.size = file_reader.pos;
return error.EndOfStream;
}
file_reader.pos += n;
return n;
}
return error.Unimplemented;
}