std.fs.File.Writer: implement positional writing

This commit is contained in:
Andrew Kelley 2025-07-06 16:58:48 -07:00
parent 7e2a26c0c4
commit fd7feed04b

View file

@ -1240,6 +1240,8 @@ pub const Reader = struct {
file: File, file: File,
err: ?ReadError = null, err: ?ReadError = null,
mode: Reader.Mode = .positional, mode: Reader.Mode = .positional,
/// Tracks the true seek position in the file. To obtain the logical
/// position, subtract the buffer size from this value.
pos: u64 = 0, pos: u64 = 0,
size: ?u64 = null, size: ?u64 = null,
size_err: ?GetEndPosError = null, size_err: ?GetEndPosError = null,
@ -1407,10 +1409,14 @@ pub const Reader = struct {
const n = posix.preadv(r.file.handle, dest, r.pos) catch |err| switch (err) { const n = posix.preadv(r.file.handle, dest, r.pos) catch |err| switch (err) {
error.Unseekable => { error.Unseekable => {
r.mode = r.mode.toStreaming(); r.mode = r.mode.toStreaming();
if (r.pos != 0) r.seekBy(@intCast(r.pos)) catch { const pos = r.pos;
if (pos != 0) {
r.pos = 0;
r.seekBy(@intCast(pos)) catch {
r.mode = .failure; r.mode = .failure;
return error.ReadFailed; return error.ReadFailed;
}; };
}
return 0; return 0;
}, },
else => |e| { else => |e| {
@ -1535,10 +1541,14 @@ pub const Reader = struct {
const n = r.file.pread(dest, r.pos) catch |err| switch (err) { const n = r.file.pread(dest, r.pos) catch |err| switch (err) {
error.Unseekable => { error.Unseekable => {
r.mode = r.mode.toStreaming(); r.mode = r.mode.toStreaming();
if (r.pos != 0) r.seekBy(@intCast(r.pos)) catch { const pos = r.pos;
if (pos != 0) {
r.pos = 0;
r.seekBy(@intCast(pos)) catch {
r.mode = .failure; r.mode = .failure;
return error.ReadFailed; return error.ReadFailed;
}; };
}
return 0; return 0;
}, },
else => |e| { else => |e| {
@ -1586,6 +1596,8 @@ pub const Writer = struct {
file: File, file: File,
err: ?WriteError = null, err: ?WriteError = null,
mode: Writer.Mode = .positional, mode: Writer.Mode = .positional,
/// Tracks the true seek position in the file. To obtain the logical
/// position, add the buffer size to this value.
pos: u64 = 0, pos: u64 = 0,
sendfile_err: ?SendfileError = null, sendfile_err: ?SendfileError = null,
copy_file_range_err: ?CopyFileRangeError = null, copy_file_range_err: ?CopyFileRangeError = null,
@ -1652,7 +1664,9 @@ pub const Writer = struct {
const w: *Writer = @fieldParentPtr("interface", io_w); const w: *Writer = @fieldParentPtr("interface", io_w);
const handle = w.file.handle; const handle = w.file.handle;
const buffered = io_w.buffered(); const buffered = io_w.buffered();
if (is_windows) { if (is_windows) switch (w.mode) {
.positional, .positional_reading => @panic("TODO"),
.streaming, .streaming_reading => {
var i: usize = 0; var i: usize = 0;
while (i < buffered.len) { while (i < buffered.len) {
const n = windows.WriteFile(handle, buffered[i..], null) catch |err| { const n = windows.WriteFile(handle, buffered[i..], null) catch |err| {
@ -1677,7 +1691,9 @@ pub const Writer = struct {
}; };
w.pos += n; w.pos += n;
return n; return n;
} },
.failure => return error.WriteFailed,
};
var iovecs: [max_buffers_len]std.posix.iovec_const = undefined; var iovecs: [max_buffers_len]std.posix.iovec_const = undefined;
var len: usize = 0; var len: usize = 0;
if (buffered.len > 0) { if (buffered.len > 0) {
@ -1733,12 +1749,39 @@ pub const Writer = struct {
}, },
}, },
} }
switch (w.mode) {
.positional, .positional_reading => {
const n = std.posix.pwritev(handle, iovecs[0..len], w.pos) catch |err| switch (err) {
error.Unseekable => {
w.mode = w.mode.toStreaming();
const pos = w.pos;
if (pos != 0) {
w.pos = 0;
w.seekTo(@intCast(pos)) catch {
w.mode = .failure;
return error.WriteFailed;
};
}
return 0;
},
else => |e| {
w.err = e;
return error.WriteFailed;
},
};
w.pos += n;
return io_w.consume(n);
},
.streaming, .streaming_reading => {
const n = std.posix.writev(handle, iovecs[0..len]) catch |err| { const n = std.posix.writev(handle, iovecs[0..len]) catch |err| {
w.err = err; w.err = err;
return error.WriteFailed; return error.WriteFailed;
}; };
w.pos += n; w.pos += n;
return io_w.consume(n); return io_w.consume(n);
},
.failure => return error.WriteFailed,
}
} }
pub fn sendFile( pub fn sendFile(
@ -1781,10 +1824,14 @@ pub const Writer = struct {
const n = std.os.linux.wrapped.sendfile(out_fd, in_fd, off_ptr, count) catch |err| switch (err) { const n = std.os.linux.wrapped.sendfile(out_fd, in_fd, off_ptr, count) catch |err| switch (err) {
error.Unseekable => { error.Unseekable => {
file_reader.mode = file_reader.mode.toStreaming(); file_reader.mode = file_reader.mode.toStreaming();
if (file_reader.pos != 0) file_reader.seekBy(@intCast(file_reader.pos)) catch { const pos = file_reader.pos;
if (pos != 0) {
file_reader.pos = 0;
file_reader.seekBy(@intCast(pos)) catch {
file_reader.mode = .failure; file_reader.mode = .failure;
return error.ReadFailed; return error.ReadFailed;
}; };
}
return 0; return 0;
}, },
else => |e| { else => |e| {
@ -1877,17 +1924,19 @@ pub const Writer = struct {
} }
pub fn seekTo(w: *Writer, offset: u64) SeekError!void { pub fn seekTo(w: *Writer, offset: u64) SeekError!void {
if (w.seek_err) |err| return err;
switch (w.mode) { switch (w.mode) {
.positional, .positional_reading => { .positional, .positional_reading => {
w.pos = offset; w.pos = offset;
}, },
.streaming, .streaming_reading => { .streaming, .streaming_reading => {
if (w.seek_err) |err| return err;
posix.lseek_SET(w.file.handle, offset) catch |err| { posix.lseek_SET(w.file.handle, offset) catch |err| {
w.seek_err = err; w.seek_err = err;
return err; return err;
}; };
w.pos = offset;
}, },
.failure => return w.seek_err.?,
} }
} }