| author | |
| committer | |
| log | 676f1b492ed8d311ff97335b4311302dfce9c0c8 |
| tree | dc0b0e15307a5857f3bcee1018722f3417d5e350 |
| parent | bdf463bee2becd84b83bb9d66725420e03680df8 |
8 files changed, 926 insertions(+), 314 deletions(-)
lib/std/Io.zig+22-65| ... | @@ -6,7 +6,6 @@ const windows = std.os.windows; | ... | @@ -6,7 +6,6 @@ const windows = std.os.windows; |
| 6 | const posix = std.posix; | 6 | const posix = std.posix; |
| 7 | const math = std.math; | 7 | const math = std.math; |
| 8 | const assert = std.debug.assert; | 8 | const assert = std.debug.assert; |
| 9 | const fs = std.fs; | ||
| 10 | const Allocator = std.mem.Allocator; | 9 | const Allocator = std.mem.Allocator; |
| 11 | const Alignment = std.mem.Alignment; | 10 | const Alignment = std.mem.Alignment; |
| 12 | 11 | ||
| ... | @@ -650,10 +649,15 @@ pub const VTable = struct { | ... | @@ -650,10 +649,15 @@ pub const VTable = struct { |
| 650 | conditionWake: *const fn (?*anyopaque, cond: *Condition, wake: Condition.Wake) void, | 649 | conditionWake: *const fn (?*anyopaque, cond: *Condition, wake: Condition.Wake) void, |
| 651 | 650 | ||
| 652 | createFile: *const fn (?*anyopaque, dir: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File, | 651 | createFile: *const fn (?*anyopaque, dir: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File, |
| 653 | openFile: *const fn (?*anyopaque, dir: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File, | 652 | fileOpen: *const fn (?*anyopaque, dir: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File, |
| 654 | closeFile: *const fn (?*anyopaque, File) void, | 653 | fileClose: *const fn (?*anyopaque, File) void, |
| 655 | pread: *const fn (?*anyopaque, file: File, buffer: []u8, offset: std.posix.off_t) File.PReadError!usize, | ||
| 656 | pwrite: *const fn (?*anyopaque, file: File, buffer: []const u8, offset: std.posix.off_t) File.PWriteError!usize, | 654 | pwrite: *const fn (?*anyopaque, file: File, buffer: []const u8, offset: std.posix.off_t) File.PWriteError!usize, |
| 655 | /// Returns 0 on end of stream. | ||
| 656 | fileReadStreaming: *const fn (?*anyopaque, file: File, data: [][]u8) File.ReadStreamingError!usize, | ||
| 657 | /// Returns 0 on end of stream. | ||
| 658 | fileReadPositional: *const fn (?*anyopaque, file: File, data: [][]u8, offset: u64) File.ReadPositionalError!usize, | ||
| 659 | fileSeekBy: *const fn (?*anyopaque, file: File, offset: i64) File.SeekError!void, | ||
| 660 | fileSeekTo: *const fn (?*anyopaque, file: File, offset: u64) File.SeekError!void, | ||
| 657 | 661 | ||
| 658 | now: *const fn (?*anyopaque, clockid: std.posix.clockid_t) ClockGetTimeError!Timestamp, | 662 | now: *const fn (?*anyopaque, clockid: std.posix.clockid_t) ClockGetTimeError!Timestamp, |
| 659 | sleep: *const fn (?*anyopaque, clockid: std.posix.clockid_t, deadline: Deadline) SleepError!void, | 663 | sleep: *const fn (?*anyopaque, clockid: std.posix.clockid_t, deadline: Deadline) SleepError!void, |
| ... | @@ -670,6 +674,18 @@ pub const Cancelable = error{ | ... | @@ -670,6 +674,18 @@ pub const Cancelable = error{ |
| 670 | Canceled, | 674 | Canceled, |
| 671 | }; | 675 | }; |
| 672 | 676 | ||
| 677 | pub const UnexpectedError = error{ | ||
| 678 | /// The Operating System returned an undocumented error code. | ||
| 679 | /// | ||
| 680 | /// This error is in theory not possible, but it would be better | ||
| 681 | /// to handle this error than to invoke undefined behavior. | ||
| 682 | /// | ||
| 683 | /// When this error code is observed, it usually means the Zig Standard | ||
| 684 | /// Library needs a small patch to add the error code to the error set for | ||
| 685 | /// the respective function. | ||
| 686 | Unexpected, | ||
| 687 | }; | ||
| 688 | |||
| 673 | pub const Dir = struct { | 689 | pub const Dir = struct { |
| 674 | handle: Handle, | 690 | handle: Handle, |
| 675 | 691 | ||
| ... | @@ -680,7 +696,7 @@ pub const Dir = struct { | ... | @@ -680,7 +696,7 @@ pub const Dir = struct { |
| 680 | pub const Handle = std.posix.fd_t; | 696 | pub const Handle = std.posix.fd_t; |
| 681 | 697 | ||
| 682 | pub fn openFile(dir: Dir, io: Io, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File { | 698 | pub fn openFile(dir: Dir, io: Io, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File { |
| 683 | return io.vtable.openFile(io.userdata, dir, sub_path, flags); | 699 | return io.vtable.fileOpen(io.userdata, dir, sub_path, flags); |
| 684 | } | 700 | } |
| 685 | 701 | ||
| 686 | pub fn createFile(dir: Dir, io: Io, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File { | 702 | pub fn createFile(dir: Dir, io: Io, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File { |
| ... | @@ -706,66 +722,7 @@ pub const Dir = struct { | ... | @@ -706,66 +722,7 @@ pub const Dir = struct { |
| 706 | } | 722 | } |
| 707 | }; | 723 | }; |
| 708 | 724 | ||
| 709 | pub const File = struct { | 725 | pub const File = @import("Io/File.zig"); |
| 710 | handle: Handle, | ||
| 711 | |||
| 712 | pub const Handle = std.posix.fd_t; | ||
| 713 | |||
| 714 | pub const OpenFlags = fs.File.OpenFlags; | ||
| 715 | pub const CreateFlags = fs.File.CreateFlags; | ||
| 716 | |||
| 717 | pub const OpenError = fs.File.OpenError || Cancelable; | ||
| 718 | |||
| 719 | pub fn close(file: File, io: Io) void { | ||
| 720 | return io.vtable.closeFile(io.userdata, file); | ||
| 721 | } | ||
| 722 | |||
| 723 | pub const ReadError = fs.File.ReadError || Cancelable; | ||
| 724 | |||
| 725 | pub fn read(file: File, io: Io, buffer: []u8) ReadError!usize { | ||
| 726 | return @errorCast(file.pread(io, buffer, -1)); | ||
| 727 | } | ||
| 728 | |||
| 729 | pub const PReadError = fs.File.PReadError || Cancelable; | ||
| 730 | |||
| 731 | pub fn pread(file: File, io: Io, buffer: []u8, offset: std.posix.off_t) PReadError!usize { | ||
| 732 | return io.vtable.pread(io.userdata, file, buffer, offset); | ||
| 733 | } | ||
| 734 | |||
| 735 | pub const WriteError = fs.File.WriteError || Cancelable; | ||
| 736 | |||
| 737 | pub fn write(file: File, io: Io, buffer: []const u8) WriteError!usize { | ||
| 738 | return @errorCast(file.pwrite(io, buffer, -1)); | ||
| 739 | } | ||
| 740 | |||
| 741 | pub const PWriteError = fs.File.PWriteError || Cancelable; | ||
| 742 | |||
| 743 | pub fn pwrite(file: File, io: Io, buffer: []const u8, offset: std.posix.off_t) PWriteError!usize { | ||
| 744 | return io.vtable.pwrite(io.userdata, file, buffer, offset); | ||
| 745 | } | ||
| 746 | |||
| 747 | pub fn writeAll(file: File, io: Io, bytes: []const u8) WriteError!void { | ||
| 748 | var index: usize = 0; | ||
| 749 | while (index < bytes.len) { | ||
| 750 | index += try file.write(io, bytes[index..]); | ||
| 751 | } | ||
| 752 | } | ||
| 753 | |||
| 754 | pub fn readAll(file: File, io: Io, buffer: []u8) ReadError!usize { | ||
| 755 | var index: usize = 0; | ||
| 756 | while (index != buffer.len) { | ||
| 757 | const amt = try file.read(io, buffer[index..]); | ||
| 758 | if (amt == 0) break; | ||
| 759 | index += amt; | ||
| 760 | } | ||
| 761 | return index; | ||
| 762 | } | ||
| 763 | |||
| 764 | pub fn openAbsolute(io: Io, absolute_path: []const u8, flags: OpenFlags) OpenError { | ||
| 765 | assert(std.fs.path.isAbsolute(absolute_path)); | ||
| 766 | return Dir.cwd().openFile(io, absolute_path, flags); | ||
| 767 | } | ||
| 768 | }; | ||
| 769 | 726 | ||
| 770 | pub const Timestamp = enum(i96) { | 727 | pub const Timestamp = enum(i96) { |
| 771 | _, | 728 | _, |
lib/std/Io/EventLoop.zig+28-28| ... | @@ -93,7 +93,7 @@ const Fiber = struct { | ... | @@ -93,7 +93,7 @@ const Fiber = struct { |
| 93 | } | 93 | } |
| 94 | 94 | ||
| 95 | fn resultPointer(f: *Fiber, comptime Result: type) *Result { | 95 | fn resultPointer(f: *Fiber, comptime Result: type) *Result { |
| 96 | return @alignCast(@ptrCast(f.resultBytes(.of(Result)))); | 96 | return @ptrCast(@alignCast(f.resultBytes(.of(Result)))); |
| 97 | } | 97 | } |
| 98 | 98 | ||
| 99 | fn resultBytes(f: *Fiber, alignment: Alignment) [*]u8 { | 99 | fn resultBytes(f: *Fiber, alignment: Alignment) [*]u8 { |
| ... | @@ -153,8 +153,8 @@ pub fn io(el: *EventLoop) Io { | ... | @@ -153,8 +153,8 @@ pub fn io(el: *EventLoop) Io { |
| 153 | .conditionWake = conditionWake, | 153 | .conditionWake = conditionWake, |
| 154 | 154 | ||
| 155 | .createFile = createFile, | 155 | .createFile = createFile, |
| 156 | .openFile = openFile, | 156 | .fileOpen = fileOpen, |
| 157 | .closeFile = closeFile, | 157 | .fileClose = fileClose, |
| 158 | .pread = pread, | 158 | .pread = pread, |
| 159 | .pwrite = pwrite, | 159 | .pwrite = pwrite, |
| 160 | 160 | ||
| ... | @@ -193,7 +193,7 @@ pub fn init(el: *EventLoop, gpa: Allocator) !void { | ... | @@ -193,7 +193,7 @@ pub fn init(el: *EventLoop, gpa: Allocator) !void { |
| 193 | }; | 193 | }; |
| 194 | const main_thread = &el.threads.allocated[0]; | 194 | const main_thread = &el.threads.allocated[0]; |
| 195 | Thread.self = main_thread; | 195 | Thread.self = main_thread; |
| 196 | const idle_stack_end: [*]align(16) usize = @alignCast(@ptrCast(allocated_slice[idle_stack_end_offset..].ptr)); | 196 | const idle_stack_end: [*]align(16) usize = @ptrCast(@alignCast(allocated_slice[idle_stack_end_offset..].ptr)); |
| 197 | (idle_stack_end - 1)[0..1].* = .{@intFromPtr(el)}; | 197 | (idle_stack_end - 1)[0..1].* = .{@intFromPtr(el)}; |
| 198 | main_thread.* = .{ | 198 | main_thread.* = .{ |
| 199 | .thread = undefined, | 199 | .thread = undefined, |
| ... | @@ -244,7 +244,7 @@ pub fn deinit(el: *EventLoop) void { | ... | @@ -244,7 +244,7 @@ pub fn deinit(el: *EventLoop) void { |
| 244 | assert(ready_fiber == null or ready_fiber == Fiber.finished); // pending async | 244 | assert(ready_fiber == null or ready_fiber == Fiber.finished); // pending async |
| 245 | } | 245 | } |
| 246 | el.yield(null, .exit); | 246 | el.yield(null, .exit); |
| 247 | const allocated_ptr: [*]align(@alignOf(Thread)) u8 = @alignCast(@ptrCast(el.threads.allocated.ptr)); | 247 | const allocated_ptr: [*]align(@alignOf(Thread)) u8 = @ptrCast(@alignCast(el.threads.allocated.ptr)); |
| 248 | const idle_stack_end_offset = std.mem.alignForward(usize, el.threads.allocated.len * @sizeOf(Thread) + idle_stack_size, std.heap.page_size_max); | 248 | const idle_stack_end_offset = std.mem.alignForward(usize, el.threads.allocated.len * @sizeOf(Thread) + idle_stack_size, std.heap.page_size_max); |
| 249 | for (el.threads.allocated[1..active_threads]) |*thread| thread.thread.join(); | 249 | for (el.threads.allocated[1..active_threads]) |*thread| thread.thread.join(); |
| 250 | el.gpa.free(allocated_ptr[0..idle_stack_end_offset]); | 250 | el.gpa.free(allocated_ptr[0..idle_stack_end_offset]); |
| ... | @@ -530,7 +530,7 @@ const SwitchMessage = struct { | ... | @@ -530,7 +530,7 @@ const SwitchMessage = struct { |
| 530 | const prev_fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.prev)); | 530 | const prev_fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.prev)); |
| 531 | assert(prev_fiber.queue_next == null); | 531 | assert(prev_fiber.queue_next == null); |
| 532 | for (futures) |any_future| { | 532 | for (futures) |any_future| { |
| 533 | const future_fiber: *Fiber = @alignCast(@ptrCast(any_future)); | 533 | const future_fiber: *Fiber = @ptrCast(@alignCast(any_future)); |
| 534 | if (@atomicRmw(?*Fiber, &future_fiber.awaiter, .Xchg, prev_fiber, .acq_rel) == Fiber.finished) { | 534 | if (@atomicRmw(?*Fiber, &future_fiber.awaiter, .Xchg, prev_fiber, .acq_rel) == Fiber.finished) { |
| 535 | const closure: *AsyncClosure = .fromFiber(future_fiber); | 535 | const closure: *AsyncClosure = .fromFiber(future_fiber); |
| 536 | if (!@atomicRmw(bool, &closure.already_awaited, .Xchg, true, .seq_cst)) { | 536 | if (!@atomicRmw(bool, &closure.already_awaited, .Xchg, true, .seq_cst)) { |
| ... | @@ -897,12 +897,12 @@ fn asyncConcurrent( | ... | @@ -897,12 +897,12 @@ fn asyncConcurrent( |
| 897 | assert(result_len <= Fiber.max_result_size); // TODO | 897 | assert(result_len <= Fiber.max_result_size); // TODO |
| 898 | assert(context.len <= Fiber.max_context_size); // TODO | 898 | assert(context.len <= Fiber.max_context_size); // TODO |
| 899 | 899 | ||
| 900 | const event_loop: *EventLoop = @alignCast(@ptrCast(userdata)); | 900 | const event_loop: *EventLoop = @ptrCast(@alignCast(userdata)); |
| 901 | const fiber = try Fiber.allocate(event_loop); | 901 | const fiber = try Fiber.allocate(event_loop); |
| 902 | std.log.debug("allocated {*}", .{fiber}); | 902 | std.log.debug("allocated {*}", .{fiber}); |
| 903 | 903 | ||
| 904 | const closure: *AsyncClosure = .fromFiber(fiber); | 904 | const closure: *AsyncClosure = .fromFiber(fiber); |
| 905 | const stack_end: [*]align(16) usize = @alignCast(@ptrCast(closure)); | 905 | const stack_end: [*]align(16) usize = @ptrCast(@alignCast(closure)); |
| 906 | (stack_end - 1)[0..1].* = .{@intFromPtr(&AsyncClosure.call)}; | 906 | (stack_end - 1)[0..1].* = .{@intFromPtr(&AsyncClosure.call)}; |
| 907 | fiber.* = .{ | 907 | fiber.* = .{ |
| 908 | .required_align = {}, | 908 | .required_align = {}, |
| ... | @@ -974,7 +974,7 @@ fn asyncDetached( | ... | @@ -974,7 +974,7 @@ fn asyncDetached( |
| 974 | assert(context_alignment.compare(.lte, Fiber.max_context_align)); // TODO | 974 | assert(context_alignment.compare(.lte, Fiber.max_context_align)); // TODO |
| 975 | assert(context.len <= Fiber.max_context_size); // TODO | 975 | assert(context.len <= Fiber.max_context_size); // TODO |
| 976 | 976 | ||
| 977 | const event_loop: *EventLoop = @alignCast(@ptrCast(userdata)); | 977 | const event_loop: *EventLoop = @ptrCast(@alignCast(userdata)); |
| 978 | const fiber = Fiber.allocate(event_loop) catch { | 978 | const fiber = Fiber.allocate(event_loop) catch { |
| 979 | start(context.ptr); | 979 | start(context.ptr); |
| 980 | return; | 980 | return; |
| ... | @@ -985,7 +985,7 @@ fn asyncDetached( | ... | @@ -985,7 +985,7 @@ fn asyncDetached( |
| 985 | const closure: *DetachedClosure = @ptrFromInt(Fiber.max_context_align.max(.of(DetachedClosure)).backward( | 985 | const closure: *DetachedClosure = @ptrFromInt(Fiber.max_context_align.max(.of(DetachedClosure)).backward( |
| 986 | @intFromPtr(fiber.allocatedEnd()) - Fiber.max_context_size, | 986 | @intFromPtr(fiber.allocatedEnd()) - Fiber.max_context_size, |
| 987 | ) - @sizeOf(DetachedClosure)); | 987 | ) - @sizeOf(DetachedClosure)); |
| 988 | const stack_end: [*]align(16) usize = @alignCast(@ptrCast(closure)); | 988 | const stack_end: [*]align(16) usize = @ptrCast(@alignCast(closure)); |
| 989 | (stack_end - 1)[0..1].* = .{@intFromPtr(&DetachedClosure.call)}; | 989 | (stack_end - 1)[0..1].* = .{@intFromPtr(&DetachedClosure.call)}; |
| 990 | fiber.* = .{ | 990 | fiber.* = .{ |
| 991 | .required_align = {}, | 991 | .required_align = {}, |
| ... | @@ -1035,8 +1035,8 @@ fn await( | ... | @@ -1035,8 +1035,8 @@ fn await( |
| 1035 | result: []u8, | 1035 | result: []u8, |
| 1036 | result_alignment: Alignment, | 1036 | result_alignment: Alignment, |
| 1037 | ) void { | 1037 | ) void { |
| 1038 | const event_loop: *EventLoop = @alignCast(@ptrCast(userdata)); | 1038 | const event_loop: *EventLoop = @ptrCast(@alignCast(userdata)); |
| 1039 | const future_fiber: *Fiber = @alignCast(@ptrCast(any_future)); | 1039 | const future_fiber: *Fiber = @ptrCast(@alignCast(any_future)); |
| 1040 | if (@atomicLoad(?*Fiber, &future_fiber.awaiter, .acquire) != Fiber.finished) | 1040 | if (@atomicLoad(?*Fiber, &future_fiber.awaiter, .acquire) != Fiber.finished) |
| 1041 | event_loop.yield(null, .{ .register_awaiter = &future_fiber.awaiter }); | 1041 | event_loop.yield(null, .{ .register_awaiter = &future_fiber.awaiter }); |
| 1042 | @memcpy(result, future_fiber.resultBytes(result_alignment)); | 1042 | @memcpy(result, future_fiber.resultBytes(result_alignment)); |
| ... | @@ -1044,11 +1044,11 @@ fn await( | ... | @@ -1044,11 +1044,11 @@ fn await( |
| 1044 | } | 1044 | } |
| 1045 | 1045 | ||
| 1046 | fn select(userdata: ?*anyopaque, futures: []const *Io.AnyFuture) usize { | 1046 | fn select(userdata: ?*anyopaque, futures: []const *Io.AnyFuture) usize { |
| 1047 | const el: *EventLoop = @alignCast(@ptrCast(userdata)); | 1047 | const el: *EventLoop = @ptrCast(@alignCast(userdata)); |
| 1048 | 1048 | ||
| 1049 | // Optimization to avoid the yield below. | 1049 | // Optimization to avoid the yield below. |
| 1050 | for (futures, 0..) |any_future, i| { | 1050 | for (futures, 0..) |any_future, i| { |
| 1051 | const future_fiber: *Fiber = @alignCast(@ptrCast(any_future)); | 1051 | const future_fiber: *Fiber = @ptrCast(@alignCast(any_future)); |
| 1052 | if (@atomicLoad(?*Fiber, &future_fiber.awaiter, .acquire) == Fiber.finished) | 1052 | if (@atomicLoad(?*Fiber, &future_fiber.awaiter, .acquire) == Fiber.finished) |
| 1053 | return i; | 1053 | return i; |
| 1054 | } | 1054 | } |
| ... | @@ -1062,7 +1062,7 @@ fn select(userdata: ?*anyopaque, futures: []const *Io.AnyFuture) usize { | ... | @@ -1062,7 +1062,7 @@ fn select(userdata: ?*anyopaque, futures: []const *Io.AnyFuture) usize { |
| 1062 | var result: ?usize = null; | 1062 | var result: ?usize = null; |
| 1063 | 1063 | ||
| 1064 | for (futures, 0..) |any_future, i| { | 1064 | for (futures, 0..) |any_future, i| { |
| 1065 | const future_fiber: *Fiber = @alignCast(@ptrCast(any_future)); | 1065 | const future_fiber: *Fiber = @ptrCast(@alignCast(any_future)); |
| 1066 | if (@cmpxchgStrong(?*Fiber, &future_fiber.awaiter, my_fiber, null, .seq_cst, .seq_cst)) |awaiter| { | 1066 | if (@cmpxchgStrong(?*Fiber, &future_fiber.awaiter, my_fiber, null, .seq_cst, .seq_cst)) |awaiter| { |
| 1067 | if (awaiter == Fiber.finished) { | 1067 | if (awaiter == Fiber.finished) { |
| 1068 | if (result == null) result = i; | 1068 | if (result == null) result = i; |
| ... | @@ -1085,7 +1085,7 @@ fn cancel( | ... | @@ -1085,7 +1085,7 @@ fn cancel( |
| 1085 | result: []u8, | 1085 | result: []u8, |
| 1086 | result_alignment: Alignment, | 1086 | result_alignment: Alignment, |
| 1087 | ) void { | 1087 | ) void { |
| 1088 | const future_fiber: *Fiber = @alignCast(@ptrCast(any_future)); | 1088 | const future_fiber: *Fiber = @ptrCast(@alignCast(any_future)); |
| 1089 | if (@atomicRmw( | 1089 | if (@atomicRmw( |
| 1090 | ?*Thread, | 1090 | ?*Thread, |
| 1091 | &future_fiber.cancel_thread, | 1091 | &future_fiber.cancel_thread, |
| ... | @@ -1124,7 +1124,7 @@ fn createFile( | ... | @@ -1124,7 +1124,7 @@ fn createFile( |
| 1124 | sub_path: []const u8, | 1124 | sub_path: []const u8, |
| 1125 | flags: Io.File.CreateFlags, | 1125 | flags: Io.File.CreateFlags, |
| 1126 | ) Io.File.OpenError!Io.File { | 1126 | ) Io.File.OpenError!Io.File { |
| 1127 | const el: *EventLoop = @alignCast(@ptrCast(userdata)); | 1127 | const el: *EventLoop = @ptrCast(@alignCast(userdata)); |
| 1128 | const thread: *Thread = .current(); | 1128 | const thread: *Thread = .current(); |
| 1129 | const iou = &thread.io_uring; | 1129 | const iou = &thread.io_uring; |
| 1130 | const fiber = thread.currentFiber(); | 1130 | const fiber = thread.currentFiber(); |
| ... | @@ -1220,13 +1220,13 @@ fn createFile( | ... | @@ -1220,13 +1220,13 @@ fn createFile( |
| 1220 | } | 1220 | } |
| 1221 | } | 1221 | } |
| 1222 | 1222 | ||
| 1223 | fn openFile( | 1223 | fn fileOpen( |
| 1224 | userdata: ?*anyopaque, | 1224 | userdata: ?*anyopaque, |
| 1225 | dir: Io.Dir, | 1225 | dir: Io.Dir, |
| 1226 | sub_path: []const u8, | 1226 | sub_path: []const u8, |
| 1227 | flags: Io.File.OpenFlags, | 1227 | flags: Io.File.OpenFlags, |
| 1228 | ) Io.File.OpenError!Io.File { | 1228 | ) Io.File.OpenError!Io.File { |
| 1229 | const el: *EventLoop = @alignCast(@ptrCast(userdata)); | 1229 | const el: *EventLoop = @ptrCast(@alignCast(userdata)); |
| 1230 | const thread: *Thread = .current(); | 1230 | const thread: *Thread = .current(); |
| 1231 | const iou = &thread.io_uring; | 1231 | const iou = &thread.io_uring; |
| 1232 | const fiber = thread.currentFiber(); | 1232 | const fiber = thread.currentFiber(); |
| ... | @@ -1328,8 +1328,8 @@ fn openFile( | ... | @@ -1328,8 +1328,8 @@ fn openFile( |
| 1328 | } | 1328 | } |
| 1329 | } | 1329 | } |
| 1330 | 1330 | ||
| 1331 | fn closeFile(userdata: ?*anyopaque, file: Io.File) void { | 1331 | fn fileClose(userdata: ?*anyopaque, file: Io.File) void { |
| 1332 | const el: *EventLoop = @alignCast(@ptrCast(userdata)); | 1332 | const el: *EventLoop = @ptrCast(@alignCast(userdata)); |
| 1333 | const thread: *Thread = .current(); | 1333 | const thread: *Thread = .current(); |
| 1334 | const iou = &thread.io_uring; | 1334 | const iou = &thread.io_uring; |
| 1335 | const fiber = thread.currentFiber(); | 1335 | const fiber = thread.currentFiber(); |
| ... | @@ -1365,7 +1365,7 @@ fn closeFile(userdata: ?*anyopaque, file: Io.File) void { | ... | @@ -1365,7 +1365,7 @@ fn closeFile(userdata: ?*anyopaque, file: Io.File) void { |
| 1365 | } | 1365 | } |
| 1366 | 1366 | ||
| 1367 | fn pread(userdata: ?*anyopaque, file: Io.File, buffer: []u8, offset: std.posix.off_t) Io.File.PReadError!usize { | 1367 | fn pread(userdata: ?*anyopaque, file: Io.File, buffer: []u8, offset: std.posix.off_t) Io.File.PReadError!usize { |
| 1368 | const el: *EventLoop = @alignCast(@ptrCast(userdata)); | 1368 | const el: *EventLoop = @ptrCast(@alignCast(userdata)); |
| 1369 | const thread: *Thread = .current(); | 1369 | const thread: *Thread = .current(); |
| 1370 | const iou = &thread.io_uring; | 1370 | const iou = &thread.io_uring; |
| 1371 | const fiber = thread.currentFiber(); | 1371 | const fiber = thread.currentFiber(); |
| ... | @@ -1417,7 +1417,7 @@ fn pread(userdata: ?*anyopaque, file: Io.File, buffer: []u8, offset: std.posix.o | ... | @@ -1417,7 +1417,7 @@ fn pread(userdata: ?*anyopaque, file: Io.File, buffer: []u8, offset: std.posix.o |
| 1417 | } | 1417 | } |
| 1418 | 1418 | ||
| 1419 | fn pwrite(userdata: ?*anyopaque, file: Io.File, buffer: []const u8, offset: std.posix.off_t) Io.File.PWriteError!usize { | 1419 | fn pwrite(userdata: ?*anyopaque, file: Io.File, buffer: []const u8, offset: std.posix.off_t) Io.File.PWriteError!usize { |
| 1420 | const el: *EventLoop = @alignCast(@ptrCast(userdata)); | 1420 | const el: *EventLoop = @ptrCast(@alignCast(userdata)); |
| 1421 | const thread: *Thread = .current(); | 1421 | const thread: *Thread = .current(); |
| 1422 | const iou = &thread.io_uring; | 1422 | const iou = &thread.io_uring; |
| 1423 | const fiber = thread.currentFiber(); | 1423 | const fiber = thread.currentFiber(); |
| ... | @@ -1479,7 +1479,7 @@ fn now(userdata: ?*anyopaque, clockid: std.posix.clockid_t) Io.ClockGetTimeError | ... | @@ -1479,7 +1479,7 @@ fn now(userdata: ?*anyopaque, clockid: std.posix.clockid_t) Io.ClockGetTimeError |
| 1479 | } | 1479 | } |
| 1480 | 1480 | ||
| 1481 | fn sleep(userdata: ?*anyopaque, clockid: std.posix.clockid_t, deadline: Io.Deadline) Io.SleepError!void { | 1481 | fn sleep(userdata: ?*anyopaque, clockid: std.posix.clockid_t, deadline: Io.Deadline) Io.SleepError!void { |
| 1482 | const el: *EventLoop = @alignCast(@ptrCast(userdata)); | 1482 | const el: *EventLoop = @ptrCast(@alignCast(userdata)); |
| 1483 | const thread: *Thread = .current(); | 1483 | const thread: *Thread = .current(); |
| 1484 | const iou = &thread.io_uring; | 1484 | const iou = &thread.io_uring; |
| 1485 | const fiber = thread.currentFiber(); | 1485 | const fiber = thread.currentFiber(); |
| ... | @@ -1532,7 +1532,7 @@ fn sleep(userdata: ?*anyopaque, clockid: std.posix.clockid_t, deadline: Io.Deadl | ... | @@ -1532,7 +1532,7 @@ fn sleep(userdata: ?*anyopaque, clockid: std.posix.clockid_t, deadline: Io.Deadl |
| 1532 | } | 1532 | } |
| 1533 | 1533 | ||
| 1534 | fn mutexLock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mutex) error{Canceled}!void { | 1534 | fn mutexLock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mutex) error{Canceled}!void { |
| 1535 | const el: *EventLoop = @alignCast(@ptrCast(userdata)); | 1535 | const el: *EventLoop = @ptrCast(@alignCast(userdata)); |
| 1536 | el.yield(null, .{ .mutex_lock = .{ .prev_state = prev_state, .mutex = mutex } }); | 1536 | el.yield(null, .{ .mutex_lock = .{ .prev_state = prev_state, .mutex = mutex } }); |
| 1537 | } | 1537 | } |
| 1538 | fn mutexUnlock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mutex) void { | 1538 | fn mutexUnlock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mutex) void { |
| ... | @@ -1553,7 +1553,7 @@ fn mutexUnlock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mut | ... | @@ -1553,7 +1553,7 @@ fn mutexUnlock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mut |
| 1553 | .acquire, | 1553 | .acquire, |
| 1554 | ) orelse return) |next_state| maybe_waiting_fiber = @ptrFromInt(@intFromEnum(next_state)); | 1554 | ) orelse return) |next_state| maybe_waiting_fiber = @ptrFromInt(@intFromEnum(next_state)); |
| 1555 | maybe_waiting_fiber.?.queue_next = null; | 1555 | maybe_waiting_fiber.?.queue_next = null; |
| 1556 | const el: *EventLoop = @alignCast(@ptrCast(userdata)); | 1556 | const el: *EventLoop = @ptrCast(@alignCast(userdata)); |
| 1557 | el.yield(maybe_waiting_fiber.?, .reschedule); | 1557 | el.yield(maybe_waiting_fiber.?, .reschedule); |
| 1558 | } | 1558 | } |
| 1559 | 1559 | ||
| ... | @@ -1566,7 +1566,7 @@ const ConditionImpl = struct { | ... | @@ -1566,7 +1566,7 @@ const ConditionImpl = struct { |
| 1566 | }; | 1566 | }; |
| 1567 | 1567 | ||
| 1568 | fn conditionWait(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) Io.Cancelable!void { | 1568 | fn conditionWait(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) Io.Cancelable!void { |
| 1569 | const el: *EventLoop = @alignCast(@ptrCast(userdata)); | 1569 | const el: *EventLoop = @ptrCast(@alignCast(userdata)); |
| 1570 | el.yield(null, .{ .condition_wait = .{ .cond = cond, .mutex = mutex } }); | 1570 | el.yield(null, .{ .condition_wait = .{ .cond = cond, .mutex = mutex } }); |
| 1571 | const thread = Thread.current(); | 1571 | const thread = Thread.current(); |
| 1572 | const fiber = thread.currentFiber(); | 1572 | const fiber = thread.currentFiber(); |
| ... | @@ -1595,7 +1595,7 @@ fn conditionWait(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) I | ... | @@ -1595,7 +1595,7 @@ fn conditionWait(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) I |
| 1595 | } | 1595 | } |
| 1596 | 1596 | ||
| 1597 | fn conditionWake(userdata: ?*anyopaque, cond: *Io.Condition, wake: Io.Condition.Wake) void { | 1597 | fn conditionWake(userdata: ?*anyopaque, cond: *Io.Condition, wake: Io.Condition.Wake) void { |
| 1598 | const el: *EventLoop = @alignCast(@ptrCast(userdata)); | 1598 | const el: *EventLoop = @ptrCast(@alignCast(userdata)); |
| 1599 | const waiting_fiber = @atomicRmw(?*Fiber, @as(*?*Fiber, @ptrCast(&cond.state)), .Xchg, null, .acquire) orelse return; | 1599 | const waiting_fiber = @atomicRmw(?*Fiber, @as(*?*Fiber, @ptrCast(&cond.state)), .Xchg, null, .acquire) orelse return; |
| 1600 | waiting_fiber.resultPointer(ConditionImpl).event = .{ .wake = wake }; | 1600 | waiting_fiber.resultPointer(ConditionImpl).event = .{ .wake = wake }; |
| 1601 | el.yield(waiting_fiber, .reschedule); | 1601 | el.yield(waiting_fiber, .reschedule); |
lib/std/Io/File.zig created+550| ... | @@ -0,0 +1,550 @@ | ||
| 1 | const builtin = @import("builtin"); | ||
| 2 | const std = @import("../std.zig"); | ||
| 3 | const Io = std.Io; | ||
| 4 | const File = @This(); | ||
| 5 | const assert = std.debug.assert; | ||
| 6 | |||
| 7 | handle: Handle, | ||
| 8 | |||
| 9 | pub const Handle = std.posix.fd_t; | ||
| 10 | pub const Mode = std.posix.mode_t; | ||
| 11 | pub const INode = std.posix.ino_t; | ||
| 12 | |||
| 13 | pub const Kind = enum { | ||
| 14 | block_device, | ||
| 15 | character_device, | ||
| 16 | directory, | ||
| 17 | named_pipe, | ||
| 18 | sym_link, | ||
| 19 | file, | ||
| 20 | unix_domain_socket, | ||
| 21 | whiteout, | ||
| 22 | door, | ||
| 23 | event_port, | ||
| 24 | unknown, | ||
| 25 | }; | ||
| 26 | |||
| 27 | pub const Stat = struct { | ||
| 28 | /// A number that the system uses to point to the file metadata. This | ||
| 29 | /// number is not guaranteed to be unique across time, as some file | ||
| 30 | /// systems may reuse an inode after its file has been deleted. Some | ||
| 31 | /// systems may change the inode of a file over time. | ||
| 32 | /// | ||
| 33 | /// On Linux, the inode is a structure that stores the metadata, and | ||
| 34 | /// the inode _number_ is what you see here: the index number of the | ||
| 35 | /// inode. | ||
| 36 | /// | ||
| 37 | /// The FileIndex on Windows is similar. It is a number for a file that | ||
| 38 | /// is unique to each filesystem. | ||
| 39 | inode: INode, | ||
| 40 | size: u64, | ||
| 41 | /// This is available on POSIX systems and is always 0 otherwise. | ||
| 42 | mode: Mode, | ||
| 43 | kind: Kind, | ||
| 44 | |||
| 45 | /// Last access time in nanoseconds, relative to UTC 1970-01-01. | ||
| 46 | atime: i128, | ||
| 47 | /// Last modification time in nanoseconds, relative to UTC 1970-01-01. | ||
| 48 | mtime: i128, | ||
| 49 | /// Last status/metadata change time in nanoseconds, relative to UTC 1970-01-01. | ||
| 50 | ctime: i128, | ||
| 51 | |||
| 52 | pub fn fromPosix(st: std.posix.Stat) Stat { | ||
| 53 | const atime = st.atime(); | ||
| 54 | const mtime = st.mtime(); | ||
| 55 | const ctime = st.ctime(); | ||
| 56 | return .{ | ||
| 57 | .inode = st.ino, | ||
| 58 | .size = @bitCast(st.size), | ||
| 59 | .mode = st.mode, | ||
| 60 | .kind = k: { | ||
| 61 | const m = st.mode & std.posix.S.IFMT; | ||
| 62 | switch (m) { | ||
| 63 | std.posix.S.IFBLK => break :k .block_device, | ||
| 64 | std.posix.S.IFCHR => break :k .character_device, | ||
| 65 | std.posix.S.IFDIR => break :k .directory, | ||
| 66 | std.posix.S.IFIFO => break :k .named_pipe, | ||
| 67 | std.posix.S.IFLNK => break :k .sym_link, | ||
| 68 | std.posix.S.IFREG => break :k .file, | ||
| 69 | std.posix.S.IFSOCK => break :k .unix_domain_socket, | ||
| 70 | else => {}, | ||
| 71 | } | ||
| 72 | if (builtin.os.tag.isSolarish()) switch (m) { | ||
| 73 | std.posix.S.IFDOOR => break :k .door, | ||
| 74 | std.posix.S.IFPORT => break :k .event_port, | ||
| 75 | else => {}, | ||
| 76 | }; | ||
| 77 | |||
| 78 | break :k .unknown; | ||
| 79 | }, | ||
| 80 | .atime = @as(i128, atime.sec) * std.time.ns_per_s + atime.nsec, | ||
| 81 | .mtime = @as(i128, mtime.sec) * std.time.ns_per_s + mtime.nsec, | ||
| 82 | .ctime = @as(i128, ctime.sec) * std.time.ns_per_s + ctime.nsec, | ||
| 83 | }; | ||
| 84 | } | ||
| 85 | |||
| 86 | pub fn fromLinux(stx: std.os.linux.Statx) Stat { | ||
| 87 | const atime = stx.atime; | ||
| 88 | const mtime = stx.mtime; | ||
| 89 | const ctime = stx.ctime; | ||
| 90 | |||
| 91 | return .{ | ||
| 92 | .inode = stx.ino, | ||
| 93 | .size = stx.size, | ||
| 94 | .mode = stx.mode, | ||
| 95 | .kind = switch (stx.mode & std.os.linux.S.IFMT) { | ||
| 96 | std.os.linux.S.IFDIR => .directory, | ||
| 97 | std.os.linux.S.IFCHR => .character_device, | ||
| 98 | std.os.linux.S.IFBLK => .block_device, | ||
| 99 | std.os.linux.S.IFREG => .file, | ||
| 100 | std.os.linux.S.IFIFO => .named_pipe, | ||
| 101 | std.os.linux.S.IFLNK => .sym_link, | ||
| 102 | std.os.linux.S.IFSOCK => .unix_domain_socket, | ||
| 103 | else => .unknown, | ||
| 104 | }, | ||
| 105 | .atime = @as(i128, atime.sec) * std.time.ns_per_s + atime.nsec, | ||
| 106 | .mtime = @as(i128, mtime.sec) * std.time.ns_per_s + mtime.nsec, | ||
| 107 | .ctime = @as(i128, ctime.sec) * std.time.ns_per_s + ctime.nsec, | ||
| 108 | }; | ||
| 109 | } | ||
| 110 | |||
| 111 | pub fn fromWasi(st: std.os.wasi.filestat_t) Stat { | ||
| 112 | return .{ | ||
| 113 | .inode = st.ino, | ||
| 114 | .size = @bitCast(st.size), | ||
| 115 | .mode = 0, | ||
| 116 | .kind = switch (st.filetype) { | ||
| 117 | .BLOCK_DEVICE => .block_device, | ||
| 118 | .CHARACTER_DEVICE => .character_device, | ||
| 119 | .DIRECTORY => .directory, | ||
| 120 | .SYMBOLIC_LINK => .sym_link, | ||
| 121 | .REGULAR_FILE => .file, | ||
| 122 | .SOCKET_STREAM, .SOCKET_DGRAM => .unix_domain_socket, | ||
| 123 | else => .unknown, | ||
| 124 | }, | ||
| 125 | .atime = st.atim, | ||
| 126 | .mtime = st.mtim, | ||
| 127 | .ctime = st.ctim, | ||
| 128 | }; | ||
| 129 | } | ||
| 130 | }; | ||
| 131 | |||
| 132 | pub const StatError = std.posix.FStatError || Io.Cancelable; | ||
| 133 | |||
| 134 | /// Returns `Stat` containing basic information about the `File`. | ||
| 135 | pub fn stat(file: File, io: Io) StatError!Stat { | ||
| 136 | _ = file; | ||
| 137 | _ = io; | ||
| 138 | @panic("TODO"); | ||
| 139 | } | ||
| 140 | |||
| 141 | pub const OpenFlags = std.fs.File.OpenFlags; | ||
| 142 | pub const CreateFlags = std.fs.File.CreateFlags; | ||
| 143 | |||
| 144 | pub const OpenError = std.fs.File.OpenError || Io.Cancelable; | ||
| 145 | |||
| 146 | pub fn close(file: File, io: Io) void { | ||
| 147 | return io.vtable.fileClose(io.userdata, file); | ||
| 148 | } | ||
| 149 | |||
| 150 | pub const ReadStreamingError = error{ | ||
| 151 | InputOutput, | ||
| 152 | SystemResources, | ||
| 153 | IsDir, | ||
| 154 | BrokenPipe, | ||
| 155 | ConnectionResetByPeer, | ||
| 156 | ConnectionTimedOut, | ||
| 157 | NotOpenForReading, | ||
| 158 | SocketNotConnected, | ||
| 159 | /// This error occurs when no global event loop is configured, | ||
| 160 | /// and reading from the file descriptor would block. | ||
| 161 | WouldBlock, | ||
| 162 | /// In WASI, this error occurs when the file descriptor does | ||
| 163 | /// not hold the required rights to read from it. | ||
| 164 | AccessDenied, | ||
| 165 | /// This error occurs in Linux if the process to be read from | ||
| 166 | /// no longer exists. | ||
| 167 | ProcessNotFound, | ||
| 168 | /// Unable to read file due to lock. | ||
| 169 | LockViolation, | ||
| 170 | } || Io.Cancelable || Io.UnexpectedError; | ||
| 171 | |||
| 172 | pub const ReadPositionalError = ReadStreamingError || error{Unseekable}; | ||
| 173 | |||
| 174 | pub fn readPositional(file: File, io: Io, buffer: []u8, offset: u64) ReadPositionalError!usize { | ||
| 175 | return io.vtable.pread(io.userdata, file, buffer, offset); | ||
| 176 | } | ||
| 177 | |||
| 178 | pub const WriteError = std.fs.File.WriteError || Io.Cancelable; | ||
| 179 | |||
| 180 | pub fn write(file: File, io: Io, buffer: []const u8) WriteError!usize { | ||
| 181 | return @errorCast(file.pwrite(io, buffer, -1)); | ||
| 182 | } | ||
| 183 | |||
| 184 | pub const PWriteError = std.fs.File.PWriteError || Io.Cancelable; | ||
| 185 | |||
| 186 | pub fn pwrite(file: File, io: Io, buffer: []const u8, offset: std.posix.off_t) PWriteError!usize { | ||
| 187 | return io.vtable.pwrite(io.userdata, file, buffer, offset); | ||
| 188 | } | ||
| 189 | |||
| 190 | pub fn openAbsolute(io: Io, absolute_path: []const u8, flags: OpenFlags) OpenError!File { | ||
| 191 | assert(std.fs.path.isAbsolute(absolute_path)); | ||
| 192 | return Io.Dir.cwd().openFile(io, absolute_path, flags); | ||
| 193 | } | ||
| 194 | |||
| 195 | /// Defaults to positional reading; falls back to streaming. | ||
| 196 | /// | ||
| 197 | /// Positional is more threadsafe, since the global seek position is not | ||
| 198 | /// affected. | ||
| 199 | pub fn reader(file: File, io: Io, buffer: []u8) Reader { | ||
| 200 | return .init(file, io, buffer); | ||
| 201 | } | ||
| 202 | |||
| 203 | /// Positional is more threadsafe, since the global seek position is not | ||
| 204 | /// affected, but when such syscalls are not available, preemptively | ||
| 205 | /// initializing in streaming mode skips a failed syscall. | ||
| 206 | pub fn readerStreaming(file: File, io: Io, buffer: []u8) Reader { | ||
| 207 | return .initStreaming(file, io, buffer); | ||
| 208 | } | ||
| 209 | |||
| 210 | pub const SeekError = error{ | ||
| 211 | Unseekable, | ||
| 212 | /// The file descriptor does not hold the required rights to seek on it. | ||
| 213 | AccessDenied, | ||
| 214 | } || Io.Cancelable || Io.UnexpectedError; | ||
| 215 | |||
| 216 | /// Memoizes key information about a file handle such as: | ||
| 217 | /// * The size from calling stat, or the error that occurred therein. | ||
| 218 | /// * The current seek position. | ||
| 219 | /// * The error that occurred when trying to seek. | ||
| 220 | /// * Whether reading should be done positionally or streaming. | ||
| 221 | /// * Whether reading should be done via fd-to-fd syscalls (e.g. `sendfile`) | ||
| 222 | /// versus plain variants (e.g. `read`). | ||
| 223 | /// | ||
| 224 | /// Fulfills the `Io.Reader` interface. | ||
| 225 | pub const Reader = struct { | ||
| 226 | io: Io, | ||
| 227 | file: File, | ||
| 228 | err: ?Error = null, | ||
| 229 | mode: Reader.Mode = .positional, | ||
| 230 | /// Tracks the true seek position in the file. To obtain the logical | ||
| 231 | /// position, use `logicalPos`. | ||
| 232 | pos: u64 = 0, | ||
| 233 | size: ?u64 = null, | ||
| 234 | size_err: ?SizeError = null, | ||
| 235 | seek_err: ?Reader.SeekError = null, | ||
| 236 | interface: Io.Reader, | ||
| 237 | |||
| 238 | pub const Error = std.posix.ReadError || Io.Cancelable; | ||
| 239 | |||
| 240 | pub const SizeError = std.os.windows.GetFileSizeError || StatError || error{ | ||
| 241 | /// Occurs if, for example, the file handle is a network socket and therefore does not have a size. | ||
| 242 | Streaming, | ||
| 243 | }; | ||
| 244 | |||
| 245 | pub const SeekError = File.SeekError || error{ | ||
| 246 | /// Seeking fell back to reading, and reached the end before the requested seek position. | ||
| 247 | /// `pos` remains at the end of the file. | ||
| 248 | EndOfStream, | ||
| 249 | /// Seeking fell back to reading, which failed. | ||
| 250 | ReadFailed, | ||
| 251 | }; | ||
| 252 | |||
| 253 | pub const Mode = enum { | ||
| 254 | streaming, | ||
| 255 | positional, | ||
| 256 | /// Avoid syscalls other than `read` and `readv`. | ||
| 257 | streaming_reading, | ||
| 258 | /// Avoid syscalls other than `pread` and `preadv`. | ||
| 259 | positional_reading, | ||
| 260 | /// Indicates reading cannot continue because of a seek failure. | ||
| 261 | failure, | ||
| 262 | |||
| 263 | pub fn toStreaming(m: @This()) @This() { | ||
| 264 | return switch (m) { | ||
| 265 | .positional, .streaming => .streaming, | ||
| 266 | .positional_reading, .streaming_reading => .streaming_reading, | ||
| 267 | .failure => .failure, | ||
| 268 | }; | ||
| 269 | } | ||
| 270 | |||
| 271 | pub fn toReading(m: @This()) @This() { | ||
| 272 | return switch (m) { | ||
| 273 | .positional, .positional_reading => .positional_reading, | ||
| 274 | .streaming, .streaming_reading => .streaming_reading, | ||
| 275 | .failure => .failure, | ||
| 276 | }; | ||
| 277 | } | ||
| 278 | }; | ||
| 279 | |||
| 280 | pub fn initInterface(buffer: []u8) Io.Reader { | ||
| 281 | return .{ | ||
| 282 | .vtable = &.{ | ||
| 283 | .stream = Reader.stream, | ||
| 284 | .discard = Reader.discard, | ||
| 285 | .readVec = Reader.readVec, | ||
| 286 | }, | ||
| 287 | .buffer = buffer, | ||
| 288 | .seek = 0, | ||
| 289 | .end = 0, | ||
| 290 | }; | ||
| 291 | } | ||
| 292 | |||
| 293 | pub fn init(file: File, io: Io, buffer: []u8) Reader { | ||
| 294 | return .{ | ||
| 295 | .io = io, | ||
| 296 | .file = file, | ||
| 297 | .interface = initInterface(buffer), | ||
| 298 | }; | ||
| 299 | } | ||
| 300 | |||
| 301 | pub fn initSize(file: File, io: Io, buffer: []u8, size: ?u64) Reader { | ||
| 302 | return .{ | ||
| 303 | .io = io, | ||
| 304 | .file = file, | ||
| 305 | .interface = initInterface(buffer), | ||
| 306 | .size = size, | ||
| 307 | }; | ||
| 308 | } | ||
| 309 | |||
| 310 | /// Positional is more threadsafe, since the global seek position is not | ||
| 311 | /// affected, but when such syscalls are not available, preemptively | ||
| 312 | /// initializing in streaming mode skips a failed syscall. | ||
| 313 | pub fn initStreaming(file: File, io: Io, buffer: []u8) Reader { | ||
| 314 | return .{ | ||
| 315 | .io = io, | ||
| 316 | .file = file, | ||
| 317 | .interface = Reader.initInterface(buffer), | ||
| 318 | .mode = .streaming, | ||
| 319 | .seek_err = error.Unseekable, | ||
| 320 | .size_err = error.Streaming, | ||
| 321 | }; | ||
| 322 | } | ||
| 323 | |||
| 324 | pub fn getSize(r: *Reader) SizeError!u64 { | ||
| 325 | return r.size orelse { | ||
| 326 | if (r.size_err) |err| return err; | ||
| 327 | if (std.posix.Stat == void) { | ||
| 328 | r.size_err = error.Streaming; | ||
| 329 | return error.Streaming; | ||
| 330 | } | ||
| 331 | if (stat(r.file, r.io)) |st| { | ||
| 332 | if (st.kind == .file) { | ||
| 333 | r.size = st.size; | ||
| 334 | return st.size; | ||
| 335 | } else { | ||
| 336 | r.mode = r.mode.toStreaming(); | ||
| 337 | r.size_err = error.Streaming; | ||
| 338 | return error.Streaming; | ||
| 339 | } | ||
| 340 | } else |err| { | ||
| 341 | r.size_err = err; | ||
| 342 | return err; | ||
| 343 | } | ||
| 344 | }; | ||
| 345 | } | ||
| 346 | |||
| 347 | pub fn seekBy(r: *Reader, offset: i64) Reader.SeekError!void { | ||
| 348 | const io = r.io; | ||
| 349 | switch (r.mode) { | ||
| 350 | .positional, .positional_reading => { | ||
| 351 | setPosAdjustingBuffer(r, @intCast(@as(i64, @intCast(r.pos)) + offset)); | ||
| 352 | }, | ||
| 353 | .streaming, .streaming_reading => { | ||
| 354 | if (std.posix.SEEK == void) { | ||
| 355 | r.seek_err = error.Unseekable; | ||
| 356 | return error.Unseekable; | ||
| 357 | } | ||
| 358 | const seek_err = r.seek_err orelse e: { | ||
| 359 | if (io.vtable.fileSeekBy(io.userdata, r.file, offset)) |_| { | ||
| 360 | setPosAdjustingBuffer(r, @intCast(@as(i64, @intCast(r.pos)) + offset)); | ||
| 361 | return; | ||
| 362 | } else |err| { | ||
| 363 | r.seek_err = err; | ||
| 364 | break :e err; | ||
| 365 | } | ||
| 366 | }; | ||
| 367 | var remaining = std.math.cast(u64, offset) orelse return seek_err; | ||
| 368 | while (remaining > 0) { | ||
| 369 | remaining -= discard(&r.interface, .limited64(remaining)) catch |err| { | ||
| 370 | r.seek_err = err; | ||
| 371 | return err; | ||
| 372 | }; | ||
| 373 | } | ||
| 374 | r.interface.seek = 0; | ||
| 375 | r.interface.end = 0; | ||
| 376 | }, | ||
| 377 | .failure => return r.seek_err.?, | ||
| 378 | } | ||
| 379 | } | ||
| 380 | |||
| 381 | pub fn seekTo(r: *Reader, offset: u64) Reader.SeekError!void { | ||
| 382 | const io = r.io; | ||
| 383 | switch (r.mode) { | ||
| 384 | .positional, .positional_reading => { | ||
| 385 | setPosAdjustingBuffer(r, offset); | ||
| 386 | }, | ||
| 387 | .streaming, .streaming_reading => { | ||
| 388 | if (offset >= r.pos) return Reader.seekBy(r, @intCast(offset - r.pos)); | ||
| 389 | if (r.seek_err) |err| return err; | ||
| 390 | io.vtable.fileSeekTo(io.userdata, r.file, offset) catch |err| { | ||
| 391 | r.seek_err = err; | ||
| 392 | return err; | ||
| 393 | }; | ||
| 394 | setPosAdjustingBuffer(r, offset); | ||
| 395 | }, | ||
| 396 | .failure => return r.seek_err.?, | ||
| 397 | } | ||
| 398 | } | ||
| 399 | |||
| 400 | pub fn logicalPos(r: *const Reader) u64 { | ||
| 401 | return r.pos - r.interface.bufferedLen(); | ||
| 402 | } | ||
| 403 | |||
| 404 | fn setPosAdjustingBuffer(r: *Reader, offset: u64) void { | ||
| 405 | const logical_pos = logicalPos(r); | ||
| 406 | if (offset < logical_pos or offset >= r.pos) { | ||
| 407 | r.interface.seek = 0; | ||
| 408 | r.interface.end = 0; | ||
| 409 | r.pos = offset; | ||
| 410 | } else { | ||
| 411 | const logical_delta: usize = @intCast(offset - logical_pos); | ||
| 412 | r.interface.seek += logical_delta; | ||
| 413 | } | ||
| 414 | } | ||
| 415 | |||
| 416 | /// Number of slices to store on the stack, when trying to send as many byte | ||
| 417 | /// vectors through the underlying read calls as possible. | ||
| 418 | const max_buffers_len = 16; | ||
| 419 | |||
| 420 | fn stream(io_reader: *Io.Reader, w: *Io.Writer, limit: Io.Limit) Io.Reader.StreamError!usize { | ||
| 421 | const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader)); | ||
| 422 | switch (r.mode) { | ||
| 423 | .positional, .streaming => return w.sendFile(r, limit) catch |write_err| switch (write_err) { | ||
| 424 | error.Unimplemented => { | ||
| 425 | r.mode = r.mode.toReading(); | ||
| 426 | return 0; | ||
| 427 | }, | ||
| 428 | else => |e| return e, | ||
| 429 | }, | ||
| 430 | .positional_reading => { | ||
| 431 | const dest = limit.slice(try w.writableSliceGreedy(1)); | ||
| 432 | var data: [1][]u8 = .{dest}; | ||
| 433 | const n = try readVecPositional(r, &data); | ||
| 434 | w.advance(n); | ||
| 435 | return n; | ||
| 436 | }, | ||
| 437 | .streaming_reading => { | ||
| 438 | const dest = limit.slice(try w.writableSliceGreedy(1)); | ||
| 439 | var data: [1][]u8 = .{dest}; | ||
| 440 | const n = try readVecStreaming(r, &data); | ||
| 441 | w.advance(n); | ||
| 442 | return n; | ||
| 443 | }, | ||
| 444 | .failure => return error.ReadFailed, | ||
| 445 | } | ||
| 446 | } | ||
| 447 | |||
| 448 | fn readVec(io_reader: *Io.Reader, data: [][]u8) Io.Reader.Error!usize { | ||
| 449 | const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader)); | ||
| 450 | switch (r.mode) { | ||
| 451 | .positional, .positional_reading => return readVecPositional(r, data), | ||
| 452 | .streaming, .streaming_reading => return readVecStreaming(r, data), | ||
| 453 | .failure => return error.ReadFailed, | ||
| 454 | } | ||
| 455 | } | ||
| 456 | |||
| 457 | fn readVecPositional(r: *Reader, data: [][]u8) Io.Reader.Error!usize { | ||
| 458 | const io = r.io; | ||
| 459 | assert(r.interface.bufferedLen() == 0); | ||
| 460 | var iovecs_buffer: [max_buffers_len][]u8 = undefined; | ||
| 461 | const dest_n, const data_size = try r.interface.writableVector(&iovecs_buffer, data); | ||
| 462 | const dest = iovecs_buffer[0..dest_n]; | ||
| 463 | assert(dest[0].len > 0); | ||
| 464 | const n = io.vtable.fileReadPositional(io.userdata, r.file, dest, r.pos) catch |err| switch (err) { | ||
| 465 | error.Unseekable => { | ||
| 466 | r.mode = r.mode.toStreaming(); | ||
| 467 | const pos = r.pos; | ||
| 468 | if (pos != 0) { | ||
| 469 | r.pos = 0; | ||
| 470 | r.seekBy(@intCast(pos)) catch { | ||
| 471 | r.mode = .failure; | ||
| 472 | return error.ReadFailed; | ||
| 473 | }; | ||
| 474 | } | ||
| 475 | return 0; | ||
| 476 | }, | ||
| 477 | else => |e| { | ||
| 478 | r.err = e; | ||
| 479 | return error.ReadFailed; | ||
| 480 | }, | ||
| 481 | }; | ||
| 482 | if (n == 0) { | ||
| 483 | r.size = r.pos; | ||
| 484 | return error.EndOfStream; | ||
| 485 | } | ||
| 486 | r.pos += n; | ||
| 487 | if (n > data_size) { | ||
| 488 | r.interface.end += n - data_size; | ||
| 489 | return data_size; | ||
| 490 | } | ||
| 491 | return n; | ||
| 492 | } | ||
| 493 | |||
| 494 | fn readVecStreaming(r: *Reader, data: [][]u8) Io.Reader.Error!usize { | ||
| 495 | const io = r.io; | ||
| 496 | var iovecs_buffer: [max_buffers_len][]u8 = undefined; | ||
| 497 | const dest_n, const data_size = try r.interface.writableVector(&iovecs_buffer, data); | ||
| 498 | const dest = iovecs_buffer[0..dest_n]; | ||
| 499 | assert(dest[0].len > 0); | ||
| 500 | const n = io.vtable.fileReadStreaming(io.userdata, r.file, dest) catch |err| { | ||
| 501 | r.err = err; | ||
| 502 | return error.ReadFailed; | ||
| 503 | }; | ||
| 504 | if (n == 0) { | ||
| 505 | r.size = r.pos; | ||
| 506 | return error.EndOfStream; | ||
| 507 | } | ||
| 508 | r.pos += n; | ||
| 509 | if (n > data_size) { | ||
| 510 | r.interface.end += n - data_size; | ||
| 511 | return data_size; | ||
| 512 | } | ||
| 513 | return n; | ||
| 514 | } | ||
| 515 | |||
| 516 | fn discard(io_reader: *Io.Reader, limit: Io.Limit) Io.Reader.Error!usize { | ||
| 517 | const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader)); | ||
| 518 | const io = r.io; | ||
| 519 | const file = r.file; | ||
| 520 | const pos = r.pos; | ||
| 521 | switch (r.mode) { | ||
| 522 | .positional, .positional_reading => { | ||
| 523 | const size = r.getSize() catch { | ||
| 524 | r.mode = r.mode.toStreaming(); | ||
| 525 | return 0; | ||
| 526 | }; | ||
| 527 | const delta = @min(@intFromEnum(limit), size - pos); | ||
| 528 | r.pos = pos + delta; | ||
| 529 | return delta; | ||
| 530 | }, | ||
| 531 | .streaming, .streaming_reading => { | ||
| 532 | const size = r.getSize() catch return 0; | ||
| 533 | const n = @min(size - pos, std.math.maxInt(i64), @intFromEnum(limit)); | ||
| 534 | io.vtable.fileSeekBy(io.userdata, file, n) catch |err| { | ||
| 535 | r.seek_err = err; | ||
| 536 | return 0; | ||
| 537 | }; | ||
| 538 | r.pos = pos + n; | ||
| 539 | return n; | ||
| 540 | }, | ||
| 541 | .failure => return error.ReadFailed, | ||
| 542 | } | ||
| 543 | } | ||
| 544 | |||
| 545 | pub fn atEnd(r: *Reader) bool { | ||
| 546 | // Even if stat fails, size is set when end is encountered. | ||
| 547 | const size = r.size orelse return false; | ||
| 548 | return size - r.pos == 0; | ||
| 549 | } | ||
| 550 | }; | ||
lib/std/Io/ThreadPool.zig+259-12| ... | @@ -1,11 +1,16 @@ | ... | @@ -1,11 +1,16 @@ |
| 1 | const Pool = @This(); | ||
| 2 | |||
| 1 | const builtin = @import("builtin"); | 3 | const builtin = @import("builtin"); |
| 4 | const native_os = builtin.os.tag; | ||
| 5 | const is_windows = native_os == .windows; | ||
| 6 | const windows = std.os.windows; | ||
| 7 | |||
| 2 | const std = @import("../std.zig"); | 8 | const std = @import("../std.zig"); |
| 3 | const Allocator = std.mem.Allocator; | 9 | const Allocator = std.mem.Allocator; |
| 4 | const assert = std.debug.assert; | 10 | const assert = std.debug.assert; |
| 5 | const WaitGroup = std.Thread.WaitGroup; | 11 | const WaitGroup = std.Thread.WaitGroup; |
| 6 | const posix = std.posix; | 12 | const posix = std.posix; |
| 7 | const Io = std.Io; | 13 | const Io = std.Io; |
| 8 | const Pool = @This(); | ||
| 9 | 14 | ||
| 10 | /// Thread-safe. | 15 | /// Thread-safe. |
| 11 | allocator: Allocator, | 16 | allocator: Allocator, |
| ... | @@ -23,6 +28,10 @@ threadlocal var current_closure: ?*AsyncClosure = null; | ... | @@ -23,6 +28,10 @@ threadlocal var current_closure: ?*AsyncClosure = null; |
| 23 | const max_iovecs_len = 8; | 28 | const max_iovecs_len = 8; |
| 24 | const splat_buffer_size = 64; | 29 | const splat_buffer_size = 64; |
| 25 | 30 | ||
| 31 | comptime { | ||
| 32 | assert(max_iovecs_len <= posix.IOV_MAX); | ||
| 33 | } | ||
| 34 | |||
| 26 | pub const Runnable = struct { | 35 | pub const Runnable = struct { |
| 27 | start: Start, | 36 | start: Start, |
| 28 | node: std.SinglyLinkedList.Node = .{}, | 37 | node: std.SinglyLinkedList.Node = .{}, |
| ... | @@ -104,10 +113,13 @@ pub fn io(pool: *Pool) Io { | ... | @@ -104,10 +113,13 @@ pub fn io(pool: *Pool) Io { |
| 104 | .conditionWake = conditionWake, | 113 | .conditionWake = conditionWake, |
| 105 | 114 | ||
| 106 | .createFile = createFile, | 115 | .createFile = createFile, |
| 107 | .openFile = openFile, | 116 | .fileOpen = fileOpen, |
| 108 | .closeFile = closeFile, | 117 | .fileClose = fileClose, |
| 109 | .pread = pread, | ||
| 110 | .pwrite = pwrite, | 118 | .pwrite = pwrite, |
| 119 | .fileReadStreaming = fileReadStreaming, | ||
| 120 | .fileReadPositional = fileReadPositional, | ||
| 121 | .fileSeekBy = fileSeekBy, | ||
| 122 | .fileSeekTo = fileSeekTo, | ||
| 111 | 123 | ||
| 112 | .now = now, | 124 | .now = now, |
| 113 | .sleep = sleep, | 125 | .sleep = sleep, |
| ... | @@ -631,7 +643,7 @@ fn createFile( | ... | @@ -631,7 +643,7 @@ fn createFile( |
| 631 | return .{ .handle = fs_file.handle }; | 643 | return .{ .handle = fs_file.handle }; |
| 632 | } | 644 | } |
| 633 | 645 | ||
| 634 | fn openFile( | 646 | fn fileOpen( |
| 635 | userdata: ?*anyopaque, | 647 | userdata: ?*anyopaque, |
| 636 | dir: Io.Dir, | 648 | dir: Io.Dir, |
| 637 | sub_path: []const u8, | 649 | sub_path: []const u8, |
| ... | @@ -644,21 +656,256 @@ fn openFile( | ... | @@ -644,21 +656,256 @@ fn openFile( |
| 644 | return .{ .handle = fs_file.handle }; | 656 | return .{ .handle = fs_file.handle }; |
| 645 | } | 657 | } |
| 646 | 658 | ||
| 647 | fn closeFile(userdata: ?*anyopaque, file: Io.File) void { | 659 | fn fileClose(userdata: ?*anyopaque, file: Io.File) void { |
| 648 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | 660 | const pool: *Pool = @ptrCast(@alignCast(userdata)); |
| 649 | _ = pool; | 661 | _ = pool; |
| 650 | const fs_file: std.fs.File = .{ .handle = file.handle }; | 662 | const fs_file: std.fs.File = .{ .handle = file.handle }; |
| 651 | return fs_file.close(); | 663 | return fs_file.close(); |
| 652 | } | 664 | } |
| 653 | 665 | ||
| 654 | fn pread(userdata: ?*anyopaque, file: Io.File, buffer: []u8, offset: posix.off_t) Io.File.PReadError!usize { | 666 | fn fileReadStreaming(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File.ReadStreamingError!usize { |
| 655 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | 667 | const pool: *Pool = @ptrCast(@alignCast(userdata)); |
| 656 | try pool.checkCancel(); | 668 | |
| 657 | const fs_file: std.fs.File = .{ .handle = file.handle }; | 669 | if (is_windows) { |
| 658 | return switch (offset) { | 670 | const DWORD = windows.DWORD; |
| 659 | -1 => fs_file.read(buffer), | 671 | var index: usize = 0; |
| 660 | else => fs_file.pread(buffer, @bitCast(offset)), | 672 | var truncate: usize = 0; |
| 673 | var total: usize = 0; | ||
| 674 | while (index < data.len) { | ||
| 675 | try pool.checkCancel(); | ||
| 676 | { | ||
| 677 | const untruncated = data[index]; | ||
| 678 | data[index] = untruncated[truncate..]; | ||
| 679 | defer data[index] = untruncated; | ||
| 680 | const buffer = data[index..]; | ||
| 681 | const want_read_count: DWORD = @min(std.math.maxInt(DWORD), buffer.len); | ||
| 682 | var n: DWORD = undefined; | ||
| 683 | if (windows.kernel32.ReadFile(file.handle, buffer.ptr, want_read_count, &n, null) == 0) { | ||
| 684 | switch (windows.GetLastError()) { | ||
| 685 | .IO_PENDING => unreachable, | ||
| 686 | .OPERATION_ABORTED => continue, | ||
| 687 | .BROKEN_PIPE => return 0, | ||
| 688 | .HANDLE_EOF => return 0, | ||
| 689 | .NETNAME_DELETED => return error.ConnectionResetByPeer, | ||
| 690 | .LOCK_VIOLATION => return error.LockViolation, | ||
| 691 | .ACCESS_DENIED => return error.AccessDenied, | ||
| 692 | .INVALID_HANDLE => return error.NotOpenForReading, | ||
| 693 | else => |err| return windows.unexpectedError(err), | ||
| 694 | } | ||
| 695 | } | ||
| 696 | total += n; | ||
| 697 | truncate += n; | ||
| 698 | } | ||
| 699 | while (index < data.len and truncate >= data[index].len) { | ||
| 700 | truncate -= data[index].len; | ||
| 701 | index += 1; | ||
| 702 | } | ||
| 703 | } | ||
| 704 | return total; | ||
| 705 | } | ||
| 706 | |||
| 707 | var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined; | ||
| 708 | var i: usize = 0; | ||
| 709 | for (data) |buf| { | ||
| 710 | if (iovecs_buffer.len - i == 0) break; | ||
| 711 | if (buf.len != 0) { | ||
| 712 | iovecs_buffer[i] = .{ .base = buf.ptr, .len = buf.len }; | ||
| 713 | i += 1; | ||
| 714 | } | ||
| 715 | } | ||
| 716 | const dest = iovecs_buffer[0..i]; | ||
| 717 | assert(dest[0].len > 0); | ||
| 718 | |||
| 719 | if (native_os == .wasi and !builtin.link_libc) { | ||
| 720 | try pool.checkCancel(); | ||
| 721 | var nread: usize = undefined; | ||
| 722 | switch (std.os.wasi.fd_read(file.handle, dest.ptr, dest.len, &nread)) { | ||
| 723 | .SUCCESS => return nread, | ||
| 724 | .INTR => unreachable, | ||
| 725 | .INVAL => unreachable, | ||
| 726 | .FAULT => unreachable, | ||
| 727 | .AGAIN => unreachable, // currently not support in WASI | ||
| 728 | .BADF => return error.NotOpenForReading, // can be a race condition | ||
| 729 | .IO => return error.InputOutput, | ||
| 730 | .ISDIR => return error.IsDir, | ||
| 731 | .NOBUFS => return error.SystemResources, | ||
| 732 | .NOMEM => return error.SystemResources, | ||
| 733 | .NOTCONN => return error.SocketNotConnected, | ||
| 734 | .CONNRESET => return error.ConnectionResetByPeer, | ||
| 735 | .TIMEDOUT => return error.ConnectionTimedOut, | ||
| 736 | .NOTCAPABLE => return error.AccessDenied, | ||
| 737 | else => |err| return posix.unexpectedErrno(err), | ||
| 738 | } | ||
| 739 | } | ||
| 740 | |||
| 741 | while (true) { | ||
| 742 | try pool.checkCancel(); | ||
| 743 | const rc = posix.system.readv(file.handle, dest.ptr, dest.len); | ||
| 744 | switch (posix.errno(rc)) { | ||
| 745 | .SUCCESS => return @intCast(rc), | ||
| 746 | .INTR => continue, | ||
| 747 | .INVAL => unreachable, | ||
| 748 | .FAULT => unreachable, | ||
| 749 | .SRCH => return error.ProcessNotFound, | ||
| 750 | .AGAIN => return error.WouldBlock, | ||
| 751 | .BADF => return error.NotOpenForReading, // can be a race condition | ||
| 752 | .IO => return error.InputOutput, | ||
| 753 | .ISDIR => return error.IsDir, | ||
| 754 | .NOBUFS => return error.SystemResources, | ||
| 755 | .NOMEM => return error.SystemResources, | ||
| 756 | .NOTCONN => return error.SocketNotConnected, | ||
| 757 | .CONNRESET => return error.ConnectionResetByPeer, | ||
| 758 | .TIMEDOUT => return error.ConnectionTimedOut, | ||
| 759 | else => |err| return posix.unexpectedErrno(err), | ||
| 760 | } | ||
| 761 | } | ||
| 762 | } | ||
| 763 | |||
| 764 | fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset: u64) Io.File.ReadPositionalError!usize { | ||
| 765 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | ||
| 766 | |||
| 767 | const have_pread_but_not_preadv = switch (native_os) { | ||
| 768 | .windows, .macos, .ios, .watchos, .tvos, .visionos, .haiku, .serenity => true, | ||
| 769 | else => false, | ||
| 661 | }; | 770 | }; |
| 771 | if (have_pread_but_not_preadv) { | ||
| 772 | @compileError("TODO"); | ||
| 773 | } | ||
| 774 | |||
| 775 | if (is_windows) { | ||
| 776 | const DWORD = windows.DWORD; | ||
| 777 | const OVERLAPPED = windows.OVERLAPPED; | ||
| 778 | var index: usize = 0; | ||
| 779 | var truncate: usize = 0; | ||
| 780 | var total: usize = 0; | ||
| 781 | while (true) { | ||
| 782 | try pool.checkCancel(); | ||
| 783 | { | ||
| 784 | const untruncated = data[index]; | ||
| 785 | data[index] = untruncated[truncate..]; | ||
| 786 | defer data[index] = untruncated; | ||
| 787 | const buffer = data[index..]; | ||
| 788 | const want_read_count: DWORD = @min(std.math.maxInt(DWORD), buffer.len); | ||
| 789 | var n: DWORD = undefined; | ||
| 790 | var overlapped_data: OVERLAPPED = undefined; | ||
| 791 | const overlapped: ?*OVERLAPPED = if (offset) |off| blk: { | ||
| 792 | overlapped_data = .{ | ||
| 793 | .Internal = 0, | ||
| 794 | .InternalHigh = 0, | ||
| 795 | .DUMMYUNIONNAME = .{ | ||
| 796 | .DUMMYSTRUCTNAME = .{ | ||
| 797 | .Offset = @as(u32, @truncate(off)), | ||
| 798 | .OffsetHigh = @as(u32, @truncate(off >> 32)), | ||
| 799 | }, | ||
| 800 | }, | ||
| 801 | .hEvent = null, | ||
| 802 | }; | ||
| 803 | break :blk &overlapped_data; | ||
| 804 | } else null; | ||
| 805 | if (windows.kernel32.ReadFile(file.handle, buffer.ptr, want_read_count, &n, overlapped) == 0) { | ||
| 806 | switch (windows.GetLastError()) { | ||
| 807 | .IO_PENDING => unreachable, | ||
| 808 | .OPERATION_ABORTED => continue, | ||
| 809 | .BROKEN_PIPE => return 0, | ||
| 810 | .HANDLE_EOF => return 0, | ||
| 811 | .NETNAME_DELETED => return error.ConnectionResetByPeer, | ||
| 812 | .LOCK_VIOLATION => return error.LockViolation, | ||
| 813 | .ACCESS_DENIED => return error.AccessDenied, | ||
| 814 | .INVALID_HANDLE => return error.NotOpenForReading, | ||
| 815 | else => |err| return windows.unexpectedError(err), | ||
| 816 | } | ||
| 817 | } | ||
| 818 | total += n; | ||
| 819 | truncate += n; | ||
| 820 | } | ||
| 821 | while (index < data.len and truncate >= data[index].len) { | ||
| 822 | truncate -= data[index].len; | ||
| 823 | index += 1; | ||
| 824 | } | ||
| 825 | } | ||
| 826 | return total; | ||
| 827 | } | ||
| 828 | |||
| 829 | var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined; | ||
| 830 | var i: usize = 0; | ||
| 831 | for (data) |buf| { | ||
| 832 | if (iovecs_buffer.len - i == 0) break; | ||
| 833 | if (buf.len != 0) { | ||
| 834 | iovecs_buffer[i] = .{ .base = buf.ptr, .len = buf.len }; | ||
| 835 | i += 1; | ||
| 836 | } | ||
| 837 | } | ||
| 838 | const dest = iovecs_buffer[0..i]; | ||
| 839 | assert(dest[0].len > 0); | ||
| 840 | |||
| 841 | if (native_os == .wasi and !builtin.link_libc) { | ||
| 842 | try pool.checkCancel(); | ||
| 843 | var nread: usize = undefined; | ||
| 844 | switch (std.os.wasi.fd_pread(file.handle, dest.ptr, dest.len, offset, &nread)) { | ||
| 845 | .SUCCESS => return nread, | ||
| 846 | .INTR => unreachable, | ||
| 847 | .INVAL => unreachable, | ||
| 848 | .FAULT => unreachable, | ||
| 849 | .AGAIN => unreachable, | ||
| 850 | .BADF => return error.NotOpenForReading, // can be a race condition | ||
| 851 | .IO => return error.InputOutput, | ||
| 852 | .ISDIR => return error.IsDir, | ||
| 853 | .NOBUFS => return error.SystemResources, | ||
| 854 | .NOMEM => return error.SystemResources, | ||
| 855 | .NOTCONN => return error.SocketNotConnected, | ||
| 856 | .CONNRESET => return error.ConnectionResetByPeer, | ||
| 857 | .TIMEDOUT => return error.ConnectionTimedOut, | ||
| 858 | .NXIO => return error.Unseekable, | ||
| 859 | .SPIPE => return error.Unseekable, | ||
| 860 | .OVERFLOW => return error.Unseekable, | ||
| 861 | .NOTCAPABLE => return error.AccessDenied, | ||
| 862 | else => |err| return posix.unexpectedErrno(err), | ||
| 863 | } | ||
| 864 | } | ||
| 865 | |||
| 866 | const preadv_sym = if (posix.lfs64_abi) posix.system.preadv64 else posix.system.preadv; | ||
| 867 | while (true) { | ||
| 868 | try pool.checkCancel(); | ||
| 869 | const rc = preadv_sym(file.handle, dest.ptr, dest.len, @bitCast(offset)); | ||
| 870 | switch (posix.errno(rc)) { | ||
| 871 | .SUCCESS => return @bitCast(rc), | ||
| 872 | .INTR => continue, | ||
| 873 | .INVAL => unreachable, | ||
| 874 | .FAULT => unreachable, | ||
| 875 | .SRCH => return error.ProcessNotFound, | ||
| 876 | .AGAIN => return error.WouldBlock, | ||
| 877 | .BADF => return error.NotOpenForReading, // can be a race condition | ||
| 878 | .IO => return error.InputOutput, | ||
| 879 | .ISDIR => return error.IsDir, | ||
| 880 | .NOBUFS => return error.SystemResources, | ||
| 881 | .NOMEM => return error.SystemResources, | ||
| 882 | .NOTCONN => return error.SocketNotConnected, | ||
| 883 | .CONNRESET => return error.ConnectionResetByPeer, | ||
| 884 | .TIMEDOUT => return error.ConnectionTimedOut, | ||
| 885 | .NXIO => return error.Unseekable, | ||
| 886 | .SPIPE => return error.Unseekable, | ||
| 887 | .OVERFLOW => return error.Unseekable, | ||
| 888 | else => |err| return posix.unexpectedErrno(err), | ||
| 889 | } | ||
| 890 | } | ||
| 891 | } | ||
| 892 | |||
| 893 | fn fileSeekBy(userdata: ?*anyopaque, file: Io.File, offset: i64) Io.File.SeekError!void { | ||
| 894 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | ||
| 895 | try pool.checkCancel(); | ||
| 896 | |||
| 897 | _ = file; | ||
| 898 | _ = offset; | ||
| 899 | @panic("TODO"); | ||
| 900 | } | ||
| 901 | |||
| 902 | fn fileSeekTo(userdata: ?*anyopaque, file: Io.File, offset: u64) Io.File.SeekError!void { | ||
| 903 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | ||
| 904 | try pool.checkCancel(); | ||
| 905 | |||
| 906 | _ = file; | ||
| 907 | _ = offset; | ||
| 908 | @panic("TODO"); | ||
| 662 | } | 909 | } |
| 663 | 910 | ||
| 664 | fn pwrite(userdata: ?*anyopaque, file: Io.File, buffer: []const u8, offset: posix.off_t) Io.File.PWriteError!usize { | 911 | fn pwrite(userdata: ?*anyopaque, file: Io.File, buffer: []const u8, offset: posix.off_t) Io.File.PWriteError!usize { |
lib/std/Io/Writer.zig+3-2| ... | @@ -5,7 +5,7 @@ const Writer = @This(); | ... | @@ -5,7 +5,7 @@ const Writer = @This(); |
| 5 | const std = @import("../std.zig"); | 5 | const std = @import("../std.zig"); |
| 6 | const assert = std.debug.assert; | 6 | const assert = std.debug.assert; |
| 7 | const Limit = std.Io.Limit; | 7 | const Limit = std.Io.Limit; |
| 8 | const File = std.fs.File; | 8 | const File = std.Io.File; |
| 9 | const testing = std.testing; | 9 | const testing = std.testing; |
| 10 | const Allocator = std.mem.Allocator; | 10 | const Allocator = std.mem.Allocator; |
| 11 | const ArrayList = std.ArrayList; | 11 | const ArrayList = std.ArrayList; |
| ... | @@ -2778,7 +2778,8 @@ pub const Allocating = struct { | ... | @@ -2778,7 +2778,8 @@ pub const Allocating = struct { |
| 2778 | if (additional == 0) return error.EndOfStream; | 2778 | if (additional == 0) return error.EndOfStream; |
| 2779 | a.ensureUnusedCapacity(limit.minInt64(additional)) catch return error.WriteFailed; | 2779 | a.ensureUnusedCapacity(limit.minInt64(additional)) catch return error.WriteFailed; |
| 2780 | const dest = limit.slice(a.writer.buffer[a.writer.end..]); | 2780 | const dest = limit.slice(a.writer.buffer[a.writer.end..]); |
| 2781 | const n = try file_reader.read(dest); | 2781 | const n = try file_reader.interface.readSliceShort(dest); |
| 2782 | if (n == 0) return error.EndOfStream; | ||
| 2782 | a.writer.end += n; | 2783 | a.writer.end += n; |
| 2783 | return n; | 2784 | return n; |
| 2784 | } | 2785 | } |
lib/std/Io/net.zig+52-32| ... | @@ -17,9 +17,12 @@ pub const ListenOptions = struct { | ... | @@ -17,9 +17,12 @@ pub const ListenOptions = struct { |
| 17 | force_nonblocking: bool = false, | 17 | force_nonblocking: bool = false, |
| 18 | }; | 18 | }; |
| 19 | 19 | ||
| 20 | /// An already-validated host name. | 20 | /// An already-validated host name. A valid host name: |
| 21 | /// * Has length less than or equal to `max_len`. | ||
| 22 | /// * Is valid UTF-8. | ||
| 23 | /// * Lacks ASCII characters other than alphanumeric, '-', and '.'. | ||
| 21 | pub const HostName = struct { | 24 | pub const HostName = struct { |
| 22 | /// Externally managed memory. Already checked to be within `max_len`. | 25 | /// Externally managed memory. Already checked to be valid. |
| 23 | bytes: []const u8, | 26 | bytes: []const u8, |
| 24 | 27 | ||
| 25 | pub const max_len = 255; | 28 | pub const max_len = 255; |
| ... | @@ -55,13 +58,14 @@ pub const HostName = struct { | ... | @@ -55,13 +58,14 @@ pub const HostName = struct { |
| 55 | family: ?IpAddress.Tag = null, | 58 | family: ?IpAddress.Tag = null, |
| 56 | }; | 59 | }; |
| 57 | 60 | ||
| 58 | pub const LookupError = Io.Cancelable || error{}; | 61 | pub const LookupError = Io.Cancelable || Io.File.OpenError || Io.File.Reader.Error || error{ |
| 62 | UnknownHostName, | ||
| 63 | }; | ||
| 59 | 64 | ||
| 60 | pub const LookupResult = struct { | 65 | pub const LookupResult = struct { |
| 61 | /// How many `LookupOptions.addresses_buffer` elements are populated. | 66 | /// How many `LookupOptions.addresses_buffer` elements are populated. |
| 62 | addresses_len: usize, | 67 | addresses_len: usize = 0, |
| 63 | /// Length zero means no canonical name returned. | 68 | canonical_name: ?HostName = null, |
| 64 | canonical_name_len: usize, | ||
| 65 | }; | 69 | }; |
| 66 | 70 | ||
| 67 | pub fn lookup(host_name: HostName, io: Io, options: LookupOptions) LookupError!LookupResult { | 71 | pub fn lookup(host_name: HostName, io: Io, options: LookupOptions) LookupError!LookupResult { |
| ... | @@ -75,17 +79,17 @@ pub const HostName = struct { | ... | @@ -75,17 +79,17 @@ pub const HostName = struct { |
| 75 | if (options.family != .ip6) { | 79 | if (options.family != .ip6) { |
| 76 | if (IpAddress.parseIp4(name, options.port)) |addr| { | 80 | if (IpAddress.parseIp4(name, options.port)) |addr| { |
| 77 | options.addresses_buffer[0] = addr; | 81 | options.addresses_buffer[0] = addr; |
| 78 | return .{ .addresses_len = 1, .canonical_name_len = 0 }; | 82 | return .{ .addresses_len = 1 }; |
| 79 | } else |_| {} | 83 | } else |_| {} |
| 80 | } | 84 | } |
| 81 | if (options.family != .ip4) { | 85 | if (options.family != .ip4) { |
| 82 | if (IpAddress.parseIp6(name, options.port)) |addr| { | 86 | if (IpAddress.parseIp6(name, options.port)) |addr| { |
| 83 | options.addresses_buffer[0] = addr; | 87 | options.addresses_buffer[0] = addr; |
| 84 | return .{ .addresses_len = 1, .canonical_name_len = 0 }; | 88 | return .{ .addresses_len = 1 }; |
| 85 | } else |_| {} | 89 | } else |_| {} |
| 86 | } | 90 | } |
| 87 | { | 91 | { |
| 88 | const result = try lookupHosts(io, options); | 92 | const result = try lookupHosts(host_name, io, options); |
| 89 | if (result.addresses_len > 0) return sortLookupResults(options, result); | 93 | if (result.addresses_len > 0) return sortLookupResults(options, result); |
| 90 | } | 94 | } |
| 91 | { | 95 | { |
| ... | @@ -110,8 +114,12 @@ pub const HostName = struct { | ... | @@ -110,8 +114,12 @@ pub const HostName = struct { |
| 110 | i += 1; | 114 | i += 1; |
| 111 | } | 115 | } |
| 112 | const canon_name = "localhost"; | 116 | const canon_name = "localhost"; |
| 113 | options.canonical_name_buffer[0..canon_name.len].* = canon_name.*; | 117 | const canon_name_dest = options.canonical_name_buffer[0..canon_name.len]; |
| 114 | return sortLookupResults(options, .{ .addresses_len = i, .canonical_name_len = canon_name.len }); | 118 | canon_name_dest.* = canon_name.*; |
| 119 | return sortLookupResults(options, .{ | ||
| 120 | .addresses_len = i, | ||
| 121 | .canonical_name = .{ .bytes = canon_name_dest }, | ||
| 122 | }); | ||
| 115 | } | 123 | } |
| 116 | } | 124 | } |
| 117 | { | 125 | { |
| ... | @@ -135,27 +143,27 @@ pub const HostName = struct { | ... | @@ -135,27 +143,27 @@ pub const HostName = struct { |
| 135 | @panic("TODO"); | 143 | @panic("TODO"); |
| 136 | } | 144 | } |
| 137 | 145 | ||
| 138 | fn lookupHosts(io: Io, options: LookupOptions) !LookupResult { | 146 | fn lookupHosts(host_name: HostName, io: Io, options: LookupOptions) !LookupResult { |
| 139 | const file = Io.File.openFileAbsoluteZ(io, "/etc/hosts", .{}) catch |err| switch (err) { | 147 | const file = Io.File.openAbsolute(io, "/etc/hosts", .{}) catch |err| switch (err) { |
| 140 | error.FileNotFound, | 148 | error.FileNotFound, |
| 141 | error.NotDir, | 149 | error.NotDir, |
| 142 | error.AccessDenied, | 150 | error.AccessDenied, |
| 143 | => return, | 151 | => return .{}, |
| 152 | |||
| 144 | else => |e| return e, | 153 | else => |e| return e, |
| 145 | }; | 154 | }; |
| 146 | defer file.close(); | 155 | defer file.close(io); |
| 147 | 156 | ||
| 148 | var line_buf: [512]u8 = undefined; | 157 | var line_buf: [512]u8 = undefined; |
| 149 | var file_reader = file.reader(io, &line_buf); | 158 | var file_reader = file.reader(io, &line_buf); |
| 150 | return lookupHostsReader(options, &file_reader.interface) catch |err| switch (err) { | 159 | return lookupHostsReader(host_name, options, &file_reader.interface) catch |err| switch (err) { |
| 151 | error.OutOfMemory => return error.OutOfMemory, | ||
| 152 | error.ReadFailed => return file_reader.err.?, | 160 | error.ReadFailed => return file_reader.err.?, |
| 153 | }; | 161 | }; |
| 154 | } | 162 | } |
| 155 | 163 | ||
| 156 | fn lookupHostsReader(options: LookupOptions, reader: *Io.Reader) error{ReadFailed}!LookupResult { | 164 | fn lookupHostsReader(host_name: HostName, options: LookupOptions, reader: *Io.Reader) error{ReadFailed}!LookupResult { |
| 157 | var addresses_len: usize = 0; | 165 | var addresses_len: usize = 0; |
| 158 | var canonical_name_len: usize = 0; | 166 | var canonical_name: ?HostName = null; |
| 159 | while (true) { | 167 | while (true) { |
| 160 | const line = reader.takeDelimiterExclusive('\n') catch |err| switch (err) { | 168 | const line = reader.takeDelimiterExclusive('\n') catch |err| switch (err) { |
| 161 | error.StreamTooLong => { | 169 | error.StreamTooLong => { |
| ... | @@ -176,19 +184,20 @@ pub const HostName = struct { | ... | @@ -176,19 +184,20 @@ pub const HostName = struct { |
| 176 | const ip_text = line_it.next() orelse continue; | 184 | const ip_text = line_it.next() orelse continue; |
| 177 | var first_name_text: ?[]const u8 = null; | 185 | var first_name_text: ?[]const u8 = null; |
| 178 | while (line_it.next()) |name_text| { | 186 | while (line_it.next()) |name_text| { |
| 179 | if (std.mem.eql(u8, name_text, options.name)) { | 187 | if (std.mem.eql(u8, name_text, host_name.bytes)) { |
| 180 | if (first_name_text == null) first_name_text = name_text; | 188 | if (first_name_text == null) first_name_text = name_text; |
| 181 | break; | 189 | break; |
| 182 | } | 190 | } |
| 183 | } else continue; | 191 | } else continue; |
| 184 | 192 | ||
| 185 | if (canonical_name_len == 0) { | 193 | if (canonical_name == null) { |
| 186 | if (HostName.init(first_name_text)) |name_text| { | 194 | if (HostName.init(first_name_text.?)) |name_text| { |
| 187 | if (name_text.len <= options.canonical_name_buffer.len) { | 195 | if (name_text.bytes.len <= options.canonical_name_buffer.len) { |
| 188 | @memcpy(options.canonical_name_buffer[0..name_text.len], name_text); | 196 | const canonical_name_dest = options.canonical_name_buffer[0..name_text.bytes.len]; |
| 189 | canonical_name_len = name_text.len; | 197 | @memcpy(canonical_name_dest, name_text.bytes); |
| 198 | canonical_name = .{ .bytes = canonical_name_dest }; | ||
| 190 | } | 199 | } |
| 191 | } | 200 | } else |_| {} |
| 192 | } | 201 | } |
| 193 | 202 | ||
| 194 | if (options.family != .ip6) { | 203 | if (options.family != .ip6) { |
| ... | @@ -197,7 +206,7 @@ pub const HostName = struct { | ... | @@ -197,7 +206,7 @@ pub const HostName = struct { |
| 197 | addresses_len += 1; | 206 | addresses_len += 1; |
| 198 | if (options.addresses_buffer.len - addresses_len == 0) return .{ | 207 | if (options.addresses_buffer.len - addresses_len == 0) return .{ |
| 199 | .addresses_len = addresses_len, | 208 | .addresses_len = addresses_len, |
| 200 | .canonical_name_len = canonical_name_len, | 209 | .canonical_name = canonical_name, |
| 201 | }; | 210 | }; |
| 202 | } else |_| {} | 211 | } else |_| {} |
| 203 | } | 212 | } |
| ... | @@ -207,11 +216,15 @@ pub const HostName = struct { | ... | @@ -207,11 +216,15 @@ pub const HostName = struct { |
| 207 | addresses_len += 1; | 216 | addresses_len += 1; |
| 208 | if (options.addresses_buffer.len - addresses_len == 0) return .{ | 217 | if (options.addresses_buffer.len - addresses_len == 0) return .{ |
| 209 | .addresses_len = addresses_len, | 218 | .addresses_len = addresses_len, |
| 210 | .canonical_name_len = canonical_name_len, | 219 | .canonical_name = canonical_name, |
| 211 | }; | 220 | }; |
| 212 | } else |_| {} | 221 | } else |_| {} |
| 213 | } | 222 | } |
| 214 | } | 223 | } |
| 224 | return .{ | ||
| 225 | .addresses_len = addresses_len, | ||
| 226 | .canonical_name = canonical_name, | ||
| 227 | }; | ||
| 215 | } | 228 | } |
| 216 | 229 | ||
| 217 | pub const ConnectTcpError = LookupError || IpAddress.ConnectTcpError; | 230 | pub const ConnectTcpError = LookupError || IpAddress.ConnectTcpError; |
| ... | @@ -289,9 +302,9 @@ pub const IpAddress = union(enum) { | ... | @@ -289,9 +302,9 @@ pub const IpAddress = union(enum) { |
| 289 | } | 302 | } |
| 290 | } | 303 | } |
| 291 | 304 | ||
| 292 | pub fn format(a: IpAddress, w: *std.io.Writer) std.io.Writer.Error!void { | 305 | pub fn format(a: IpAddress, w: *Io.Writer) Io.Writer.Error!void { |
| 293 | switch (a) { | 306 | switch (a) { |
| 294 | .ip4, .ip6 => |x| return x.format(w), | 307 | inline .ip4, .ip6 => |x| return x.format(w), |
| 295 | } | 308 | } |
| 296 | } | 309 | } |
| 297 | 310 | ||
| ... | @@ -365,7 +378,7 @@ pub const Ip4Address = struct { | ... | @@ -365,7 +378,7 @@ pub const Ip4Address = struct { |
| 365 | return error.Incomplete; | 378 | return error.Incomplete; |
| 366 | } | 379 | } |
| 367 | 380 | ||
| 368 | pub fn format(a: Ip4Address, w: *std.io.Writer) std.io.Writer.Error!void { | 381 | pub fn format(a: Ip4Address, w: *Io.Writer) Io.Writer.Error!void { |
| 369 | const bytes = &a.bytes; | 382 | const bytes = &a.bytes; |
| 370 | try w.print("{d}.{d}.{d}.{d}:{d}", .{ bytes[0], bytes[1], bytes[2], bytes[3], a.port }); | 383 | try w.print("{d}.{d}.{d}.{d}:{d}", .{ bytes[0], bytes[1], bytes[2], bytes[3], a.port }); |
| 371 | } | 384 | } |
| ... | @@ -393,6 +406,13 @@ pub const Ip6Address = struct { | ... | @@ -393,6 +406,13 @@ pub const Ip6Address = struct { |
| 393 | Incomplete, | 406 | Incomplete, |
| 394 | }; | 407 | }; |
| 395 | 408 | ||
| 409 | pub fn localhost(port: u16) Ip6Address { | ||
| 410 | return .{ | ||
| 411 | .bytes = .{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, | ||
| 412 | .port = port, | ||
| 413 | }; | ||
| 414 | } | ||
| 415 | |||
| 396 | pub fn parse(buffer: []const u8, port: u16) ParseError!Ip6Address { | 416 | pub fn parse(buffer: []const u8, port: u16) ParseError!Ip6Address { |
| 397 | var result: Ip6Address = .{ | 417 | var result: Ip6Address = .{ |
| 398 | .port = port, | 418 | .port = port, |
| ... | @@ -504,7 +524,7 @@ pub const Ip6Address = struct { | ... | @@ -504,7 +524,7 @@ pub const Ip6Address = struct { |
| 504 | } | 524 | } |
| 505 | } | 525 | } |
| 506 | 526 | ||
| 507 | pub fn format(a: Ip6Address, w: *std.io.Writer) std.io.Writer.Error!void { | 527 | pub fn format(a: Ip6Address, w: *Io.Writer) Io.Writer.Error!void { |
| 508 | const bytes = &a.bytes; | 528 | const bytes = &a.bytes; |
| 509 | if (std.mem.eql(u8, bytes[0..12], &[_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff })) { | 529 | if (std.mem.eql(u8, bytes[0..12], &[_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff })) { |
| 510 | try w.print("[::ffff:{d}.{d}.{d}.{d}]:{d}", .{ | 530 | try w.print("[::ffff:{d}.{d}.{d}.{d}]:{d}", .{ |
lib/std/fs/File.zig+7-124| ... | @@ -17,25 +17,12 @@ const Alignment = std.mem.Alignment; | ... | @@ -17,25 +17,12 @@ const Alignment = std.mem.Alignment; |
| 17 | /// The OS-specific file descriptor or file handle. | 17 | /// The OS-specific file descriptor or file handle. |
| 18 | handle: Handle, | 18 | handle: Handle, |
| 19 | 19 | ||
| 20 | pub const Handle = posix.fd_t; | 20 | pub const Handle = std.Io.File.Handle; |
| 21 | pub const Mode = posix.mode_t; | 21 | pub const Mode = std.Io.File.Mode; |
| 22 | pub const INode = posix.ino_t; | 22 | pub const INode = std.Io.File.INode; |
| 23 | pub const Uid = posix.uid_t; | 23 | pub const Uid = posix.uid_t; |
| 24 | pub const Gid = posix.gid_t; | 24 | pub const Gid = posix.gid_t; |
| 25 | 25 | pub const Kind = std.Io.File.Kind; | |
| 26 | pub const Kind = enum { | ||
| 27 | block_device, | ||
| 28 | character_device, | ||
| 29 | directory, | ||
| 30 | named_pipe, | ||
| 31 | sym_link, | ||
| 32 | file, | ||
| 33 | unix_domain_socket, | ||
| 34 | whiteout, | ||
| 35 | door, | ||
| 36 | event_port, | ||
| 37 | unknown, | ||
| 38 | }; | ||
| 39 | 26 | ||
| 40 | /// This is the default mode given to POSIX operating systems for creating | 27 | /// This is the default mode given to POSIX operating systems for creating |
| 41 | /// files. `0o666` is "-rw-rw-rw-" which is counter-intuitive at first, | 28 | /// files. `0o666` is "-rw-rw-rw-" which is counter-intuitive at first, |
| ... | @@ -399,115 +386,11 @@ pub fn mode(self: File) ModeError!Mode { | ... | @@ -399,115 +386,11 @@ pub fn mode(self: File) ModeError!Mode { |
| 399 | return (try self.stat()).mode; | 386 | return (try self.stat()).mode; |
| 400 | } | 387 | } |
| 401 | 388 | ||
| 402 | pub const Stat = struct { | 389 | pub const Stat = std.Io.File.Stat; |
| 403 | /// A number that the system uses to point to the file metadata. This | ||
| 404 | /// number is not guaranteed to be unique across time, as some file | ||
| 405 | /// systems may reuse an inode after its file has been deleted. Some | ||
| 406 | /// systems may change the inode of a file over time. | ||
| 407 | /// | ||
| 408 | /// On Linux, the inode is a structure that stores the metadata, and | ||
| 409 | /// the inode _number_ is what you see here: the index number of the | ||
| 410 | /// inode. | ||
| 411 | /// | ||
| 412 | /// The FileIndex on Windows is similar. It is a number for a file that | ||
| 413 | /// is unique to each filesystem. | ||
| 414 | inode: INode, | ||
| 415 | size: u64, | ||
| 416 | /// This is available on POSIX systems and is always 0 otherwise. | ||
| 417 | mode: Mode, | ||
| 418 | kind: Kind, | ||
| 419 | |||
| 420 | /// Last access time in nanoseconds, relative to UTC 1970-01-01. | ||
| 421 | atime: i128, | ||
| 422 | /// Last modification time in nanoseconds, relative to UTC 1970-01-01. | ||
| 423 | mtime: i128, | ||
| 424 | /// Last status/metadata change time in nanoseconds, relative to UTC 1970-01-01. | ||
| 425 | ctime: i128, | ||
| 426 | |||
| 427 | pub fn fromPosix(st: posix.Stat) Stat { | ||
| 428 | const atime = st.atime(); | ||
| 429 | const mtime = st.mtime(); | ||
| 430 | const ctime = st.ctime(); | ||
| 431 | return .{ | ||
| 432 | .inode = st.ino, | ||
| 433 | .size = @bitCast(st.size), | ||
| 434 | .mode = st.mode, | ||
| 435 | .kind = k: { | ||
| 436 | const m = st.mode & posix.S.IFMT; | ||
| 437 | switch (m) { | ||
| 438 | posix.S.IFBLK => break :k .block_device, | ||
| 439 | posix.S.IFCHR => break :k .character_device, | ||
| 440 | posix.S.IFDIR => break :k .directory, | ||
| 441 | posix.S.IFIFO => break :k .named_pipe, | ||
| 442 | posix.S.IFLNK => break :k .sym_link, | ||
| 443 | posix.S.IFREG => break :k .file, | ||
| 444 | posix.S.IFSOCK => break :k .unix_domain_socket, | ||
| 445 | else => {}, | ||
| 446 | } | ||
| 447 | if (builtin.os.tag.isSolarish()) switch (m) { | ||
| 448 | posix.S.IFDOOR => break :k .door, | ||
| 449 | posix.S.IFPORT => break :k .event_port, | ||
| 450 | else => {}, | ||
| 451 | }; | ||
| 452 | |||
| 453 | break :k .unknown; | ||
| 454 | }, | ||
| 455 | .atime = @as(i128, atime.sec) * std.time.ns_per_s + atime.nsec, | ||
| 456 | .mtime = @as(i128, mtime.sec) * std.time.ns_per_s + mtime.nsec, | ||
| 457 | .ctime = @as(i128, ctime.sec) * std.time.ns_per_s + ctime.nsec, | ||
| 458 | }; | ||
| 459 | } | ||
| 460 | |||
| 461 | pub fn fromLinux(stx: linux.Statx) Stat { | ||
| 462 | const atime = stx.atime; | ||
| 463 | const mtime = stx.mtime; | ||
| 464 | const ctime = stx.ctime; | ||
| 465 | |||
| 466 | return .{ | ||
| 467 | .inode = stx.ino, | ||
| 468 | .size = stx.size, | ||
| 469 | .mode = stx.mode, | ||
| 470 | .kind = switch (stx.mode & linux.S.IFMT) { | ||
| 471 | linux.S.IFDIR => .directory, | ||
| 472 | linux.S.IFCHR => .character_device, | ||
| 473 | linux.S.IFBLK => .block_device, | ||
| 474 | linux.S.IFREG => .file, | ||
| 475 | linux.S.IFIFO => .named_pipe, | ||
| 476 | linux.S.IFLNK => .sym_link, | ||
| 477 | linux.S.IFSOCK => .unix_domain_socket, | ||
| 478 | else => .unknown, | ||
| 479 | }, | ||
| 480 | .atime = @as(i128, atime.sec) * std.time.ns_per_s + atime.nsec, | ||
| 481 | .mtime = @as(i128, mtime.sec) * std.time.ns_per_s + mtime.nsec, | ||
| 482 | .ctime = @as(i128, ctime.sec) * std.time.ns_per_s + ctime.nsec, | ||
| 483 | }; | ||
| 484 | } | ||
| 485 | |||
| 486 | pub fn fromWasi(st: std.os.wasi.filestat_t) Stat { | ||
| 487 | return .{ | ||
| 488 | .inode = st.ino, | ||
| 489 | .size = @bitCast(st.size), | ||
| 490 | .mode = 0, | ||
| 491 | .kind = switch (st.filetype) { | ||
| 492 | .BLOCK_DEVICE => .block_device, | ||
| 493 | .CHARACTER_DEVICE => .character_device, | ||
| 494 | .DIRECTORY => .directory, | ||
| 495 | .SYMBOLIC_LINK => .sym_link, | ||
| 496 | .REGULAR_FILE => .file, | ||
| 497 | .SOCKET_STREAM, .SOCKET_DGRAM => .unix_domain_socket, | ||
| 498 | else => .unknown, | ||
| 499 | }, | ||
| 500 | .atime = st.atim, | ||
| 501 | .mtime = st.mtim, | ||
| 502 | .ctime = st.ctim, | ||
| 503 | }; | ||
| 504 | } | ||
| 505 | }; | ||
| 506 | 390 | ||
| 507 | pub const StatError = posix.FStatError; | 391 | pub const StatError = posix.FStatError; |
| 508 | 392 | ||
| 509 | /// Returns `Stat` containing basic information about the `File`. | 393 | /// Returns `Stat` containing basic information about the `File`. |
| 510 | /// TODO: integrate with async I/O | ||
| 511 | pub fn stat(self: File) StatError!Stat { | 394 | pub fn stat(self: File) StatError!Stat { |
| 512 | if (builtin.os.tag == .windows) { | 395 | if (builtin.os.tag == .windows) { |
| 513 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | 396 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; |
| ... | @@ -1727,7 +1610,7 @@ pub const Writer = struct { | ... | @@ -1727,7 +1610,7 @@ pub const Writer = struct { |
| 1727 | 1610 | ||
| 1728 | pub fn sendFile( | 1611 | pub fn sendFile( |
| 1729 | io_w: *std.Io.Writer, | 1612 | io_w: *std.Io.Writer, |
| 1730 | file_reader: *Reader, | 1613 | file_reader: *std.Io.File.Reader, |
| 1731 | limit: std.Io.Limit, | 1614 | limit: std.Io.Limit, |
| 1732 | ) std.Io.Writer.FileError!usize { | 1615 | ) std.Io.Writer.FileError!usize { |
| 1733 | const reader_buffered = file_reader.interface.buffered(); | 1616 | const reader_buffered = file_reader.interface.buffered(); |
| ... | @@ -1994,7 +1877,7 @@ pub const Writer = struct { | ... | @@ -1994,7 +1877,7 @@ pub const Writer = struct { |
| 1994 | 1877 | ||
| 1995 | fn sendFileBuffered( | 1878 | fn sendFileBuffered( |
| 1996 | io_w: *std.Io.Writer, | 1879 | io_w: *std.Io.Writer, |
| 1997 | file_reader: *Reader, | 1880 | file_reader: *std.Io.File.Reader, |
| 1998 | reader_buffered: []const u8, | 1881 | reader_buffered: []const u8, |
| 1999 | ) std.Io.Writer.FileError!usize { | 1882 | ) std.Io.Writer.FileError!usize { |
| 2000 | const n = try drain(io_w, &.{reader_buffered}, 1); | 1883 | const n = try drain(io_w, &.{reader_buffered}, 1); |
lib/std/posix.zig+5-51| ... | @@ -806,36 +806,7 @@ pub fn exit(status: u8) noreturn { | ... | @@ -806,36 +806,7 @@ pub fn exit(status: u8) noreturn { |
| 806 | system.exit(status); | 806 | system.exit(status); |
| 807 | } | 807 | } |
| 808 | 808 | ||
| 809 | pub const ReadError = error{ | 809 | pub const ReadError = std.Io.File.ReadStreamingError; |
| 810 | InputOutput, | ||
| 811 | SystemResources, | ||
| 812 | IsDir, | ||
| 813 | OperationAborted, | ||
| 814 | BrokenPipe, | ||
| 815 | ConnectionResetByPeer, | ||
| 816 | ConnectionTimedOut, | ||
| 817 | NotOpenForReading, | ||
| 818 | SocketNotConnected, | ||
| 819 | |||
| 820 | /// This error occurs when no global event loop is configured, | ||
| 821 | /// and reading from the file descriptor would block. | ||
| 822 | WouldBlock, | ||
| 823 | |||
| 824 | /// reading a timerfd with CANCEL_ON_SET will lead to this error | ||
| 825 | /// when the clock goes through a discontinuous change | ||
| 826 | Canceled, | ||
| 827 | |||
| 828 | /// In WASI, this error occurs when the file descriptor does | ||
| 829 | /// not hold the required rights to read from it. | ||
| 830 | AccessDenied, | ||
| 831 | |||
| 832 | /// This error occurs in Linux if the process to be read from | ||
| 833 | /// no longer exists. | ||
| 834 | ProcessNotFound, | ||
| 835 | |||
| 836 | /// Unable to read file due to lock. | ||
| 837 | LockViolation, | ||
| 838 | } || UnexpectedError; | ||
| 839 | 810 | ||
| 840 | /// Returns the number of bytes that were read, which can be less than | 811 | /// Returns the number of bytes that were read, which can be less than |
| 841 | /// buf.len. If 0 bytes were read, that means EOF. | 812 | /// buf.len. If 0 bytes were read, that means EOF. |
| ... | @@ -922,7 +893,6 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { | ... | @@ -922,7 +893,6 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { |
| 922 | /// a pointer within the address space of the application. | 893 | /// a pointer within the address space of the application. |
| 923 | pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { | 894 | pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { |
| 924 | if (native_os == .windows) { | 895 | if (native_os == .windows) { |
| 925 | // TODO improve this to use ReadFileScatter | ||
| 926 | if (iov.len == 0) return 0; | 896 | if (iov.len == 0) return 0; |
| 927 | const first = iov[0]; | 897 | const first = iov[0]; |
| 928 | return read(fd, first.base[0..first.len]); | 898 | return read(fd, first.base[0..first.len]); |
| ... | @@ -970,7 +940,7 @@ pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { | ... | @@ -970,7 +940,7 @@ pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { |
| 970 | } | 940 | } |
| 971 | } | 941 | } |
| 972 | 942 | ||
| 973 | pub const PReadError = ReadError || error{Unseekable}; | 943 | pub const PReadError = std.Io.ReadPositionalError; |
| 974 | 944 | ||
| 975 | /// Number of bytes read is returned. Upon reading end-of-file, zero is returned. | 945 | /// Number of bytes read is returned. Upon reading end-of-file, zero is returned. |
| 976 | /// | 946 | /// |
| ... | @@ -5376,13 +5346,7 @@ pub fn gettimeofday(tv: ?*timeval, tz: ?*timezone) void { | ... | @@ -5376,13 +5346,7 @@ pub fn gettimeofday(tv: ?*timeval, tz: ?*timezone) void { |
| 5376 | } | 5346 | } |
| 5377 | } | 5347 | } |
| 5378 | 5348 | ||
| 5379 | pub const SeekError = error{ | 5349 | pub const SeekError = std.Io.File.SeekError; |
| 5380 | Unseekable, | ||
| 5381 | |||
| 5382 | /// In WASI, this error may occur when the file descriptor does | ||
| 5383 | /// not hold the required rights to seek on it. | ||
| 5384 | AccessDenied, | ||
| 5385 | } || UnexpectedError; | ||
| 5386 | 5350 | ||
| 5387 | /// Repositions read/write file offset relative to the beginning. | 5351 | /// Repositions read/write file offset relative to the beginning. |
| 5388 | pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void { | 5352 | pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void { |
| ... | @@ -7558,7 +7522,7 @@ pub fn ioctl_SIOCGIFINDEX(fd: fd_t, ifr: *ifreq) IoCtl_SIOCGIFINDEX_Error!void { | ... | @@ -7558,7 +7522,7 @@ pub fn ioctl_SIOCGIFINDEX(fd: fd_t, ifr: *ifreq) IoCtl_SIOCGIFINDEX_Error!void { |
| 7558 | } | 7522 | } |
| 7559 | } | 7523 | } |
| 7560 | 7524 | ||
| 7561 | const lfs64_abi = native_os == .linux and builtin.link_libc and (builtin.abi.isGnu() or builtin.abi.isAndroid()); | 7525 | pub const lfs64_abi = native_os == .linux and builtin.link_libc and (builtin.abi.isGnu() or builtin.abi.isAndroid()); |
| 7562 | 7526 | ||
| 7563 | /// Whether or not `error.Unexpected` will print its value and a stack trace. | 7527 | /// Whether or not `error.Unexpected` will print its value and a stack trace. |
| 7564 | /// | 7528 | /// |
| ... | @@ -7570,17 +7534,7 @@ pub const unexpected_error_tracing = builtin.mode == .Debug and switch (builtin. | ... | @@ -7570,17 +7534,7 @@ pub const unexpected_error_tracing = builtin.mode == .Debug and switch (builtin. |
| 7570 | else => false, | 7534 | else => false, |
| 7571 | }; | 7535 | }; |
| 7572 | 7536 | ||
| 7573 | pub const UnexpectedError = error{ | 7537 | pub const UnexpectedError = std.Io.UnexpectedError; |
| 7574 | /// The Operating System returned an undocumented error code. | ||
| 7575 | /// | ||
| 7576 | /// This error is in theory not possible, but it would be better | ||
| 7577 | /// to handle this error than to invoke undefined behavior. | ||
| 7578 | /// | ||
| 7579 | /// When this error code is observed, it usually means the Zig Standard | ||
| 7580 | /// Library needs a small patch to add the error code to the error set for | ||
| 7581 | /// the respective function. | ||
| 7582 | Unexpected, | ||
| 7583 | }; | ||
| 7584 | 7538 | ||
| 7585 | /// Call this when you made a syscall or something that sets errno | 7539 | /// Call this when you made a syscall or something that sets errno |
| 7586 | /// and you get an unexpected error. | 7540 | /// and you get an unexpected error. |