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,
err: ?ReadError = null,
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,
size: ?u64 = 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) {
error.Unseekable => {
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;
return error.ReadFailed;
};
}
return 0;
},
else => |e| {
@ -1535,10 +1541,14 @@ pub const Reader = struct {
const n = r.file.pread(dest, r.pos) catch |err| switch (err) {
error.Unseekable => {
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;
return error.ReadFailed;
};
}
return 0;
},
else => |e| {
@ -1586,6 +1596,8 @@ pub const Writer = struct {
file: File,
err: ?WriteError = null,
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,
sendfile_err: ?SendfileError = null,
copy_file_range_err: ?CopyFileRangeError = null,
@ -1652,7 +1664,9 @@ pub const Writer = struct {
const w: *Writer = @fieldParentPtr("interface", io_w);
const handle = w.file.handle;
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;
while (i < buffered.len) {
const n = windows.WriteFile(handle, buffered[i..], null) catch |err| {
@ -1677,7 +1691,9 @@ pub const Writer = struct {
};
w.pos += n;
return n;
}
},
.failure => return error.WriteFailed,
};
var iovecs: [max_buffers_len]std.posix.iovec_const = undefined;
var len: usize = 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| {
w.err = err;
return error.WriteFailed;
};
w.pos += n;
return io_w.consume(n);
},
.failure => return error.WriteFailed,
}
}
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) {
error.Unseekable => {
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;
return error.ReadFailed;
};
}
return 0;
},
else => |e| {
@ -1877,17 +1924,19 @@ pub const Writer = struct {
}
pub fn seekTo(w: *Writer, offset: u64) SeekError!void {
if (w.seek_err) |err| return err;
switch (w.mode) {
.positional, .positional_reading => {
w.pos = offset;
},
.streaming, .streaming_reading => {
if (w.seek_err) |err| return err;
posix.lseek_SET(w.file.handle, offset) catch |err| {
w.seek_err = err;
return err;
};
w.pos = offset;
},
.failure => return w.seek_err.?,
}
}