| author | |
| committer | |
| log | 71ff6e0ef748fbc78cd469317f546c99c0dc5074 |
| tree | 3f2d60e4af02b30b0ce81452d5507c3d1f37a518 |
| parent | d40803284e6395e7a4a065a73c9fb09d1c0ff7a8 |
7 files changed, 114 insertions(+), 20 deletions(-)
lib/std/Io.zig+2-2| ... | @@ -666,8 +666,8 @@ pub const VTable = struct { | ... | @@ -666,8 +666,8 @@ pub const VTable = struct { |
| 666 | fileReadStreaming: *const fn (?*anyopaque, File, data: [][]u8) File.ReadStreamingError!usize, | 666 | fileReadStreaming: *const fn (?*anyopaque, File, data: [][]u8) File.ReadStreamingError!usize, |
| 667 | /// Returns 0 on end of stream. | 667 | /// Returns 0 on end of stream. |
| 668 | fileReadPositional: *const fn (?*anyopaque, File, data: [][]u8, offset: u64) File.ReadPositionalError!usize, | 668 | fileReadPositional: *const fn (?*anyopaque, File, data: [][]u8, offset: u64) File.ReadPositionalError!usize, |
| 669 | fileSeekBy: *const fn (?*anyopaque, File, offset: i64) File.SeekError!void, | 669 | fileSeekBy: *const fn (?*anyopaque, File, relative_offset: i64) File.SeekError!void, |
| 670 | fileSeekTo: *const fn (?*anyopaque, File, offset: u64) File.SeekError!void, | 670 | fileSeekTo: *const fn (?*anyopaque, File, absolute_offset: u64) File.SeekError!void, |
| 671 | 671 | ||
| 672 | now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp, | 672 | now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp, |
| 673 | sleep: *const fn (?*anyopaque, Timeout) SleepError!void, | 673 | sleep: *const fn (?*anyopaque, Timeout) SleepError!void, |
lib/std/Io/File.zig+48-9| ... | @@ -71,6 +71,8 @@ pub const StatError = error{ | ... | @@ -71,6 +71,8 @@ pub const StatError = error{ |
| 71 | /// not hold the required rights to get its filestat information. | 71 | /// not hold the required rights to get its filestat information. |
| 72 | AccessDenied, | 72 | AccessDenied, |
| 73 | PermissionDenied, | 73 | PermissionDenied, |
| 74 | /// Attempted to stat a non-file stream. | ||
| 75 | Streaming, | ||
| 74 | } || Io.Cancelable || Io.UnexpectedError; | 76 | } || Io.Cancelable || Io.UnexpectedError; |
| 75 | 77 | ||
| 76 | /// Returns `Stat` containing basic information about the `File`. | 78 | /// Returns `Stat` containing basic information about the `File`. |
| ... | @@ -357,10 +359,6 @@ pub const Reader = struct { | ... | @@ -357,10 +359,6 @@ pub const Reader = struct { |
| 357 | setLogicalPos(r, @intCast(@as(i64, @intCast(logicalPos(r))) + offset)); | 359 | setLogicalPos(r, @intCast(@as(i64, @intCast(logicalPos(r))) + offset)); |
| 358 | }, | 360 | }, |
| 359 | .streaming, .streaming_reading => { | 361 | .streaming, .streaming_reading => { |
| 360 | if (std.posix.SEEK == void) { | ||
| 361 | r.seek_err = error.Unseekable; | ||
| 362 | return error.Unseekable; | ||
| 363 | } | ||
| 364 | const seek_err = r.seek_err orelse e: { | 362 | const seek_err = r.seek_err orelse e: { |
| 365 | if (io.vtable.fileSeekBy(io.userdata, r.file, offset)) |_| { | 363 | if (io.vtable.fileSeekBy(io.userdata, r.file, offset)) |_| { |
| 366 | setLogicalPos(r, @intCast(@as(i64, @intCast(logicalPos(r))) + offset)); | 364 | setLogicalPos(r, @intCast(@as(i64, @intCast(logicalPos(r))) + offset)); |
| ... | @@ -384,6 +382,7 @@ pub const Reader = struct { | ... | @@ -384,6 +382,7 @@ pub const Reader = struct { |
| 384 | } | 382 | } |
| 385 | } | 383 | } |
| 386 | 384 | ||
| 385 | /// Repositions logical read offset relative to the beginning of the file. | ||
| 387 | pub fn seekTo(r: *Reader, offset: u64) Reader.SeekError!void { | 386 | pub fn seekTo(r: *Reader, offset: u64) Reader.SeekError!void { |
| 388 | const io = r.io; | 387 | const io = r.io; |
| 389 | switch (r.mode) { | 388 | switch (r.mode) { |
| ... | @@ -527,25 +526,65 @@ pub const Reader = struct { | ... | @@ -527,25 +526,65 @@ pub const Reader = struct { |
| 527 | const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader)); | 526 | const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader)); |
| 528 | const io = r.io; | 527 | const io = r.io; |
| 529 | const file = r.file; | 528 | const file = r.file; |
| 530 | const pos = r.pos; | ||
| 531 | switch (r.mode) { | 529 | switch (r.mode) { |
| 532 | .positional, .positional_reading => { | 530 | .positional, .positional_reading => { |
| 533 | const size = r.getSize() catch { | 531 | const size = r.getSize() catch { |
| 534 | r.mode = r.mode.toStreaming(); | 532 | r.mode = r.mode.toStreaming(); |
| 535 | return 0; | 533 | return 0; |
| 536 | }; | 534 | }; |
| 537 | const delta = @min(@intFromEnum(limit), size - pos); | 535 | const logical_pos = logicalPos(r); |
| 538 | r.pos = pos + delta; | 536 | const delta = @min(@intFromEnum(limit), size - logical_pos); |
| 537 | setLogicalPos(r, logical_pos + delta); | ||
| 539 | return delta; | 538 | return delta; |
| 540 | }, | 539 | }, |
| 541 | .streaming, .streaming_reading => { | 540 | .streaming, .streaming_reading => { |
| 541 | // Unfortunately we can't seek forward without knowing the | ||
| 542 | // size because the seek syscalls provided to us will not | ||
| 543 | // return the true end position if a seek would exceed the | ||
| 544 | // end. | ||
| 545 | fallback: { | ||
| 546 | if (r.size_err == null and r.seek_err == null) break :fallback; | ||
| 547 | |||
| 548 | const buffered_len = r.interface.bufferedLen(); | ||
| 549 | var remaining = @intFromEnum(limit); | ||
| 550 | if (remaining <= buffered_len) { | ||
| 551 | r.interface.seek += remaining; | ||
| 552 | return remaining; | ||
| 553 | } | ||
| 554 | remaining -= buffered_len; | ||
| 555 | r.interface.seek = 0; | ||
| 556 | r.interface.end = 0; | ||
| 557 | |||
| 558 | var trash_buffer: [128]u8 = undefined; | ||
| 559 | var data: [1][]u8 = .{trash_buffer[0..@min(trash_buffer.len, remaining)]}; | ||
| 560 | var iovecs_buffer: [max_buffers_len][]u8 = undefined; | ||
| 561 | const dest_n, const data_size = try r.interface.writableVector(&iovecs_buffer, &data); | ||
| 562 | const dest = iovecs_buffer[0..dest_n]; | ||
| 563 | assert(dest[0].len > 0); | ||
| 564 | const n = io.vtable.fileReadStreaming(io.userdata, file, dest) catch |err| { | ||
| 565 | r.err = err; | ||
| 566 | return error.ReadFailed; | ||
| 567 | }; | ||
| 568 | if (n == 0) { | ||
| 569 | r.size = r.pos; | ||
| 570 | return error.EndOfStream; | ||
| 571 | } | ||
| 572 | r.pos += n; | ||
| 573 | if (n > data_size) { | ||
| 574 | r.interface.end += n - data_size; | ||
| 575 | remaining -= data_size; | ||
| 576 | } else { | ||
| 577 | remaining -= n; | ||
| 578 | } | ||
| 579 | return @intFromEnum(limit) - remaining; | ||
| 580 | } | ||
| 542 | const size = r.getSize() catch return 0; | 581 | const size = r.getSize() catch return 0; |
| 543 | const n = @min(size - pos, std.math.maxInt(i64), @intFromEnum(limit)); | 582 | const n = @min(size - r.pos, std.math.maxInt(i64), @intFromEnum(limit)); |
| 544 | io.vtable.fileSeekBy(io.userdata, file, n) catch |err| { | 583 | io.vtable.fileSeekBy(io.userdata, file, n) catch |err| { |
| 545 | r.seek_err = err; | 584 | r.seek_err = err; |
| 546 | return 0; | 585 | return 0; |
| 547 | }; | 586 | }; |
| 548 | r.pos = pos + n; | 587 | r.pos += n; |
| 549 | return n; | 588 | return n; |
| 550 | }, | 589 | }, |
| 551 | .failure => return error.ReadFailed, | 590 | .failure => return error.ReadFailed, |
lib/std/Io/Threaded.zig+59-7| ... | @@ -869,7 +869,6 @@ fn dirStatPathPosix( | ... | @@ -869,7 +869,6 @@ fn dirStatPathPosix( |
| 869 | const sub_path_posix = try pathToPosix(sub_path, &path_buffer); | 869 | const sub_path_posix = try pathToPosix(sub_path, &path_buffer); |
| 870 | 870 | ||
| 871 | const flags: u32 = if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0; | 871 | const flags: u32 = if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0; |
| 872 | const fstatat_sym = if (posix.lfs64_abi) posix.system.fstatat64 else posix.system.fstatat; | ||
| 873 | 872 | ||
| 874 | while (true) { | 873 | while (true) { |
| 875 | try pool.checkCancel(); | 874 | try pool.checkCancel(); |
| ... | @@ -895,7 +894,9 @@ fn dirStatPathPosix( | ... | @@ -895,7 +894,9 @@ fn dirStatPathPosix( |
| 895 | 894 | ||
| 896 | fn fileStatPosix(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File.Stat { | 895 | fn fileStatPosix(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File.Stat { |
| 897 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | 896 | const pool: *Pool = @ptrCast(@alignCast(userdata)); |
| 898 | const fstat_sym = if (posix.lfs64_abi) posix.system.fstat64 else posix.system.fstat; | 897 | |
| 898 | if (posix.Stat == void) return error.Streaming; | ||
| 899 | |||
| 899 | while (true) { | 900 | while (true) { |
| 900 | try pool.checkCancel(); | 901 | try pool.checkCancel(); |
| 901 | var stat = std.mem.zeroes(posix.Stat); | 902 | var stat = std.mem.zeroes(posix.Stat); |
| ... | @@ -969,6 +970,10 @@ fn fileStatWasi(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File. | ... | @@ -969,6 +970,10 @@ fn fileStatWasi(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File. |
| 969 | 970 | ||
| 970 | const have_flock = @TypeOf(posix.system.flock) != void; | 971 | const have_flock = @TypeOf(posix.system.flock) != void; |
| 971 | const openat_sym = if (posix.lfs64_abi) posix.system.openat64 else posix.system.openat; | 972 | const openat_sym = if (posix.lfs64_abi) posix.system.openat64 else posix.system.openat; |
| 973 | const fstat_sym = if (posix.lfs64_abi) posix.system.fstat64 else posix.system.fstat; | ||
| 974 | const fstatat_sym = if (posix.lfs64_abi) posix.system.fstatat64 else posix.system.fstatat; | ||
| 975 | const lseek_sym = if (posix.lfs64_abi) posix.system.lseek64 else posix.system.lseek; | ||
| 976 | const preadv_sym = if (posix.lfs64_abi) posix.system.preadv64 else posix.system.preadv; | ||
| 972 | 977 | ||
| 973 | fn dirCreateFilePosix( | 978 | fn dirCreateFilePosix( |
| 974 | userdata: ?*anyopaque, | 979 | userdata: ?*anyopaque, |
| ... | @@ -1423,7 +1428,6 @@ fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset | ... | @@ -1423,7 +1428,6 @@ fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset |
| 1423 | } | 1428 | } |
| 1424 | }; | 1429 | }; |
| 1425 | 1430 | ||
| 1426 | const preadv_sym = if (posix.lfs64_abi) posix.system.preadv64 else posix.system.preadv; | ||
| 1427 | while (true) { | 1431 | while (true) { |
| 1428 | try pool.checkCancel(); | 1432 | try pool.checkCancel(); |
| 1429 | const rc = preadv_sym(file.handle, dest.ptr, @intCast(dest.len), @bitCast(offset)); | 1433 | 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 | ... | @@ -1461,11 +1465,59 @@ fn fileSeekBy(userdata: ?*anyopaque, file: Io.File, offset: i64) Io.File.SeekErr |
| 1461 | 1465 | ||
| 1462 | fn fileSeekTo(userdata: ?*anyopaque, file: Io.File, offset: u64) Io.File.SeekError!void { | 1466 | fn fileSeekTo(userdata: ?*anyopaque, file: Io.File, offset: u64) Io.File.SeekError!void { |
| 1463 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | 1467 | const pool: *Pool = @ptrCast(@alignCast(userdata)); |
| 1464 | try pool.checkCancel(); | 1468 | const fd = file.handle; |
| 1465 | 1469 | ||
| 1466 | _ = file; | 1470 | if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) while (true) { |
| 1467 | _ = offset; | 1471 | try pool.checkCancel(); |
| 1468 | @panic("TODO"); | 1472 | var result: u64 = undefined; |
| 1473 | switch (posix.errno(posix.system.llseek(fd, offset, &result, posix.SEEK.SET))) { | ||
| 1474 | .SUCCESS => return, | ||
| 1475 | .INTR => continue, | ||
| 1476 | .BADF => |err| return errnoBug(err), // Always a race condition. | ||
| 1477 | .INVAL => return error.Unseekable, | ||
| 1478 | .OVERFLOW => return error.Unseekable, | ||
| 1479 | .SPIPE => return error.Unseekable, | ||
| 1480 | .NXIO => return error.Unseekable, | ||
| 1481 | else => |err| return posix.unexpectedErrno(err), | ||
| 1482 | } | ||
| 1483 | }; | ||
| 1484 | |||
| 1485 | if (native_os == .windows) { | ||
| 1486 | try pool.checkCancel(); | ||
| 1487 | return windows.SetFilePointerEx_BEGIN(fd, offset); | ||
| 1488 | } | ||
| 1489 | |||
| 1490 | if (native_os == .wasi and !builtin.link_libc) while (true) { | ||
| 1491 | try pool.checkCancel(); | ||
| 1492 | var new_offset: std.os.wasi.filesize_t = undefined; | ||
| 1493 | switch (std.os.wasi.fd_seek(fd, @bitCast(offset), .SET, &new_offset)) { | ||
| 1494 | .SUCCESS => return, | ||
| 1495 | .INTR => continue, | ||
| 1496 | .BADF => |err| return errnoBug(err), // Always a race condition. | ||
| 1497 | .INVAL => return error.Unseekable, | ||
| 1498 | .OVERFLOW => return error.Unseekable, | ||
| 1499 | .SPIPE => return error.Unseekable, | ||
| 1500 | .NXIO => return error.Unseekable, | ||
| 1501 | .NOTCAPABLE => return error.AccessDenied, | ||
| 1502 | else => |err| return posix.unexpectedErrno(err), | ||
| 1503 | } | ||
| 1504 | }; | ||
| 1505 | |||
| 1506 | if (posix.SEEK == void) return error.Unseekable; | ||
| 1507 | |||
| 1508 | while (true) { | ||
| 1509 | try pool.checkCancel(); | ||
| 1510 | switch (posix.errno(lseek_sym(fd, @bitCast(offset), posix.SEEK.SET))) { | ||
| 1511 | .SUCCESS => return, | ||
| 1512 | .INTR => continue, | ||
| 1513 | .BADF => |err| return errnoBug(err), // Always a race condition. | ||
| 1514 | .INVAL => return error.Unseekable, | ||
| 1515 | .OVERFLOW => return error.Unseekable, | ||
| 1516 | .SPIPE => return error.Unseekable, | ||
| 1517 | .NXIO => return error.Unseekable, | ||
| 1518 | else => |err| return posix.unexpectedErrno(err), | ||
| 1519 | } | ||
| 1520 | } | ||
| 1469 | } | 1521 | } |
| 1470 | 1522 | ||
| 1471 | fn pwrite(userdata: ?*anyopaque, file: Io.File, buffer: []const u8, offset: posix.off_t) Io.File.PWriteError!usize { | 1523 | fn pwrite(userdata: ?*anyopaque, file: Io.File, buffer: []const u8, offset: posix.off_t) Io.File.PWriteError!usize { |
lib/std/debug/ElfFile.zig+2-1| ... | @@ -108,6 +108,7 @@ pub const LoadError = error{ | ... | @@ -108,6 +108,7 @@ pub const LoadError = error{ |
| 108 | LockedMemoryLimitExceeded, | 108 | LockedMemoryLimitExceeded, |
| 109 | ProcessFdQuotaExceeded, | 109 | ProcessFdQuotaExceeded, |
| 110 | SystemFdQuotaExceeded, | 110 | SystemFdQuotaExceeded, |
| 111 | Streaming, | ||
| 111 | Canceled, | 112 | Canceled, |
| 112 | Unexpected, | 113 | Unexpected, |
| 113 | }; | 114 | }; |
| ... | @@ -409,7 +410,7 @@ fn loadInner( | ... | @@ -409,7 +410,7 @@ fn loadInner( |
| 409 | arena: Allocator, | 410 | arena: Allocator, |
| 410 | elf_file: std.fs.File, | 411 | elf_file: std.fs.File, |
| 411 | opt_crc: ?u32, | 412 | opt_crc: ?u32, |
| 412 | ) (LoadError || error{ CrcMismatch, Canceled })!LoadInnerResult { | 413 | ) (LoadError || error{ CrcMismatch, Streaming, Canceled })!LoadInnerResult { |
| 413 | const mapped_mem: []align(std.heap.page_size_min) const u8 = mapped: { | 414 | const mapped_mem: []align(std.heap.page_size_min) const u8 = mapped: { |
| 414 | const file_len = std.math.cast( | 415 | const file_len = std.math.cast( |
| 415 | usize, | 416 | usize, |
lib/std/debug/SelfInfo/Elf.zig+1| ... | @@ -354,6 +354,7 @@ const Module = struct { | ... | @@ -354,6 +354,7 @@ const Module = struct { |
| 354 | error.LockedMemoryLimitExceeded, | 354 | error.LockedMemoryLimitExceeded, |
| 355 | error.ProcessFdQuotaExceeded, | 355 | error.ProcessFdQuotaExceeded, |
| 356 | error.SystemFdQuotaExceeded, | 356 | error.SystemFdQuotaExceeded, |
| 357 | error.Streaming, | ||
| 357 | => return error.ReadFailed, | 358 | => return error.ReadFailed, |
| 358 | }; | 359 | }; |
| 359 | errdefer elf_file.deinit(gpa); | 360 | errdefer elf_file.deinit(gpa); |
lib/std/dynamic_library.zig+1| ... | @@ -138,6 +138,7 @@ const ElfDynLibError = error{ | ... | @@ -138,6 +138,7 @@ const ElfDynLibError = error{ |
| 138 | ElfSymSectionNotFound, | 138 | ElfSymSectionNotFound, |
| 139 | ElfHashTableNotFound, | 139 | ElfHashTableNotFound, |
| 140 | Canceled, | 140 | Canceled, |
| 141 | Streaming, | ||
| 141 | } || posix.OpenError || posix.MMapError; | 142 | } || posix.OpenError || posix.MMapError; |
| 142 | 143 | ||
| 143 | pub const ElfDynLib = struct { | 144 | pub const ElfDynLib = struct { |
lib/std/posix.zig+1-1| ... | @@ -488,6 +488,7 @@ fn fchmodat2(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtEr | ... | @@ -488,6 +488,7 @@ fn fchmodat2(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtEr |
| 488 | error.NameTooLong => unreachable, | 488 | error.NameTooLong => unreachable, |
| 489 | error.FileNotFound => unreachable, | 489 | error.FileNotFound => unreachable, |
| 490 | error.InvalidUtf8 => unreachable, | 490 | error.InvalidUtf8 => unreachable, |
| 491 | error.Streaming => unreachable, | ||
| 491 | error.Canceled => return error.Canceled, | 492 | error.Canceled => return error.Canceled, |
| 492 | else => |e| return e, | 493 | else => |e| return e, |
| 493 | }; | 494 | }; |
| ... | @@ -5262,7 +5263,6 @@ pub fn gettimeofday(tv: ?*timeval, tz: ?*timezone) void { | ... | @@ -5262,7 +5263,6 @@ pub fn gettimeofday(tv: ?*timeval, tz: ?*timezone) void { |
| 5262 | 5263 | ||
| 5263 | pub const SeekError = std.Io.File.SeekError; | 5264 | pub const SeekError = std.Io.File.SeekError; |
| 5264 | 5265 | ||
| 5265 | /// Repositions read/write file offset relative to the beginning. | ||
| 5266 | pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void { | 5266 | pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void { |
| 5267 | if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { | 5267 | if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { |
| 5268 | var result: u64 = undefined; | 5268 | var result: u64 = undefined; |