Commit 71ff6e0ef7
Changed files (7)
lib
std
lib/std/debug/SelfInfo/Elf.zig
@@ -354,6 +354,7 @@ const Module = struct {
error.LockedMemoryLimitExceeded,
error.ProcessFdQuotaExceeded,
error.SystemFdQuotaExceeded,
+ error.Streaming,
=> return error.ReadFailed,
};
errdefer elf_file.deinit(gpa);
lib/std/debug/ElfFile.zig
@@ -108,6 +108,7 @@ pub const LoadError = error{
LockedMemoryLimitExceeded,
ProcessFdQuotaExceeded,
SystemFdQuotaExceeded,
+ Streaming,
Canceled,
Unexpected,
};
@@ -409,7 +410,7 @@ fn loadInner(
arena: Allocator,
elf_file: std.fs.File,
opt_crc: ?u32,
-) (LoadError || error{ CrcMismatch, Canceled })!LoadInnerResult {
+) (LoadError || error{ CrcMismatch, Streaming, Canceled })!LoadInnerResult {
const mapped_mem: []align(std.heap.page_size_min) const u8 = mapped: {
const file_len = std.math.cast(
usize,
lib/std/Io/File.zig
@@ -71,6 +71,8 @@ pub const StatError = error{
/// not hold the required rights to get its filestat information.
AccessDenied,
PermissionDenied,
+ /// Attempted to stat a non-file stream.
+ Streaming,
} || Io.Cancelable || Io.UnexpectedError;
/// Returns `Stat` containing basic information about the `File`.
@@ -357,10 +359,6 @@ pub const Reader = struct {
setLogicalPos(r, @intCast(@as(i64, @intCast(logicalPos(r))) + offset));
},
.streaming, .streaming_reading => {
- if (std.posix.SEEK == void) {
- r.seek_err = error.Unseekable;
- return error.Unseekable;
- }
const seek_err = r.seek_err orelse e: {
if (io.vtable.fileSeekBy(io.userdata, r.file, offset)) |_| {
setLogicalPos(r, @intCast(@as(i64, @intCast(logicalPos(r))) + offset));
@@ -384,6 +382,7 @@ pub const Reader = struct {
}
}
+ /// Repositions logical read offset relative to the beginning of the file.
pub fn seekTo(r: *Reader, offset: u64) Reader.SeekError!void {
const io = r.io;
switch (r.mode) {
@@ -527,25 +526,65 @@ pub const Reader = struct {
const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader));
const io = r.io;
const file = r.file;
- const pos = r.pos;
switch (r.mode) {
.positional, .positional_reading => {
const size = r.getSize() catch {
r.mode = r.mode.toStreaming();
return 0;
};
- const delta = @min(@intFromEnum(limit), size - pos);
- r.pos = pos + delta;
+ const logical_pos = logicalPos(r);
+ const delta = @min(@intFromEnum(limit), size - logical_pos);
+ setLogicalPos(r, logical_pos + delta);
return delta;
},
.streaming, .streaming_reading => {
+ // Unfortunately we can't seek forward without knowing the
+ // size because the seek syscalls provided to us will not
+ // return the true end position if a seek would exceed the
+ // end.
+ fallback: {
+ if (r.size_err == null and r.seek_err == null) break :fallback;
+
+ const buffered_len = r.interface.bufferedLen();
+ var remaining = @intFromEnum(limit);
+ if (remaining <= buffered_len) {
+ r.interface.seek += remaining;
+ return remaining;
+ }
+ remaining -= buffered_len;
+ r.interface.seek = 0;
+ r.interface.end = 0;
+
+ var trash_buffer: [128]u8 = undefined;
+ var data: [1][]u8 = .{trash_buffer[0..@min(trash_buffer.len, remaining)]};
+ var iovecs_buffer: [max_buffers_len][]u8 = undefined;
+ const dest_n, const data_size = try r.interface.writableVector(&iovecs_buffer, &data);
+ const dest = iovecs_buffer[0..dest_n];
+ assert(dest[0].len > 0);
+ const n = io.vtable.fileReadStreaming(io.userdata, file, dest) catch |err| {
+ r.err = err;
+ return error.ReadFailed;
+ };
+ if (n == 0) {
+ r.size = r.pos;
+ return error.EndOfStream;
+ }
+ r.pos += n;
+ if (n > data_size) {
+ r.interface.end += n - data_size;
+ remaining -= data_size;
+ } else {
+ remaining -= n;
+ }
+ return @intFromEnum(limit) - remaining;
+ }
const size = r.getSize() catch return 0;
- const n = @min(size - pos, std.math.maxInt(i64), @intFromEnum(limit));
+ const n = @min(size - r.pos, std.math.maxInt(i64), @intFromEnum(limit));
io.vtable.fileSeekBy(io.userdata, file, n) catch |err| {
r.seek_err = err;
return 0;
};
- r.pos = pos + n;
+ r.pos += n;
return n;
},
.failure => return error.ReadFailed,
lib/std/Io/Threaded.zig
@@ -869,7 +869,6 @@ fn dirStatPathPosix(
const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
const flags: u32 = if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0;
- const fstatat_sym = if (posix.lfs64_abi) posix.system.fstatat64 else posix.system.fstatat;
while (true) {
try pool.checkCancel();
@@ -895,7 +894,9 @@ fn dirStatPathPosix(
fn fileStatPosix(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File.Stat {
const pool: *Pool = @ptrCast(@alignCast(userdata));
- const fstat_sym = if (posix.lfs64_abi) posix.system.fstat64 else posix.system.fstat;
+
+ if (posix.Stat == void) return error.Streaming;
+
while (true) {
try pool.checkCancel();
var stat = std.mem.zeroes(posix.Stat);
@@ -969,6 +970,10 @@ fn fileStatWasi(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File.
const have_flock = @TypeOf(posix.system.flock) != void;
const openat_sym = if (posix.lfs64_abi) posix.system.openat64 else posix.system.openat;
+const fstat_sym = if (posix.lfs64_abi) posix.system.fstat64 else posix.system.fstat;
+const fstatat_sym = if (posix.lfs64_abi) posix.system.fstatat64 else posix.system.fstatat;
+const lseek_sym = if (posix.lfs64_abi) posix.system.lseek64 else posix.system.lseek;
+const preadv_sym = if (posix.lfs64_abi) posix.system.preadv64 else posix.system.preadv;
fn dirCreateFilePosix(
userdata: ?*anyopaque,
@@ -1423,7 +1428,6 @@ fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset
}
};
- const preadv_sym = if (posix.lfs64_abi) posix.system.preadv64 else posix.system.preadv;
while (true) {
try pool.checkCancel();
const rc = preadv_sym(file.handle, dest.ptr, @intCast(dest.len), @bitCast(offset));
@@ -1461,11 +1465,59 @@ fn fileSeekBy(userdata: ?*anyopaque, file: Io.File, offset: i64) Io.File.SeekErr
fn fileSeekTo(userdata: ?*anyopaque, file: Io.File, offset: u64) Io.File.SeekError!void {
const pool: *Pool = @ptrCast(@alignCast(userdata));
- try pool.checkCancel();
+ const fd = file.handle;
- _ = file;
- _ = offset;
- @panic("TODO");
+ if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) while (true) {
+ try pool.checkCancel();
+ var result: u64 = undefined;
+ switch (posix.errno(posix.system.llseek(fd, offset, &result, posix.SEEK.SET))) {
+ .SUCCESS => return,
+ .INTR => continue,
+ .BADF => |err| return errnoBug(err), // Always a race condition.
+ .INVAL => return error.Unseekable,
+ .OVERFLOW => return error.Unseekable,
+ .SPIPE => return error.Unseekable,
+ .NXIO => return error.Unseekable,
+ else => |err| return posix.unexpectedErrno(err),
+ }
+ };
+
+ if (native_os == .windows) {
+ try pool.checkCancel();
+ return windows.SetFilePointerEx_BEGIN(fd, offset);
+ }
+
+ if (native_os == .wasi and !builtin.link_libc) while (true) {
+ try pool.checkCancel();
+ var new_offset: std.os.wasi.filesize_t = undefined;
+ switch (std.os.wasi.fd_seek(fd, @bitCast(offset), .SET, &new_offset)) {
+ .SUCCESS => return,
+ .INTR => continue,
+ .BADF => |err| return errnoBug(err), // Always a race condition.
+ .INVAL => return error.Unseekable,
+ .OVERFLOW => return error.Unseekable,
+ .SPIPE => return error.Unseekable,
+ .NXIO => return error.Unseekable,
+ .NOTCAPABLE => return error.AccessDenied,
+ else => |err| return posix.unexpectedErrno(err),
+ }
+ };
+
+ if (posix.SEEK == void) return error.Unseekable;
+
+ while (true) {
+ try pool.checkCancel();
+ switch (posix.errno(lseek_sym(fd, @bitCast(offset), posix.SEEK.SET))) {
+ .SUCCESS => return,
+ .INTR => continue,
+ .BADF => |err| return errnoBug(err), // Always a race condition.
+ .INVAL => return error.Unseekable,
+ .OVERFLOW => return error.Unseekable,
+ .SPIPE => return error.Unseekable,
+ .NXIO => return error.Unseekable,
+ else => |err| return posix.unexpectedErrno(err),
+ }
+ }
}
fn pwrite(userdata: ?*anyopaque, file: Io.File, buffer: []const u8, offset: posix.off_t) Io.File.PWriteError!usize {
lib/std/dynamic_library.zig
@@ -138,6 +138,7 @@ const ElfDynLibError = error{
ElfSymSectionNotFound,
ElfHashTableNotFound,
Canceled,
+ Streaming,
} || posix.OpenError || posix.MMapError;
pub const ElfDynLib = struct {
lib/std/Io.zig
@@ -666,8 +666,8 @@ pub const VTable = struct {
fileReadStreaming: *const fn (?*anyopaque, File, data: [][]u8) File.ReadStreamingError!usize,
/// Returns 0 on end of stream.
fileReadPositional: *const fn (?*anyopaque, File, data: [][]u8, offset: u64) File.ReadPositionalError!usize,
- fileSeekBy: *const fn (?*anyopaque, File, offset: i64) File.SeekError!void,
- fileSeekTo: *const fn (?*anyopaque, File, offset: u64) File.SeekError!void,
+ fileSeekBy: *const fn (?*anyopaque, File, relative_offset: i64) File.SeekError!void,
+ fileSeekTo: *const fn (?*anyopaque, File, absolute_offset: u64) File.SeekError!void,
now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp,
sleep: *const fn (?*anyopaque, Timeout) SleepError!void,
lib/std/posix.zig
@@ -488,6 +488,7 @@ fn fchmodat2(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtEr
error.NameTooLong => unreachable,
error.FileNotFound => unreachable,
error.InvalidUtf8 => unreachable,
+ error.Streaming => unreachable,
error.Canceled => return error.Canceled,
else => |e| return e,
};
@@ -5262,7 +5263,6 @@ pub fn gettimeofday(tv: ?*timeval, tz: ?*timezone) void {
pub const SeekError = std.Io.File.SeekError;
-/// Repositions read/write file offset relative to the beginning.
pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void {
if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
var result: u64 = undefined;