authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-08 00:19:12-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-08 00:19:12-08:00
log6f7968f1658ef92fb878f0284af7761aaaa3e71a
tree468179e8088f533e6d7deb24de94793de79e2ab3
parentcc38acfea30218ba8a1ad29d4787528ff3014e64
parentce890060350a848590a7371f11316c2040a89a2b

Merge branch "remove many std.posix functions"

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/30741

15 files changed, 315 insertions(+), 1521 deletions(-)

lib/compiler/aro/aro/Driver.zig+2-2
...@@ -1219,12 +1219,12 @@ pub fn getDepFileName(d: *Driver, source: Source, buf: *[std.fs.max_name_bytes]u...@@ -1219,12 +1219,12 @@ pub fn getDepFileName(d: *Driver, source: Source, buf: *[std.fs.max_name_bytes]u
1219fn getRandomFilename(d: *Driver, buf: *[std.fs.max_name_bytes]u8, extension: []const u8) ![]const u8 {1219fn getRandomFilename(d: *Driver, buf: *[std.fs.max_name_bytes]u8, extension: []const u8) ![]const u8 {
1220 const io = d.comp.io;1220 const io = d.comp.io;
1221 const random_bytes_count = 12;1221 const random_bytes_count = 12;
1222 const sub_path_len = comptime std.fs.base64_encoder.calcSize(random_bytes_count);1222 const sub_path_len = comptime std.base64.url_safe.Encoder.calcSize(random_bytes_count);
12231223
1224 var random_bytes: [random_bytes_count]u8 = undefined;1224 var random_bytes: [random_bytes_count]u8 = undefined;
1225 io.random(&random_bytes);1225 io.random(&random_bytes);
1226 var random_name: [sub_path_len]u8 = undefined;1226 var random_name: [sub_path_len]u8 = undefined;
1227 _ = std.fs.base64_encoder.encode(&random_name, &random_bytes);1227 _ = std.base64.url_safe.Encoder.encode(&random_name, &random_bytes);
12281228
1229 const fmt_template = "/tmp/{s}{s}";1229 const fmt_template = "/tmp/{s}{s}";
1230 const fmt_args = .{1230 const fmt_args = .{
lib/std/Build/Watch.zig+7-5
...@@ -1,5 +1,7 @@...@@ -1,5 +1,7 @@
1const builtin = @import("builtin");1const builtin = @import("builtin");
2
2const std = @import("../std.zig");3const std = @import("../std.zig");
4const Io = std.Io;
3const Step = std.Build.Step;5const Step = std.Build.Step;
4const Allocator = std.mem.Allocator;6const Allocator = std.mem.Allocator;
5const assert = std.debug.assert;7const assert = std.debug.assert;
...@@ -666,7 +668,7 @@ const Os = switch (builtin.os.tag) {...@@ -666,7 +668,7 @@ const Os = switch (builtin.os.tag) {
666 .dir_table = .{},668 .dir_table = .{},
667 .dir_count = 0,669 .dir_count = 0,
668 .os = .{670 .os = .{
669 .kq_fd = try posix.kqueue(),671 .kq_fd = try Io.Kqueue.createFileDescriptor(),
670 .handles = .empty,672 .handles = .empty,
671 },673 },
672 .generation = 0,674 .generation = 0,
...@@ -697,7 +699,7 @@ const Os = switch (builtin.os.tag) {...@@ -697,7 +699,7 @@ const Os = switch (builtin.os.tag) {
697 .data = 0,699 .data = 0,
698 .udata = gop.index,700 .udata = gop.index,
699 }};701 }};
700 _ = try posix.kevent(w.os.kq_fd, &changes, &.{}, null);702 _ = try Io.Kqueue.kevent(w.os.kq_fd, &changes, &.{}, null);
701 assert(handles.len == gop.index);703 assert(handles.len == gop.index);
702 try handles.append(gpa, .{704 try handles.append(gpa, .{
703 .rs = .{},705 .rs = .{},
...@@ -787,7 +789,7 @@ const Os = switch (builtin.os.tag) {...@@ -787,7 +789,7 @@ const Os = switch (builtin.os.tag) {
787 },789 },
788 };790 };
789 const filtered_changes = if (i == handles.len - 1) changes[0..1] else &changes;791 const filtered_changes = if (i == handles.len - 1) changes[0..1] else &changes;
790 _ = try posix.kevent(w.os.kq_fd, filtered_changes, &.{}, null);792 _ = try Io.Kqueue.kevent(w.os.kq_fd, filtered_changes, &.{}, null);
791 if (path.sub_path.len != 0) posix.close(dir_fd);793 if (path.sub_path.len != 0) posix.close(dir_fd);
792794
793 w.dir_table.swapRemoveAt(i);795 w.dir_table.swapRemoveAt(i);
...@@ -801,13 +803,13 @@ const Os = switch (builtin.os.tag) {...@@ -801,13 +803,13 @@ const Os = switch (builtin.os.tag) {
801 fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult {803 fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult {
802 var timespec_buffer: posix.timespec = undefined;804 var timespec_buffer: posix.timespec = undefined;
803 var event_buffer: [100]posix.Kevent = undefined;805 var event_buffer: [100]posix.Kevent = undefined;
804 var n = try posix.kevent(w.os.kq_fd, &.{}, &event_buffer, timeout.toTimespec(&timespec_buffer));806 var n = try Io.Kqueue.kevent(w.os.kq_fd, &.{}, &event_buffer, timeout.toTimespec(&timespec_buffer));
805 if (n == 0) return .timeout;807 if (n == 0) return .timeout;
806 const reaction_sets = w.os.handles.items(.rs);808 const reaction_sets = w.os.handles.items(.rs);
807 var any_dirty = markDirtySteps(gpa, reaction_sets, event_buffer[0..n], false);809 var any_dirty = markDirtySteps(gpa, reaction_sets, event_buffer[0..n], false);
808 timespec_buffer = .{ .sec = 0, .nsec = 0 };810 timespec_buffer = .{ .sec = 0, .nsec = 0 };
809 while (n == event_buffer.len) {811 while (n == event_buffer.len) {
810 n = try posix.kevent(w.os.kq_fd, &.{}, &event_buffer, &timespec_buffer);812 n = try Io.Kqueue.kevent(w.os.kq_fd, &.{}, &event_buffer, &timespec_buffer);
811 if (n == 0) break;813 if (n == 0) break;
812 any_dirty = markDirtySteps(gpa, reaction_sets, event_buffer[0..n], any_dirty);814 any_dirty = markDirtySteps(gpa, reaction_sets, event_buffer[0..n], any_dirty);
813 }815 }
lib/std/Io.zig+6-4
...@@ -540,18 +540,20 @@ test {...@@ -540,18 +540,20 @@ test {
540540
541const Io = @This();541const Io = @This();
542542
543pub const Threaded = @import("Io/Threaded.zig");
543pub const Evented = switch (builtin.os.tag) {544pub const Evented = switch (builtin.os.tag) {
544 .linux => switch (builtin.cpu.arch) {545 .linux => switch (builtin.cpu.arch) {
545 .x86_64, .aarch64 => @import("Io/IoUring.zig"),546 .x86_64, .aarch64 => IoUring,
546 else => void, // context-switching code not implemented yet547 else => void, // context-switching code not implemented yet
547 },548 },
548 .dragonfly, .freebsd, .netbsd, .openbsd, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => switch (builtin.cpu.arch) {549 .dragonfly, .freebsd, .netbsd, .openbsd, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => switch (builtin.cpu.arch) {
549 .x86_64, .aarch64 => @import("Io/Kqueue.zig"),550 .x86_64, .aarch64 => Kqueue,
550 else => void, // context-switching code not implemented yet551 else => void, // context-switching code not implemented yet
551 },552 },
552 else => void,553 else => void,
553};554};
554pub const Threaded = @import("Io/Threaded.zig");555pub const Kqueue = @import("Io/Kqueue.zig");
556pub const IoUring = @import("Io/IoUring.zig");
555pub const net = @import("Io/net.zig");557pub const net = @import("Io/net.zig");
556558
557userdata: ?*anyopaque,559userdata: ?*anyopaque,
...@@ -1356,7 +1358,7 @@ pub const Mutex = extern struct {...@@ -1356,7 +1358,7 @@ pub const Mutex = extern struct {
13561358
1357 pub const init: Mutex = .{ .state = .init(.unlocked) };1359 pub const init: Mutex = .{ .state = .init(.unlocked) };
13581360
1359 const State = enum(u32) {1361 pub const State = enum(u32) {
1360 unlocked,1362 unlocked,
1361 locked_once,1363 locked_once,
1362 contended,1364 contended,
lib/std/Io/Kqueue.zig+164-185
...@@ -157,8 +157,11 @@ pub const InitOptions = struct {...@@ -157,8 +157,11 @@ pub const InitOptions = struct {
157 n_threads: ?usize = null,157 n_threads: ?usize = null,
158};158};
159159
160pub const InitError = Allocator.Error || CreateFileDescriptorError;
161
160pub fn init(k: *Kqueue, gpa: Allocator, options: InitOptions) !void {162pub fn init(k: *Kqueue, gpa: Allocator, options: InitOptions) !void {
161 assert(options.n_threads != 0);163 assert(options.n_threads != 0);
164
162 const n_threads = @max(1, options.n_threads orelse std.Thread.getCpuCount() catch 1);165 const n_threads = @max(1, options.n_threads orelse std.Thread.getCpuCount() catch 1);
163 const threads_size = n_threads * @sizeOf(Thread);166 const threads_size = n_threads * @sizeOf(Thread);
164 const idle_stack_end_offset = std.mem.alignForward(usize, threads_size + idle_stack_size, std.heap.page_size_max);167 const idle_stack_end_offset = std.mem.alignForward(usize, threads_size + idle_stack_size, std.heap.page_size_max);
...@@ -204,7 +207,7 @@ pub fn init(k: *Kqueue, gpa: Allocator, options: InitOptions) !void {...@@ -204,7 +207,7 @@ pub fn init(k: *Kqueue, gpa: Allocator, options: InitOptions) !void {
204 },207 },
205 .current_context = &main_fiber.context,208 .current_context = &main_fiber.context,
206 .ready_queue = null,209 .ready_queue = null,
207 .kq_fd = try posix.kqueue(),210 .kq_fd = try createFileDescriptor(),
208 .idle_search_index = 1,211 .idle_search_index = 1,
209 .steal_ready_search_index = 1,212 .steal_ready_search_index = 1,
210 .wait_queues = .empty,213 .wait_queues = .empty,
...@@ -231,6 +234,23 @@ pub fn deinit(k: *Kqueue) void {...@@ -231,6 +234,23 @@ pub fn deinit(k: *Kqueue) void {
231 k.* = undefined;234 k.* = undefined;
232}235}
233236
237pub const CreateFileDescriptorError = error{
238 /// The per-process limit on the number of open file descriptors has been reached.
239 ProcessFdQuotaExceeded,
240 /// The system-wide limit on the total number of open files has been reached.
241 SystemFdQuotaExceeded,
242} || Io.UnexpectedError;
243
244pub fn createFileDescriptor() CreateFileDescriptorError!posix.fd_t {
245 const rc = posix.system.kqueue();
246 switch (posix.errno(rc)) {
247 .SUCCESS => return @intCast(rc),
248 .MFILE => return error.ProcessFdQuotaExceeded,
249 .NFILE => return error.SystemFdQuotaExceeded,
250 else => |err| return posix.unexpectedErrno(err),
251 }
252}
253
234fn findReadyFiber(k: *Kqueue, thread: *Thread) ?*Fiber {254fn findReadyFiber(k: *Kqueue, thread: *Thread) ?*Fiber {
235 if (@atomicRmw(?*Fiber, &thread.ready_queue, .Xchg, Fiber.finished, .acquire)) |ready_fiber| {255 if (@atomicRmw(?*Fiber, &thread.ready_queue, .Xchg, Fiber.finished, .acquire)) |ready_fiber| {
236 @atomicStore(?*Fiber, &thread.ready_queue, ready_fiber.queue_next, .release);256 @atomicStore(?*Fiber, &thread.ready_queue, ready_fiber.queue_next, .release);
...@@ -314,7 +334,10 @@ fn schedule(k: *Kqueue, thread: *Thread, ready_queue: Fiber.Queue) void {...@@ -314,7 +334,10 @@ fn schedule(k: *Kqueue, thread: *Thread, ready_queue: Fiber.Queue) void {
314 },334 },
315 };335 };
316 // If an error occurs it only pessimises scheduling.336 // If an error occurs it only pessimises scheduling.
317 _ = posix.kevent(idle_search_thread.kq_fd, &changes, &.{}, null) catch {};337 _ = kevent(idle_search_thread.kq_fd, &changes, &.{}, null) catch |err| {
338 // TODO handle EINTR for cancellation purposes
339 @panic(@errorName(err)); // TODO
340 };
318 return;341 return;
319 }342 }
320 spawn_thread: {343 spawn_thread: {
...@@ -334,7 +357,7 @@ fn schedule(k: *Kqueue, thread: *Thread, ready_queue: Fiber.Queue) void {...@@ -334,7 +357,7 @@ fn schedule(k: *Kqueue, thread: *Thread, ready_queue: Fiber.Queue) void {
334 .idle_context = undefined,357 .idle_context = undefined,
335 .current_context = &new_thread.idle_context,358 .current_context = &new_thread.idle_context,
336 .ready_queue = ready_queue.head,359 .ready_queue = ready_queue.head,
337 .kq_fd = posix.kqueue() catch |err| {360 .kq_fd = createFileDescriptor() catch |err| {
338 @atomicStore(u32, &k.threads.reserved, new_thread_index, .release);361 @atomicStore(u32, &k.threads.reserved, new_thread_index, .release);
339 // no more access to `thread` after giving up reservation362 // no more access to `thread` after giving up reservation
340 std.log.warn("unable to create worker thread due to kqueue init failure: {t}", .{err});363 std.log.warn("unable to create worker thread due to kqueue init failure: {t}", .{err});
...@@ -409,9 +432,9 @@ fn idle(k: *Kqueue, thread: *Thread) void {...@@ -409,9 +432,9 @@ fn idle(k: *Kqueue, thread: *Thread) void {
409 k.yield(ready_fiber, .nothing);432 k.yield(ready_fiber, .nothing);
410 maybe_ready_fiber = null;433 maybe_ready_fiber = null;
411 }434 }
412 const n = posix.kevent(thread.kq_fd, &.{}, &events_buffer, null) catch |err| {435 const n = kevent(thread.kq_fd, &.{}, &events_buffer, null) catch |err| {
413 // TODO handle EINTR for cancellation purposes436 // TODO handle EINTR for cancellation purposes
414 @panic(@errorName(err));437 @panic(@errorName(err)); // TODO
415 };438 };
416 var maybe_ready_queue: ?Fiber.Queue = null;439 var maybe_ready_queue: ?Fiber.Queue = null;
417 for (events_buffer[0..n]) |event| switch (@as(Completion.UserData, @enumFromInt(event.udata))) {440 for (events_buffer[0..n]) |event| switch (@as(Completion.UserData, @enumFromInt(event.udata))) {
...@@ -471,14 +494,6 @@ const SwitchMessage = struct {...@@ -471,14 +494,6 @@ const SwitchMessage = struct {
471 recycle: *Fiber,494 recycle: *Fiber,
472 register_awaiter: *?*Fiber,495 register_awaiter: *?*Fiber,
473 register_select: []const *Io.AnyFuture,496 register_select: []const *Io.AnyFuture,
474 mutex_lock: struct {
475 prev_state: Io.Mutex.State,
476 mutex: *Io.Mutex,
477 },
478 condition_wait: struct {
479 cond: *Io.Condition,
480 mutex: *Io.Mutex,
481 },
482 exit,497 exit,
483 };498 };
484499
...@@ -514,59 +529,6 @@ const SwitchMessage = struct {...@@ -514,59 +529,6 @@ const SwitchMessage = struct {
514 }529 }
515 }530 }
516 },531 },
517 .mutex_lock => |mutex_lock| {
518 const prev_fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.prev));
519 assert(prev_fiber.queue_next == null);
520 var prev_state = mutex_lock.prev_state;
521 while (switch (prev_state) {
522 else => next_state: {
523 prev_fiber.queue_next = @ptrFromInt(@intFromEnum(prev_state));
524 break :next_state @cmpxchgWeak(
525 Io.Mutex.State,
526 &mutex_lock.mutex.state,
527 prev_state,
528 @enumFromInt(@intFromPtr(prev_fiber)),
529 .release,
530 .acquire,
531 );
532 },
533 .unlocked => @cmpxchgWeak(
534 Io.Mutex.State,
535 &mutex_lock.mutex.state,
536 .unlocked,
537 .locked_once,
538 .acquire,
539 .acquire,
540 ) orelse {
541 prev_fiber.queue_next = null;
542 k.schedule(thread, .{ .head = prev_fiber, .tail = prev_fiber });
543 return;
544 },
545 }) |next_state| prev_state = next_state;
546 },
547 .condition_wait => |condition_wait| {
548 const prev_fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.prev));
549 assert(prev_fiber.queue_next == null);
550 const cond_impl = prev_fiber.resultPointer(Condition);
551 cond_impl.* = .{
552 .tail = prev_fiber,
553 .event = .queued,
554 };
555 if (@cmpxchgStrong(
556 ?*Fiber,
557 @as(*?*Fiber, @ptrCast(&condition_wait.cond.state)),
558 null,
559 prev_fiber,
560 .release,
561 .acquire,
562 )) |waiting_fiber| {
563 const waiting_cond_impl = waiting_fiber.?.resultPointer(Condition);
564 assert(waiting_cond_impl.tail.queue_next == null);
565 waiting_cond_impl.tail.queue_next = prev_fiber;
566 waiting_cond_impl.tail = prev_fiber;
567 }
568 condition_wait.mutex.unlock(k.io());
569 },
570 .exit => for (k.threads.allocated[0..@atomicLoad(u32, &k.threads.active, .acquire)]) |*each_thread| {532 .exit => for (k.threads.allocated[0..@atomicLoad(u32, &k.threads.active, .acquire)]) |*each_thread| {
571 const changes = [_]posix.Kevent{533 const changes = [_]posix.Kevent{
572 .{534 .{
...@@ -578,8 +540,9 @@ const SwitchMessage = struct {...@@ -578,8 +540,9 @@ const SwitchMessage = struct {
578 .udata = @intFromEnum(Completion.UserData.exit),540 .udata = @intFromEnum(Completion.UserData.exit),
579 },541 },
580 };542 };
581 _ = posix.kevent(each_thread.kq_fd, &changes, &.{}, null) catch |err| {543 _ = kevent(each_thread.kq_fd, &changes, &.{}, null) catch |err| {
582 @panic(@errorName(err));544 // TODO handle EINTR for cancellation purposes
545 @panic(@errorName(err)); // TODO
583 };546 };
584 },547 },
585 }548 }
...@@ -854,21 +817,13 @@ pub fn io(k: *Kqueue) Io {...@@ -854,21 +817,13 @@ pub fn io(k: *Kqueue) Io {
854 .concurrent = concurrent,817 .concurrent = concurrent,
855 .await = await,818 .await = await,
856 .cancel = cancel,819 .cancel = cancel,
857 .cancelRequested = cancelRequested,
858 .select = select,820 .select = select,
859821
860 .groupAsync = groupAsync,822 .groupAsync = groupAsync,
861 .groupWait = groupWait,823 .groupConcurrent = groupConcurrent,
824 .groupAwait = groupAwait,
862 .groupCancel = groupCancel,825 .groupCancel = groupCancel,
863826
864 .mutexLock = mutexLock,
865 .mutexLockUncancelable = mutexLockUncancelable,
866 .mutexUnlock = mutexUnlock,
867
868 .conditionWait = conditionWait,
869 .conditionWaitUncancelable = conditionWaitUncancelable,
870 .conditionWake = conditionWake,
871
872 .dirCreateDir = dirCreateDir,827 .dirCreateDir = dirCreateDir,
873 .dirCreateDirPath = dirCreateDirPath,828 .dirCreateDirPath = dirCreateDirPath,
874 .dirCreateDirPathOpen = dirCreateDirPathOpen,829 .dirCreateDirPathOpen = dirCreateDirPathOpen,
...@@ -888,7 +843,6 @@ pub fn io(k: *Kqueue) Io {...@@ -888,7 +843,6 @@ pub fn io(k: *Kqueue) Io {
888 .fileReadPositional = fileReadPositional,843 .fileReadPositional = fileReadPositional,
889 .fileSeekBy = fileSeekBy,844 .fileSeekBy = fileSeekBy,
890 .fileSeekTo = fileSeekTo,845 .fileSeekTo = fileSeekTo,
891 .openExecutable = openExecutable,
892846
893 .now = now,847 .now = now,
894 .sleep = sleep,848 .sleep = sleep,
...@@ -1013,139 +967,111 @@ fn cancelRequested(userdata: ?*anyopaque) bool {...@@ -1013,139 +967,111 @@ fn cancelRequested(userdata: ?*anyopaque) bool {
1013967
1014fn groupAsync(968fn groupAsync(
1015 userdata: ?*anyopaque,969 userdata: ?*anyopaque,
1016 group: *Io.Group,970 type_erased: *Io.Group,
1017 context: []const u8,971 context: []const u8,
1018 context_alignment: std.mem.Alignment,972 context_alignment: Alignment,
1019 start: *const fn (*Io.Group, context: *const anyopaque) void,973 start: *const fn (context: *const anyopaque) Io.Cancelable!void,
1020) void {974) void {
1021 const k: *Kqueue = @ptrCast(@alignCast(userdata));975 const k: *Kqueue = @ptrCast(@alignCast(userdata));
1022 _ = k;976 _ = k;
1023 _ = group;977 _ = type_erased;
1024 _ = context;978 _ = context;
1025 _ = context_alignment;979 _ = context_alignment;
1026 _ = start;980 _ = start;
1027 @panic("TODO");981 @panic("TODO");
1028}982}
1029983
1030fn groupWait(userdata: ?*anyopaque, group: *Io.Group, token: *anyopaque) void {984fn groupConcurrent(
1031 const k: *Kqueue = @ptrCast(@alignCast(userdata));985 userdata: ?*anyopaque,
1032 _ = k;986 type_erased: *Io.Group,
1033 _ = group;987 context: []const u8,
1034 _ = token;988 context_alignment: Alignment,
1035 @panic("TODO");989 start: *const fn (context: *const anyopaque) Io.Cancelable!void,
1036}990) Io.ConcurrentError!void {
1037
1038fn groupCancel(userdata: ?*anyopaque, group: *Io.Group, token: *anyopaque) void {
1039 const k: *Kqueue = @ptrCast(@alignCast(userdata));991 const k: *Kqueue = @ptrCast(@alignCast(userdata));
1040 _ = k;992 _ = k;
1041 _ = group;993 _ = type_erased;
1042 _ = token;994 _ = context;
995 _ = context_alignment;
996 _ = start;
1043 @panic("TODO");997 @panic("TODO");
1044}998}
1045999
1046fn select(userdata: ?*anyopaque, futures: []const *Io.AnyFuture) Io.Cancelable!usize {1000fn groupAwait(userdata: ?*anyopaque, type_erased: *Io.Group, initial_token: *anyopaque) Io.Cancelable!void {
1047 const k: *Kqueue = @ptrCast(@alignCast(userdata));1001 const k: *Kqueue = @ptrCast(@alignCast(userdata));
1048 _ = k;1002 _ = k;
1049 _ = futures;1003 _ = type_erased;
1004 _ = initial_token;
1050 @panic("TODO");1005 @panic("TODO");
1051}1006}
10521007
1053fn mutexLock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mutex) Io.Cancelable!void {1008fn groupCancel(userdata: ?*anyopaque, group: *Io.Group, token: *anyopaque) void {
1054 const k: *Kqueue = @ptrCast(@alignCast(userdata));
1055 _ = k;
1056 _ = prev_state;
1057 _ = mutex;
1058 @panic("TODO");
1059}
1060fn mutexLockUncancelable(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mutex) void {
1061 const k: *Kqueue = @ptrCast(@alignCast(userdata));
1062 _ = k;
1063 _ = prev_state;
1064 _ = mutex;
1065 @panic("TODO");
1066}
1067fn mutexUnlock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mutex) void {
1068 const k: *Kqueue = @ptrCast(@alignCast(userdata));1009 const k: *Kqueue = @ptrCast(@alignCast(userdata));
1069 _ = k;1010 _ = k;
1070 _ = prev_state;1011 _ = group;
1071 _ = mutex;1012 _ = token;
1072 @panic("TODO");1013 @panic("TODO");
1073}1014}
10741015
1075fn conditionWait(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) Io.Cancelable!void {1016fn select(userdata: ?*anyopaque, futures: []const *Io.AnyFuture) Io.Cancelable!usize {
1076 const k: *Kqueue = @ptrCast(@alignCast(userdata));
1077 k.yield(null, .{ .condition_wait = .{ .cond = cond, .mutex = mutex } });
1078 const thread = Thread.current();
1079 const fiber = thread.currentFiber();
1080 const cond_impl = fiber.resultPointer(Condition);
1081 try mutex.lock(k.io());
1082 switch (cond_impl.event) {
1083 .queued => {},
1084 .wake => |wake| if (fiber.queue_next) |next_fiber| switch (wake) {
1085 .one => if (@cmpxchgStrong(
1086 ?*Fiber,
1087 @as(*?*Fiber, @ptrCast(&cond.state)),
1088 null,
1089 next_fiber,
1090 .release,
1091 .acquire,
1092 )) |old_fiber| {
1093 const old_cond_impl = old_fiber.?.resultPointer(Condition);
1094 assert(old_cond_impl.tail.queue_next == null);
1095 old_cond_impl.tail.queue_next = next_fiber;
1096 old_cond_impl.tail = cond_impl.tail;
1097 },
1098 .all => k.schedule(thread, .{ .head = next_fiber, .tail = cond_impl.tail }),
1099 },
1100 }
1101 fiber.queue_next = null;
1102}
1103
1104fn conditionWaitUncancelable(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) void {
1105 const k: *Kqueue = @ptrCast(@alignCast(userdata));1017 const k: *Kqueue = @ptrCast(@alignCast(userdata));
1106 _ = k;1018 _ = k;
1107 _ = cond;1019 _ = futures;
1108 _ = mutex;
1109 @panic("TODO");1020 @panic("TODO");
1110}1021}
1111fn conditionWake(userdata: ?*anyopaque, cond: *Io.Condition, wake: Io.Condition.Wake) void {
1112 const k: *Kqueue = @ptrCast(@alignCast(userdata));
1113 const waiting_fiber = @atomicRmw(?*Fiber, @as(*?*Fiber, @ptrCast(&cond.state)), .Xchg, null, .acquire) orelse return;
1114 waiting_fiber.resultPointer(Condition).event = .{ .wake = wake };
1115 k.yield(waiting_fiber, .reschedule);
1116}
11171022
1118fn dirCreateDir(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, mode: Dir.Mode) Dir.CreateDirError!void {1023fn dirCreateDir(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions) Dir.CreateDirError!void {
1119 const k: *Kqueue = @ptrCast(@alignCast(userdata));1024 const k: *Kqueue = @ptrCast(@alignCast(userdata));
1120 _ = k;1025 _ = k;
1121 _ = dir;1026 _ = dir;
1122 _ = sub_path;1027 _ = sub_path;
1123 _ = mode;1028 _ = permissions;
1124 @panic("TODO");1029 @panic("TODO");
1125}1030}
1126fn dirCreateDirPath(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, mode: Dir.Mode) Dir.CreateDirError!void {1031
1032fn dirCreateDirPath(
1033 userdata: ?*anyopaque,
1034 dir: Dir,
1035 sub_path: []const u8,
1036 permissions: Dir.Permissions,
1037) Dir.CreateDirPathError!Dir.CreatePathStatus {
1127 const k: *Kqueue = @ptrCast(@alignCast(userdata));1038 const k: *Kqueue = @ptrCast(@alignCast(userdata));
1128 _ = k;1039 _ = k;
1129 _ = dir;1040 _ = dir;
1130 _ = sub_path;1041 _ = sub_path;
1131 _ = mode;1042 _ = permissions;
1132 @panic("TODO");1043 @panic("TODO");
1133}1044}
1134fn dirCreateDirPathOpen(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, options: Dir.OpenOptions) Dir.CreateDirPathOpenError!Dir {1045
1046fn dirCreateDirPathOpen(
1047 userdata: ?*anyopaque,
1048 dir: Dir,
1049 sub_path: []const u8,
1050 permissions: Dir.Permissions,
1051 options: Dir.OpenOptions,
1052) Dir.CreateDirPathOpenError!Dir {
1135 const k: *Kqueue = @ptrCast(@alignCast(userdata));1053 const k: *Kqueue = @ptrCast(@alignCast(userdata));
1136 _ = k;1054 _ = k;
1137 _ = dir;1055 _ = dir;
1138 _ = sub_path;1056 _ = sub_path;
1057 _ = permissions;
1139 _ = options;1058 _ = options;
1140 @panic("TODO");1059 @panic("TODO");
1141}1060}
1061
1142fn dirStat(userdata: ?*anyopaque, dir: Dir) Dir.StatError!Dir.Stat {1062fn dirStat(userdata: ?*anyopaque, dir: Dir) Dir.StatError!Dir.Stat {
1143 const k: *Kqueue = @ptrCast(@alignCast(userdata));1063 const k: *Kqueue = @ptrCast(@alignCast(userdata));
1144 _ = k;1064 _ = k;
1145 _ = dir;1065 _ = dir;
1146 @panic("TODO");1066 @panic("TODO");
1147}1067}
1148fn dirStatFile(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, options: Dir.StatPathOptions) Dir.StatFileError!File.Stat {1068
1069fn dirStatFile(
1070 userdata: ?*anyopaque,
1071 dir: Dir,
1072 sub_path: []const u8,
1073 options: Dir.StatFileOptions,
1074) Dir.StatFileError!File.Stat {
1149 const k: *Kqueue = @ptrCast(@alignCast(userdata));1075 const k: *Kqueue = @ptrCast(@alignCast(userdata));
1150 _ = k;1076 _ = k;
1151 _ = dir;1077 _ = dir;
...@@ -1185,10 +1111,10 @@ fn dirOpenDir(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, options: Di...@@ -1185,10 +1111,10 @@ fn dirOpenDir(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, options: Di
1185 _ = options;1111 _ = options;
1186 @panic("TODO");1112 @panic("TODO");
1187}1113}
1188fn dirClose(userdata: ?*anyopaque, dir: Dir) void {1114fn dirClose(userdata: ?*anyopaque, dirs: []const Dir) void {
1189 const k: *Kqueue = @ptrCast(@alignCast(userdata));1115 const k: *Kqueue = @ptrCast(@alignCast(userdata));
1190 _ = k;1116 _ = k;
1191 _ = dir;1117 _ = dirs;
1192 @panic("TODO");1118 @panic("TODO");
1193}1119}
1194fn fileStat(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {1120fn fileStat(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
...@@ -1197,35 +1123,57 @@ fn fileStat(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {...@@ -1197,35 +1123,57 @@ fn fileStat(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
1197 _ = file;1123 _ = file;
1198 @panic("TODO");1124 @panic("TODO");
1199}1125}
1200fn fileClose(userdata: ?*anyopaque, file: File) void {1126
1127fn fileClose(userdata: ?*anyopaque, files: []const File) void {
1201 const k: *Kqueue = @ptrCast(@alignCast(userdata));1128 const k: *Kqueue = @ptrCast(@alignCast(userdata));
1202 _ = k;1129 _ = k;
1203 _ = file;1130 _ = files;
1204 @panic("TODO");1131 @panic("TODO");
1205}1132}
1206fn fileWriteStreaming(userdata: ?*anyopaque, file: File, buffer: [][]const u8) File.WriteStreamingError!usize {1133
1134fn fileWriteStreaming(
1135 userdata: ?*anyopaque,
1136 file: File,
1137 header: []const u8,
1138 data: []const []const u8,
1139 splat: usize,
1140) File.Writer.Error!usize {
1207 const k: *Kqueue = @ptrCast(@alignCast(userdata));1141 const k: *Kqueue = @ptrCast(@alignCast(userdata));
1208 _ = k;1142 _ = k;
1209 _ = file;1143 _ = file;
1210 _ = buffer;1144 _ = header;
1145 _ = data;
1146 _ = splat;
1211 @panic("TODO");1147 @panic("TODO");
1212}1148}
1213fn fileWritePositional(userdata: ?*anyopaque, file: File, buffer: [][]const u8, offset: u64) File.WritePositionalError!usize {1149
1150fn fileWritePositional(
1151 userdata: ?*anyopaque,
1152 file: File,
1153 header: []const u8,
1154 data: []const []const u8,
1155 splat: usize,
1156 offset: u64,
1157) File.WritePositionalError!usize {
1214 const k: *Kqueue = @ptrCast(@alignCast(userdata));1158 const k: *Kqueue = @ptrCast(@alignCast(userdata));
1215 _ = k;1159 _ = k;
1216 _ = file;1160 _ = file;
1217 _ = buffer;1161 _ = header;
1162 _ = data;
1163 _ = splat;
1218 _ = offset;1164 _ = offset;
1219 @panic("TODO");1165 @panic("TODO");
1220}1166}
1221fn fileReadStreaming(userdata: ?*anyopaque, file: File, data: [][]u8) File.Reader.Error!usize {1167
1168fn fileReadStreaming(userdata: ?*anyopaque, file: File, data: []const []u8) File.Reader.Error!usize {
1222 const k: *Kqueue = @ptrCast(@alignCast(userdata));1169 const k: *Kqueue = @ptrCast(@alignCast(userdata));
1223 _ = k;1170 _ = k;
1224 _ = file;1171 _ = file;
1225 _ = data;1172 _ = data;
1226 @panic("TODO");1173 @panic("TODO");
1227}1174}
1228fn fileReadPositional(userdata: ?*anyopaque, file: File, data: [][]u8, offset: u64) File.ReadPositionalError!usize {1175
1176fn fileReadPositional(userdata: ?*anyopaque, file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize {
1229 const k: *Kqueue = @ptrCast(@alignCast(userdata));1177 const k: *Kqueue = @ptrCast(@alignCast(userdata));
1230 _ = k;1178 _ = k;
1231 _ = file;1179 _ = file;
...@@ -1247,12 +1195,6 @@ fn fileSeekTo(userdata: ?*anyopaque, file: File, absolute_offset: u64) File.Seek...@@ -1247,12 +1195,6 @@ fn fileSeekTo(userdata: ?*anyopaque, file: File, absolute_offset: u64) File.Seek
1247 _ = absolute_offset;1195 _ = absolute_offset;
1248 @panic("TODO");1196 @panic("TODO");
1249}1197}
1250fn openExecutable(userdata: ?*anyopaque, file: File.OpenFlags) File.OpenExecutableError!File {
1251 const k: *Kqueue = @ptrCast(@alignCast(userdata));
1252 _ = k;
1253 _ = file;
1254 @panic("TODO");
1255}
12561198
1257fn now(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp {1199fn now(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp {
1258 const k: *Kqueue = @ptrCast(@alignCast(userdata));1200 const k: *Kqueue = @ptrCast(@alignCast(userdata));
...@@ -1518,7 +1460,8 @@ fn netRead(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net.Strea...@@ -1518,7 +1460,8 @@ fn netRead(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net.Strea
1518 .udata = @intFromPtr(fiber),1460 .udata = @intFromPtr(fiber),
1519 },1461 },
1520 };1462 };
1521 assert(0 == (posix.kevent(thread.kq_fd, &changes, &.{}, null) catch |err| {1463 assert(0 == (kevent(thread.kq_fd, &changes, &.{}, null) catch |err| {
1464 // TODO handle EINTR for cancellation purposes
1522 @panic(@errorName(err)); // TODO1465 @panic(@errorName(err)); // TODO
1523 }));1466 }));
1524 }1467 }
...@@ -1551,10 +1494,10 @@ fn netWrite(userdata: ?*anyopaque, dest: net.Socket.Handle, header: []const u8,...@@ -1551,10 +1494,10 @@ fn netWrite(userdata: ?*anyopaque, dest: net.Socket.Handle, header: []const u8,
1551 @panic("TODO");1494 @panic("TODO");
1552}1495}
15531496
1554fn netClose(userdata: ?*anyopaque, handle: net.Socket.Handle) void {1497fn netClose(userdata: ?*anyopaque, handles: []const net.Socket.Handle) void {
1555 const k: *Kqueue = @ptrCast(@alignCast(userdata));1498 const k: *Kqueue = @ptrCast(@alignCast(userdata));
1556 _ = k;1499 _ = k;
1557 _ = handle;1500 _ = handles;
1558 @panic("TODO");1501 @panic("TODO");
1559}1502}
15601503
...@@ -1586,13 +1529,13 @@ fn netInterfaceName(userdata: ?*anyopaque, interface: net.Interface) net.Interfa...@@ -1586,13 +1529,13 @@ fn netInterfaceName(userdata: ?*anyopaque, interface: net.Interface) net.Interfa
1586fn netLookup(1529fn netLookup(
1587 userdata: ?*anyopaque,1530 userdata: ?*anyopaque,
1588 host_name: net.HostName,1531 host_name: net.HostName,
1589 result: *Io.Queue(net.HostName.LookupResult),1532 resolved: *Io.Queue(net.HostName.LookupResult),
1590 options: net.HostName.LookupOptions,1533 options: net.HostName.LookupOptions,
1591) void {1534) net.HostName.LookupError!void {
1592 const k: *Kqueue = @ptrCast(@alignCast(userdata));1535 const k: *Kqueue = @ptrCast(@alignCast(userdata));
1593 _ = k;1536 _ = k;
1594 _ = host_name;1537 _ = host_name;
1595 _ = result;1538 _ = resolved;
1596 _ = options;1539 _ = options;
1597 @panic("TODO");1540 @panic("TODO");
1598}1541}
...@@ -1747,10 +1690,46 @@ fn checkCancel(k: *Kqueue) error{Canceled}!void {...@@ -1747,10 +1690,46 @@ fn checkCancel(k: *Kqueue) error{Canceled}!void {
1747 if (cancelRequested(k)) return error.Canceled;1690 if (cancelRequested(k)) return error.Canceled;
1748}1691}
17491692
1750const Condition = struct {1693pub const KEventError = error{
1751 tail: *Fiber,1694 /// The process does not have permission to register a filter.
1752 event: union(enum) {1695 AccessDenied,
1753 queued,1696 /// The event could not be found to be modified or deleted.
1754 wake: Io.Condition.Wake,1697 EventNotFound,
1755 },1698 /// No memory was available to register the event.
1699 SystemResources,
1700 /// The specified process to attach to does not exist.
1701 ProcessNotFound,
1702 /// changelist or eventlist had too many items on it.
1703 /// TODO remove this possibility
1704 Overflow,
1756};1705};
1706
1707pub fn kevent(
1708 kq: i32,
1709 changelist: []const posix.Kevent,
1710 eventlist: []posix.Kevent,
1711 timeout: ?*const posix.timespec,
1712) KEventError!usize {
1713 while (true) {
1714 const rc = posix.system.kevent(
1715 kq,
1716 changelist.ptr,
1717 std.math.cast(c_int, changelist.len) orelse return error.Overflow,
1718 eventlist.ptr,
1719 std.math.cast(c_int, eventlist.len) orelse return error.Overflow,
1720 timeout,
1721 );
1722 switch (posix.errno(rc)) {
1723 .SUCCESS => return @intCast(rc),
1724 .ACCES => return error.AccessDenied,
1725 .FAULT => unreachable, // TODO use error.Unexpected for these
1726 .BADF => unreachable, // Always a race condition.
1727 .INTR => continue, // TODO handle cancelation
1728 .INVAL => unreachable,
1729 .NOENT => return error.EventNotFound,
1730 .NOMEM => return error.SystemResources,
1731 .SRCH => return error.ProcessNotFound,
1732 else => unreachable,
1733 }
1734 }
1735}
lib/std/Io/net/test.zig+2-2
...@@ -310,11 +310,11 @@ test "listen on a unix socket, send bytes, receive bytes" {...@@ -310,11 +310,11 @@ test "listen on a unix socket, send bytes, receive bytes" {
310310
311fn generateFileName(io: Io, base_name: []const u8) ![]const u8 {311fn generateFileName(io: Io, base_name: []const u8) ![]const u8 {
312 const random_bytes_count = 12;312 const random_bytes_count = 12;
313 const sub_path_len = comptime std.fs.base64_encoder.calcSize(random_bytes_count);313 const sub_path_len = comptime std.base64.url_safe.Encoder.calcSize(random_bytes_count);
314 var random_bytes: [12]u8 = undefined;314 var random_bytes: [12]u8 = undefined;
315 io.random(&random_bytes);315 io.random(&random_bytes);
316 var sub_path: [sub_path_len]u8 = undefined;316 var sub_path: [sub_path_len]u8 = undefined;
317 _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);317 _ = std.base64.url_safe.Encoder.encode(&sub_path, &random_bytes);
318 return std.fmt.allocPrint(testing.allocator, "{s}-{s}", .{ sub_path[0..], base_name });318 return std.fmt.allocPrint(testing.allocator, "{s}-{s}", .{ sub_path[0..], base_name });
319}319}
320320
lib/std/Thread.zig+1-1
...@@ -175,7 +175,7 @@ pub const SetNameError = error{...@@ -175,7 +175,7 @@ pub const SetNameError = error{
175 Unsupported,175 Unsupported,
176 Unexpected,176 Unexpected,
177 InvalidWtf8,177 InvalidWtf8,
178} || posix.PrctlError || posix.WriteError || Io.File.OpenError || std.fmt.BufPrintError;178} || posix.PrctlError || Io.File.Writer.Error || Io.File.OpenError || std.fmt.BufPrintError;
179179
180pub fn setName(self: Thread, io: Io, name: []const u8) SetNameError!void {180pub fn setName(self: Thread, io: Io, name: []const u8) SetNameError!void {
181 if (name.len > max_name_len) return error.NameTooLong;181 if (name.len > max_name_len) return error.NameTooLong;
lib/std/dynamic_library.zig+15-23
...@@ -148,7 +148,7 @@ const ElfDynLibError = error{...@@ -148,7 +148,7 @@ const ElfDynLibError = error{
148 ElfHashTableNotFound,148 ElfHashTableNotFound,
149 Canceled,149 Canceled,
150 Streaming,150 Streaming,
151} || posix.OpenError || posix.MMapError;151} || Io.File.OpenError || posix.MMapError;
152152
153pub const ElfDynLib = struct {153pub const ElfDynLib = struct {
154 strings: [*:0]u8,154 strings: [*:0]u8,
...@@ -177,27 +177,20 @@ pub const ElfDynLib = struct {...@@ -177,27 +177,20 @@ pub const ElfDynLib = struct {
177 return parent;177 return parent;
178 }178 }
179179
180 fn resolveFromSearchPath(io: Io, search_path: []const u8, file_name: []const u8, delim: u8) ?posix.fd_t {180 fn resolveFromSearchPath(io: Io, search_path: []const u8, file_name: []const u8, delim: u8) ?Io.File {
181 var paths = std.mem.tokenizeScalar(u8, search_path, delim);181 var paths = std.mem.tokenizeScalar(u8, search_path, delim);
182 while (paths.next()) |p| {182 while (paths.next()) |p| {
183 var dir = openPath(io, p) catch continue;183 var dir = openPath(io, p) catch continue;
184 defer dir.close(io);184 defer dir.close(io);
185 const fd = posix.openat(dir.handle, file_name, .{185 return dir.openFile(io, file_name, .{}) catch continue;
186 .ACCMODE = .RDONLY,
187 .CLOEXEC = true,
188 }, 0) catch continue;
189 return fd;
190 }186 }
191 return null;187 return null;
192 }188 }
193189
194 fn resolveFromParent(io: Io, dir_path: []const u8, file_name: []const u8) ?posix.fd_t {190 fn resolveFromParent(io: Io, dir_path: []const u8, file_name: []const u8) ?Io.File {
195 var dir = Io.Dir.cwd().openDir(io, dir_path, .{}) catch return null;191 var dir = Io.Dir.cwd().openDir(io, dir_path, .{}) catch return null;
196 defer dir.close(io);192 defer dir.close(io);
197 return posix.openat(dir.handle, file_name, .{193 return dir.openFile(io, file_name, .{}) catch null;
198 .ACCMODE = .RDONLY,
199 .CLOEXEC = true,
200 }, 0) catch null;
201 }194 }
202195
203 // This implements enough to be able to load system libraries in general196 // This implements enough to be able to load system libraries in general
...@@ -205,10 +198,10 @@ pub const ElfDynLib = struct {...@@ -205,10 +198,10 @@ pub const ElfDynLib = struct {
205 // - DT_RPATH of the calling binary is not used as a search path198 // - DT_RPATH of the calling binary is not used as a search path
206 // - DT_RUNPATH of the calling binary is not used as a search path199 // - DT_RUNPATH of the calling binary is not used as a search path
207 // - /etc/ld.so.cache is not read200 // - /etc/ld.so.cache is not read
208 fn resolveFromName(io: Io, path_or_name: []const u8, LD_LIBRARY_PATH: ?[]const u8) !posix.fd_t {201 fn resolveFromName(io: Io, path_or_name: []const u8, LD_LIBRARY_PATH: ?[]const u8) !Io.File {
209 // If filename contains a slash ("/"), then it is interpreted as a (relative or absolute) pathname202 // If filename contains a slash ("/"), then it is interpreted as a (relative or absolute) pathname
210 if (std.mem.findScalarPos(u8, path_or_name, 0, '/')) |_| {203 if (std.mem.findScalarPos(u8, path_or_name, 0, '/')) |_| {
211 return posix.open(path_or_name, .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);204 return Io.Dir.cwd().openFile(io, path_or_name, .{});
212 }205 }
213206
214 // Only read LD_LIBRARY_PATH if the binary is not setuid/setgid207 // Only read LD_LIBRARY_PATH if the binary is not setuid/setgid
...@@ -216,15 +209,15 @@ pub const ElfDynLib = struct {...@@ -216,15 +209,15 @@ pub const ElfDynLib = struct {
216 std.os.linux.getegid() == std.os.linux.getgid())209 std.os.linux.getegid() == std.os.linux.getgid())
217 {210 {
218 if (LD_LIBRARY_PATH) |ld_library_path| {211 if (LD_LIBRARY_PATH) |ld_library_path| {
219 if (resolveFromSearchPath(io, ld_library_path, path_or_name, ':')) |fd| {212 if (resolveFromSearchPath(io, ld_library_path, path_or_name, ':')) |file| {
220 return fd;213 return file;
221 }214 }
222 }215 }
223 }216 }
224217
225 // Lastly the directories /lib and /usr/lib are searched (in this exact order)218 // Lastly the directories /lib and /usr/lib are searched (in this exact order)
226 if (resolveFromParent(io, "/lib", path_or_name)) |fd| return fd;219 if (resolveFromParent(io, "/lib", path_or_name)) |file| return file;
227 if (resolveFromParent(io, "/usr/lib", path_or_name)) |fd| return fd;220 if (resolveFromParent(io, "/usr/lib", path_or_name)) |file| return file;
228 return error.FileNotFound;221 return error.FileNotFound;
229 }222 }
230223
...@@ -232,10 +225,9 @@ pub const ElfDynLib = struct {...@@ -232,10 +225,9 @@ pub const ElfDynLib = struct {
232 pub fn open(path: []const u8, LD_LIBRARY_PATH: ?[]const u8) Error!ElfDynLib {225 pub fn open(path: []const u8, LD_LIBRARY_PATH: ?[]const u8) Error!ElfDynLib {
233 const io = std.Options.debug_io;226 const io = std.Options.debug_io;
234227
235 const fd = try resolveFromName(io, path, LD_LIBRARY_PATH);228 const file = try resolveFromName(io, path, LD_LIBRARY_PATH);
236 defer posix.close(fd);229 defer file.close(io);
237230
238 const file: Io.File = .{ .handle = fd };
239 const stat = try file.stat(io);231 const stat = try file.stat(io);
240 const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig;232 const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig;
241233
...@@ -248,7 +240,7 @@ pub const ElfDynLib = struct {...@@ -248,7 +240,7 @@ pub const ElfDynLib = struct {
248 mem.alignForward(usize, size, page_size),240 mem.alignForward(usize, size, page_size),
249 posix.PROT.READ,241 posix.PROT.READ,
250 .{ .TYPE = .PRIVATE },242 .{ .TYPE = .PRIVATE },
251 fd,243 file.handle,
252 0,244 0,
253 );245 );
254 defer posix.munmap(file_bytes);246 defer posix.munmap(file_bytes);
...@@ -318,7 +310,7 @@ pub const ElfDynLib = struct {...@@ -318,7 +310,7 @@ pub const ElfDynLib = struct {
318 extended_memsz,310 extended_memsz,
319 prot,311 prot,
320 .{ .TYPE = .PRIVATE, .FIXED = true },312 .{ .TYPE = .PRIVATE, .FIXED = true },
321 fd,313 file.handle,
322 ph.p_offset - extra_bytes,314 ph.p_offset - extra_bytes,
323 );315 );
324 } else {316 } else {
lib/std/fs.zig+6-9
...@@ -4,15 +4,12 @@ const std = @import("std.zig");...@@ -4,15 +4,12 @@ const std = @import("std.zig");
44
5/// Deprecated, use `std.Io.Dir.path`.5/// Deprecated, use `std.Io.Dir.path`.
6pub const path = @import("fs/path.zig");6pub const path = @import("fs/path.zig");
77/// Deprecated, use `std.base64.url_safe_alphabet_chars`.
8pub const base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".*;8pub const base64_alphabet = std.base64.url_safe_alphabet_chars;
99/// Deprecated, use `std.base64.url_safe.Encoder`.
10/// Base64 encoder, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem.10pub const base64_encoder = std.base64.url_safe.Encoder;
11pub const base64_encoder = std.base64.Base64Encoder.init(base64_alphabet, null);11/// Deprecated, use `std.base64.url_safe.Decoder`.
1212pub const base64_decoder = std.base64.url_safe.Decoder;
13/// Base64 decoder, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem.
14pub const base64_decoder = std.base64.Base64Decoder.init(base64_alphabet, null);
15
16/// Deprecated, use `std.Io.Dir.max_path_bytes`.13/// Deprecated, use `std.Io.Dir.max_path_bytes`.
17pub const max_path_bytes = std.Io.Dir.max_path_bytes;14pub const max_path_bytes = std.Io.Dir.max_path_bytes;
18/// Deprecated, use `std.Io.Dir.max_name_bytes`.15/// Deprecated, use `std.Io.Dir.max_name_bytes`.
lib/std/fs/test.zig+2-2
...@@ -1777,8 +1777,8 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {...@@ -1777,8 +1777,8 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {
1777 var random_bytes: [12]u8 = undefined;1777 var random_bytes: [12]u8 = undefined;
1778 io.random(&random_bytes);1778 io.random(&random_bytes);
17791779
1780 var random_b64: [std.fs.base64_encoder.calcSize(random_bytes.len)]u8 = undefined;1780 var random_b64: [std.base64.url_safe.Encoder.calcSize(random_bytes.len)]u8 = undefined;
1781 _ = std.fs.base64_encoder.encode(&random_b64, &random_bytes);1781 _ = std.base64.url_safe.Encoder.encode(&random_b64, &random_bytes);
17821782
1783 const sub_path = random_b64 ++ "-zig-test-absolute-paths.txt";1783 const sub_path = random_b64 ++ "-zig-test-absolute-paths.txt";
17841784
lib/std/os/linux/IoUring/test.zig+82-39
...@@ -529,17 +529,17 @@ test "sendmsg/recvmsg" {...@@ -529,17 +529,17 @@ test "sendmsg/recvmsg" {
529 .addr = @bitCast([4]u8{ 127, 0, 0, 1 }),529 .addr = @bitCast([4]u8{ 127, 0, 0, 1 }),
530 };530 };
531531
532 const server = try posix.socket(address_server.family, posix.SOCK.DGRAM, 0);532 const server = try socket(address_server.family, posix.SOCK.DGRAM, 0);
533 defer posix.close(server);533 defer posix.close(server);
534 try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEPORT, &mem.toBytes(@as(c_int, 1)));534 try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEPORT, &mem.toBytes(@as(c_int, 1)));
535 try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));535 try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));
536 try posix.bind(server, addrAny(&address_server), @sizeOf(linux.sockaddr.in));536 try bind(server, addrAny(&address_server), @sizeOf(linux.sockaddr.in));
537537
538 // set address_server to the OS-chosen IP/port.538 // set address_server to the OS-chosen IP/port.
539 var slen: posix.socklen_t = @sizeOf(linux.sockaddr.in);539 var slen: posix.socklen_t = @sizeOf(linux.sockaddr.in);
540 try posix.getsockname(server, addrAny(&address_server), &slen);540 try getsockname(server, addrAny(&address_server), &slen);
541541
542 const client = try posix.socket(address_server.family, posix.SOCK.DGRAM, 0);542 const client = try socket(address_server.family, posix.SOCK.DGRAM, 0);
543 defer posix.close(client);543 defer posix.close(client);
544544
545 const buffer_send = [_]u8{42} ** 128;545 const buffer_send = [_]u8{42} ** 128;
...@@ -932,6 +932,8 @@ test "accept/connect/recv/cancel" {...@@ -932,6 +932,8 @@ test "accept/connect/recv/cancel" {
932}932}
933933
934test "register_files_update" {934test "register_files_update" {
935 const io = testing.io;
936
935 var ring = IoUring.init(1, 0) catch |err| switch (err) {937 var ring = IoUring.init(1, 0) catch |err| switch (err) {
936 error.SystemOutdated => return error.SkipZigTest,938 error.SystemOutdated => return error.SkipZigTest,
937 error.PermissionDenied => return error.SkipZigTest,939 error.PermissionDenied => return error.SkipZigTest,
...@@ -939,13 +941,13 @@ test "register_files_update" {...@@ -939,13 +941,13 @@ test "register_files_update" {
939 };941 };
940 defer ring.deinit();942 defer ring.deinit();
941943
942 const fd = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);944 const file = try Io.Dir.openFileAbsolute(io, "/dev/zero", .{});
943 defer posix.close(fd);945 defer file.close(io);
944946
945 var registered_fds = [_]linux.fd_t{0} ** 2;947 var registered_fds = [_]linux.fd_t{0} ** 2;
946 const fd_index = 0;948 const fd_index = 0;
947 const fd_index2 = 1;949 const fd_index2 = 1;
948 registered_fds[fd_index] = fd;950 registered_fds[fd_index] = file.handle;
949 registered_fds[fd_index2] = -1;951 registered_fds[fd_index2] = -1;
950952
951 ring.register_files(registered_fds[0..]) catch |err| switch (err) {953 ring.register_files(registered_fds[0..]) catch |err| switch (err) {
...@@ -957,10 +959,10 @@ test "register_files_update" {...@@ -957,10 +959,10 @@ test "register_files_update" {
957 // Test IORING_REGISTER_FILES_UPDATE959 // Test IORING_REGISTER_FILES_UPDATE
958 // Only available since Linux 5.5960 // Only available since Linux 5.5
959961
960 const fd2 = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);962 const file2 = try Io.Dir.openFileAbsolute(io, "/dev/zero", .{});
961 defer posix.close(fd2);963 defer file2.close(io);
962964
963 registered_fds[fd_index] = fd2;965 registered_fds[fd_index] = file2.handle;
964 registered_fds[fd_index2] = -1;966 registered_fds[fd_index2] = -1;
965 try ring.register_files_update(0, registered_fds[0..]);967 try ring.register_files_update(0, registered_fds[0..]);
966968
...@@ -1031,15 +1033,15 @@ test "shutdown" {...@@ -1031,15 +1033,15 @@ test "shutdown" {
10311033
1032 // Socket bound, expect shutdown to work1034 // Socket bound, expect shutdown to work
1033 {1035 {
1034 const server = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);1036 const server = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
1035 defer posix.close(server);1037 defer posix.close(server);
1036 try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));1038 try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));
1037 try posix.bind(server, addrAny(&address), @sizeOf(linux.sockaddr.in));1039 try bind(server, addrAny(&address), @sizeOf(linux.sockaddr.in));
1038 try posix.listen(server, 1);1040 try listen(server, 1);
10391041
1040 // set address to the OS-chosen IP/port.1042 // set address to the OS-chosen IP/port.
1041 var slen: posix.socklen_t = @sizeOf(linux.sockaddr.in);1043 var slen: posix.socklen_t = @sizeOf(linux.sockaddr.in);
1042 try posix.getsockname(server, addrAny(&address), &slen);1044 try getsockname(server, addrAny(&address), &slen);
10431045
1044 const shutdown_sqe = try ring.shutdown(0x445445445, server, linux.SHUT.RD);1046 const shutdown_sqe = try ring.shutdown(0x445445445, server, linux.SHUT.RD);
1045 try testing.expectEqual(linux.IORING_OP.SHUTDOWN, shutdown_sqe.opcode);1047 try testing.expectEqual(linux.IORING_OP.SHUTDOWN, shutdown_sqe.opcode);
...@@ -1064,7 +1066,7 @@ test "shutdown" {...@@ -1064,7 +1066,7 @@ test "shutdown" {
10641066
1065 // Socket not bound, expect to fail with ENOTCONN1067 // Socket not bound, expect to fail with ENOTCONN
1066 {1068 {
1067 const server = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);1069 const server = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
1068 defer posix.close(server);1070 defer posix.close(server);
10691071
1070 const shutdown_sqe = ring.shutdown(0x445445445, server, linux.SHUT.RD) catch |err| switch (err) {1072 const shutdown_sqe = ring.shutdown(0x445445445, server, linux.SHUT.RD) catch |err| switch (err) {
...@@ -1339,6 +1341,8 @@ test "linkat" {...@@ -1339,6 +1341,8 @@ test "linkat" {
1339}1341}
13401342
1341test "provide_buffers: read" {1343test "provide_buffers: read" {
1344 const io = testing.io;
1345
1342 var ring = IoUring.init(1, 0) catch |err| switch (err) {1346 var ring = IoUring.init(1, 0) catch |err| switch (err) {
1343 error.SystemOutdated => return error.SkipZigTest,1347 error.SystemOutdated => return error.SkipZigTest,
1344 error.PermissionDenied => return error.SkipZigTest,1348 error.PermissionDenied => return error.SkipZigTest,
...@@ -1346,8 +1350,8 @@ test "provide_buffers: read" {...@@ -1346,8 +1350,8 @@ test "provide_buffers: read" {
1346 };1350 };
1347 defer ring.deinit();1351 defer ring.deinit();
13481352
1349 const fd = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);1353 const file = try Io.Dir.openFileAbsolute(io, "/dev/zero", .{});
1350 defer posix.close(fd);1354 defer file.close(io);
13511355
1352 const group_id = 1337;1356 const group_id = 1337;
1353 const buffer_id = 0;1357 const buffer_id = 0;
...@@ -1380,9 +1384,9 @@ test "provide_buffers: read" {...@@ -1380,9 +1384,9 @@ test "provide_buffers: read" {
13801384
1381 var i: usize = 0;1385 var i: usize = 0;
1382 while (i < buffers.len) : (i += 1) {1386 while (i < buffers.len) : (i += 1) {
1383 const sqe = try ring.read(0xdededede, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);1387 const sqe = try ring.read(0xdededede, file.handle, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);
1384 try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode);1388 try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode);
1385 try testing.expectEqual(@as(i32, fd), sqe.fd);1389 try testing.expectEqual(@as(i32, file.handle), sqe.fd);
1386 try testing.expectEqual(@as(u64, 0), sqe.addr);1390 try testing.expectEqual(@as(u64, 0), sqe.addr);
1387 try testing.expectEqual(@as(u32, buffer_len), sqe.len);1391 try testing.expectEqual(@as(u32, buffer_len), sqe.len);
1388 try testing.expectEqual(@as(u16, group_id), sqe.buf_index);1392 try testing.expectEqual(@as(u16, group_id), sqe.buf_index);
...@@ -1406,9 +1410,9 @@ test "provide_buffers: read" {...@@ -1406,9 +1410,9 @@ test "provide_buffers: read" {
1406 // This read should fail1410 // This read should fail
14071411
1408 {1412 {
1409 const sqe = try ring.read(0xdfdfdfdf, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);1413 const sqe = try ring.read(0xdfdfdfdf, file.handle, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);
1410 try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode);1414 try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode);
1411 try testing.expectEqual(@as(i32, fd), sqe.fd);1415 try testing.expectEqual(@as(i32, file.handle), sqe.fd);
1412 try testing.expectEqual(@as(u64, 0), sqe.addr);1416 try testing.expectEqual(@as(u64, 0), sqe.addr);
1413 try testing.expectEqual(@as(u32, buffer_len), sqe.len);1417 try testing.expectEqual(@as(u32, buffer_len), sqe.len);
1414 try testing.expectEqual(@as(u16, group_id), sqe.buf_index);1418 try testing.expectEqual(@as(u16, group_id), sqe.buf_index);
...@@ -1445,9 +1449,9 @@ test "provide_buffers: read" {...@@ -1445,9 +1449,9 @@ test "provide_buffers: read" {
1445 // Final read which should work1449 // Final read which should work
14461450
1447 {1451 {
1448 const sqe = try ring.read(0xdfdfdfdf, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);1452 const sqe = try ring.read(0xdfdfdfdf, file.handle, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);
1449 try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode);1453 try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode);
1450 try testing.expectEqual(@as(i32, fd), sqe.fd);1454 try testing.expectEqual(@as(i32, file.handle), sqe.fd);
1451 try testing.expectEqual(@as(u64, 0), sqe.addr);1455 try testing.expectEqual(@as(u64, 0), sqe.addr);
1452 try testing.expectEqual(@as(u32, buffer_len), sqe.len);1456 try testing.expectEqual(@as(u32, buffer_len), sqe.len);
1453 try testing.expectEqual(@as(u16, group_id), sqe.buf_index);1457 try testing.expectEqual(@as(u16, group_id), sqe.buf_index);
...@@ -1469,6 +1473,8 @@ test "provide_buffers: read" {...@@ -1469,6 +1473,8 @@ test "provide_buffers: read" {
1469}1473}
14701474
1471test "remove_buffers" {1475test "remove_buffers" {
1476 const io = testing.io;
1477
1472 var ring = IoUring.init(1, 0) catch |err| switch (err) {1478 var ring = IoUring.init(1, 0) catch |err| switch (err) {
1473 error.SystemOutdated => return error.SkipZigTest,1479 error.SystemOutdated => return error.SkipZigTest,
1474 error.PermissionDenied => return error.SkipZigTest,1480 error.PermissionDenied => return error.SkipZigTest,
...@@ -1476,8 +1482,8 @@ test "remove_buffers" {...@@ -1476,8 +1482,8 @@ test "remove_buffers" {
1476 };1482 };
1477 defer ring.deinit();1483 defer ring.deinit();
14781484
1479 const fd = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);1485 const file = try Io.Dir.openFileAbsolute(io, "/dev/zero", .{});
1480 defer posix.close(fd);1486 defer file.close(io);
14811487
1482 const group_id = 1337;1488 const group_id = 1337;
1483 const buffer_id = 0;1489 const buffer_id = 0;
...@@ -1522,7 +1528,7 @@ test "remove_buffers" {...@@ -1522,7 +1528,7 @@ test "remove_buffers" {
1522 // This read should work1528 // This read should work
15231529
1524 {1530 {
1525 _ = try ring.read(0xdfdfdfdf, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);1531 _ = try ring.read(0xdfdfdfdf, file.handle, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);
1526 try testing.expectEqual(@as(u32, 1), try ring.submit());1532 try testing.expectEqual(@as(u32, 1), try ring.submit());
15271533
1528 const cqe = try ring.copy_cqe();1534 const cqe = try ring.copy_cqe();
...@@ -1542,7 +1548,7 @@ test "remove_buffers" {...@@ -1542,7 +1548,7 @@ test "remove_buffers" {
1542 // Final read should _not_ work1548 // Final read should _not_ work
15431549
1544 {1550 {
1545 _ = try ring.read(0xdfdfdfdf, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);1551 _ = try ring.read(0xdfdfdfdf, file.handle, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);
1546 try testing.expectEqual(@as(u32, 1), try ring.submit());1552 try testing.expectEqual(@as(u32, 1), try ring.submit());
15471553
1548 const cqe = try ring.copy_cqe();1554 const cqe = try ring.copy_cqe();
...@@ -1747,7 +1753,7 @@ test "accept multishot" {...@@ -1747,7 +1753,7 @@ test "accept multishot" {
1747 var nr: usize = 4; // number of clients to connect1753 var nr: usize = 4; // number of clients to connect
1748 while (nr > 0) : (nr -= 1) {1754 while (nr > 0) : (nr -= 1) {
1749 // connect client1755 // connect client
1750 const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);1756 const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
1751 errdefer posix.close(client);1757 errdefer posix.close(client);
1752 try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));1758 try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
17531759
...@@ -1856,7 +1862,7 @@ test "accept_direct" {...@@ -1856,7 +1862,7 @@ test "accept_direct" {
1856 try testing.expectEqual(@as(u32, 1), try ring.submit());1862 try testing.expectEqual(@as(u32, 1), try ring.submit());
18571863
1858 // connect1864 // connect
1859 const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);1865 const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
1860 try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));1866 try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
1861 defer posix.close(client);1867 defer posix.close(client);
18621868
...@@ -1868,7 +1874,7 @@ test "accept_direct" {...@@ -1868,7 +1874,7 @@ test "accept_direct" {
1868 try testing.expect(cqe_accept.user_data == accept_userdata);1874 try testing.expect(cqe_accept.user_data == accept_userdata);
18691875
1870 // send data1876 // send data
1871 _ = try posix.send(client, buffer_send, 0);1877 _ = try send(client, buffer_send, 0);
18721878
1873 // Example of how to use registered fd:1879 // Example of how to use registered fd:
1874 // Submit receive to fixed file returned by accept (fd_index).1880 // Submit receive to fixed file returned by accept (fd_index).
...@@ -1890,7 +1896,7 @@ test "accept_direct" {...@@ -1890,7 +1896,7 @@ test "accept_direct" {
1890 _ = try ring.accept_direct(accept_userdata, listener_socket, null, null, 0);1896 _ = try ring.accept_direct(accept_userdata, listener_socket, null, null, 0);
1891 try testing.expectEqual(@as(u32, 1), try ring.submit());1897 try testing.expectEqual(@as(u32, 1), try ring.submit());
1892 // connect1898 // connect
1893 const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);1899 const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
1894 try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));1900 try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
1895 defer posix.close(client);1901 defer posix.close(client);
1896 // completion with error1902 // completion with error
...@@ -1940,7 +1946,7 @@ test "accept_multishot_direct" {...@@ -1940,7 +1946,7 @@ test "accept_multishot_direct" {
19401946
1941 for (registered_fds) |_| {1947 for (registered_fds) |_| {
1942 // connect1948 // connect
1943 const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);1949 const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
1944 try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));1950 try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
1945 defer posix.close(client);1951 defer posix.close(client);
19461952
...@@ -1955,7 +1961,7 @@ test "accept_multishot_direct" {...@@ -1955,7 +1961,7 @@ test "accept_multishot_direct" {
1955 // Multishot is terminated (more flag is not set).1961 // Multishot is terminated (more flag is not set).
1956 {1962 {
1957 // connect1963 // connect
1958 const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);1964 const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
1959 try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));1965 try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
1960 defer posix.close(client);1966 defer posix.close(client);
1961 // completion with error1967 // completion with error
...@@ -2456,7 +2462,7 @@ test "bind/listen/connect" {...@@ -2456,7 +2462,7 @@ test "bind/listen/connect" {
24562462
2457 // Read system assigned port into addr2463 // Read system assigned port into addr
2458 var addr_len: posix.socklen_t = @sizeOf(linux.sockaddr.in);2464 var addr_len: posix.socklen_t = @sizeOf(linux.sockaddr.in);
2459 try posix.getsockname(listen_fd, addrAny(&addr), &addr_len);2465 try getsockname(listen_fd, addrAny(&addr), &addr_len);
24602466
2461 break :brk listen_fd;2467 break :brk listen_fd;
2462 };2468 };
...@@ -2611,7 +2617,7 @@ pub fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness {...@@ -2611,7 +2617,7 @@ pub fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness {
2611 _ = try ring.accept(0xaaaaaaaa, listener_socket, &accept_addr, &accept_addr_len, 0);2617 _ = try ring.accept(0xaaaaaaaa, listener_socket, &accept_addr, &accept_addr_len, 0);
26122618
2613 // Create a TCP client socket2619 // Create a TCP client socket
2614 const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);2620 const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
2615 errdefer posix.close(client);2621 errdefer posix.close(client);
2616 _ = try ring.connect(0xcccccccc, client, addrAny(&address), @sizeOf(linux.sockaddr.in));2622 _ = try ring.connect(0xcccccccc, client, addrAny(&address), @sizeOf(linux.sockaddr.in));
26172623
...@@ -2651,16 +2657,16 @@ pub fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness {...@@ -2651,16 +2657,16 @@ pub fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness {
26512657
2652fn createListenerSocket(address: *linux.sockaddr.in) !posix.socket_t {2658fn createListenerSocket(address: *linux.sockaddr.in) !posix.socket_t {
2653 const kernel_backlog = 1;2659 const kernel_backlog = 1;
2654 const listener_socket = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);2660 const listener_socket = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
2655 errdefer posix.close(listener_socket);2661 errdefer posix.close(listener_socket);
26562662
2657 try posix.setsockopt(listener_socket, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));2663 try posix.setsockopt(listener_socket, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));
2658 try posix.bind(listener_socket, addrAny(address), @sizeOf(linux.sockaddr.in));2664 try bind(listener_socket, addrAny(address), @sizeOf(linux.sockaddr.in));
2659 try posix.listen(listener_socket, kernel_backlog);2665 try listen(listener_socket, kernel_backlog);
26602666
2661 // set address to the OS-chosen IP/port.2667 // set address to the OS-chosen IP/port.
2662 var slen: posix.socklen_t = @sizeOf(linux.sockaddr.in);2668 var slen: posix.socklen_t = @sizeOf(linux.sockaddr.in);
2663 try posix.getsockname(listener_socket, addrAny(address), &slen);2669 try getsockname(listener_socket, addrAny(address), &slen);
26642670
2665 return listener_socket;2671 return listener_socket;
2666}2672}
...@@ -2689,3 +2695,40 @@ inline fn skipKernelLessThan(required: std.SemanticVersion) !void {...@@ -2689,3 +2695,40 @@ inline fn skipKernelLessThan(required: std.SemanticVersion) !void {
2689fn addrAny(addr: *linux.sockaddr.in) *linux.sockaddr {2695fn addrAny(addr: *linux.sockaddr.in) *linux.sockaddr {
2690 return @ptrCast(addr);2696 return @ptrCast(addr);
2691}2697}
2698
2699fn socket(domain: u32, socket_type: u32, protocol: u32) !posix.socket_t {
2700 const rc = posix.system.socket(domain, socket_type, protocol);
2701 switch (posix.errno(rc)) {
2702 .SUCCESS => return @intCast(rc),
2703 else => return error.SocketCreationFailure,
2704 }
2705}
2706
2707fn bind(sock: posix.socket_t, addr: *const posix.sockaddr, len: posix.socklen_t) !void {
2708 switch (posix.errno(posix.system.bind(sock, addr, len))) {
2709 .SUCCESS => return,
2710 else => return error.BindFailure,
2711 }
2712}
2713
2714fn listen(sock: posix.socket_t, backlog: u31) !void {
2715 switch (posix.errno(posix.system.listen(sock, backlog))) {
2716 .SUCCESS => return,
2717 else => return error.ListenFailure,
2718 }
2719}
2720
2721fn getsockname(sock: posix.socket_t, addr: *posix.sockaddr, addrlen: *posix.socklen_t) !void {
2722 switch (posix.errno(posix.system.getsockname(sock, addr, addrlen))) {
2723 .SUCCESS => return,
2724 else => return error.GetSockNameFailure,
2725 }
2726}
2727
2728fn send(sockfd: posix.socket_t, buf: []const u8, flags: u32) !usize {
2729 const rc = posix.system.sendto(sockfd, buf.ptr, buf.len, flags, null, 0);
2730 switch (posix.errno(rc)) {
2731 .SUCCESS => return @intCast(rc),
2732 else => return error.SendFailed,
2733 }
2734}
lib/std/posix.zig+1-1173
...@@ -446,165 +446,8 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {...@@ -446,165 +446,8 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
446 }446 }
447}447}
448448
449pub const WriteError = error{
450 DiskQuota,
451 FileTooBig,
452 InputOutput,
453 NoSpaceLeft,
454 DeviceBusy,
455 InvalidArgument,
456
457 /// File descriptor does not hold the required rights to write to it.
458 AccessDenied,
459 PermissionDenied,
460 BrokenPipe,
461 SystemResources,
462 Canceled,
463 NotOpenForWriting,
464
465 /// The process cannot access the file because another process has locked
466 /// a portion of the file. Windows-only.
467 LockViolation,
468
469 /// This error occurs when no global event loop is configured,
470 /// and reading from the file descriptor would block.
471 WouldBlock,
472
473 /// Connection reset by peer.
474 ConnectionResetByPeer,
475
476 /// This error occurs in Linux if the process being written to
477 /// no longer exists.
478 ProcessNotFound,
479 /// This error occurs when a device gets disconnected before or mid-flush
480 /// while it's being written to - errno(6): No such device or address.
481 NoDevice,
482
483 /// The socket type requires that message be sent atomically, and the size of the message
484 /// to be sent made this impossible. The message is not transmitted.
485 MessageOversize,
486} || UnexpectedError;
487
488/// Write to a file descriptor.
489/// Retries when interrupted by a signal.
490/// Returns the number of bytes written. If nonzero bytes were supplied, this will be nonzero.
491///
492/// Note that a successful write() may transfer fewer than count bytes. Such partial writes can
493/// occur for various reasons; for example, because there was insufficient space on the disk
494/// device to write all of the requested bytes, or because a blocked write() to a socket, pipe, or
495/// similar was interrupted by a signal handler after it had transferred some, but before it had
496/// transferred all of the requested bytes. In the event of a partial write, the caller can make
497/// another write() call to transfer the remaining bytes. The subsequent call will either
498/// transfer further bytes or may result in an error (e.g., if the disk is now full).
499///
500/// For POSIX systems, if `fd` is opened in non blocking mode, the function will
501/// return error.WouldBlock when EAGAIN is received.
502/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
503/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
504///
505/// Linux has a limit on how many bytes may be transferred in one `write` call, which is `0x7ffff000`
506/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
507/// well as stuffing the errno codes into the last `4096` values. This is noted on the `write` man page.
508/// The limit on Darwin is `0x7fffffff`, trying to read more than that returns EINVAL.
509/// The corresponding POSIX limit is `maxInt(isize)`.
510pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
511 if (bytes.len == 0) return 0;
512 if (native_os == .windows) @compileError("unsupported OS");
513 if (native_os == .wasi) @compileError("unsupported OS");
514
515 const max_count = switch (native_os) {
516 .linux => 0x7ffff000,
517 .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => maxInt(i32),
518 else => maxInt(isize),
519 };
520 while (true) {
521 const rc = system.write(fd, bytes.ptr, @min(bytes.len, max_count));
522 switch (errno(rc)) {
523 .SUCCESS => return @intCast(rc),
524 .INTR => continue,
525 .INVAL => return error.InvalidArgument,
526 .FAULT => unreachable,
527 .AGAIN => return error.WouldBlock,
528 .BADF => return error.NotOpenForWriting, // can be a race condition.
529 .DESTADDRREQ => unreachable, // `connect` was never called.
530 .DQUOT => return error.DiskQuota,
531 .FBIG => return error.FileTooBig,
532 .IO => return error.InputOutput,
533 .NOSPC => return error.NoSpaceLeft,
534 .ACCES => return error.AccessDenied,
535 .PERM => return error.PermissionDenied,
536 .PIPE => return error.BrokenPipe,
537 .CONNRESET => return error.ConnectionResetByPeer,
538 .BUSY => return error.DeviceBusy,
539 .NXIO => return error.NoDevice,
540 .MSGSIZE => return error.MessageOversize,
541 else => |err| return unexpectedErrno(err),
542 }
543 }
544}
545
546pub const OpenError = std.Io.File.OpenError || error{WouldBlock};449pub const OpenError = std.Io.File.OpenError || error{WouldBlock};
547450
548/// Open and possibly create a file. Keeps trying if it gets interrupted.
549/// On Windows, `file_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
550/// On WASI, `file_path` should be encoded as valid UTF-8.
551/// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding.
552/// See also `openZ`.
553pub fn open(file_path: []const u8, flags: O, perm: mode_t) OpenError!fd_t {
554 if (native_os == .windows) {
555 @compileError("Windows does not support POSIX; use Windows-specific API or cross-platform std.fs API");
556 } else if (native_os == .wasi and !builtin.link_libc) {
557 return openat(AT.FDCWD, file_path, flags, perm);
558 }
559 const file_path_c = try toPosixPath(file_path);
560 return openZ(&file_path_c, flags, perm);
561}
562
563/// Open and possibly create a file. Keeps trying if it gets interrupted.
564/// On Windows, `file_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
565/// On WASI, `file_path` should be encoded as valid UTF-8.
566/// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding.
567/// See also `open`.
568pub fn openZ(file_path: [*:0]const u8, flags: O, perm: mode_t) OpenError!fd_t {
569 if (native_os == .windows) {
570 @compileError("Windows does not support POSIX; use Windows-specific API or cross-platform std.fs API");
571 } else if (native_os == .wasi and !builtin.link_libc) {
572 return open(mem.sliceTo(file_path, 0), flags, perm);
573 }
574
575 const open_sym = if (lfs64_abi) system.open64 else system.open;
576 while (true) {
577 const rc = open_sym(file_path, flags, perm);
578 switch (errno(rc)) {
579 .SUCCESS => return @intCast(rc),
580 .INTR => continue,
581
582 .FAULT => unreachable,
583 .INVAL => return error.BadPathName,
584 .ACCES => return error.AccessDenied,
585 .FBIG => return error.FileTooBig,
586 .OVERFLOW => return error.FileTooBig,
587 .ISDIR => return error.IsDir,
588 .LOOP => return error.SymLinkLoop,
589 .MFILE => return error.ProcessFdQuotaExceeded,
590 .NAMETOOLONG => return error.NameTooLong,
591 .NFILE => return error.SystemFdQuotaExceeded,
592 .NODEV => return error.NoDevice,
593 .NOENT => return error.FileNotFound,
594 // Can happen on Linux when opening procfs files.
595 .SRCH => return error.FileNotFound,
596 .NOMEM => return error.SystemResources,
597 .NOSPC => return error.NoSpaceLeft,
598 .NOTDIR => return error.NotDir,
599 .PERM => return error.PermissionDenied,
600 .EXIST => return error.PathAlreadyExists,
601 .BUSY => return error.DeviceBusy,
602 .ILSEQ => return error.BadPathName,
603 else => |err| return unexpectedErrno(err),
604 }
605 }
606}
607
608/// Open and possibly create a file. Keeps trying if it gets interrupted.451/// Open and possibly create a file. Keeps trying if it gets interrupted.
609/// `file_path` is relative to the open directory handle `dir_fd`.452/// `file_path` is relative to the open directory handle `dir_fd`.
610/// On Windows, `file_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).453/// On Windows, `file_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
...@@ -708,67 +551,6 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {...@@ -708,67 +551,6 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {
708 }551 }
709}552}
710553
711pub const SetEidError = error{
712 InvalidUserId,
713 PermissionDenied,
714} || UnexpectedError;
715
716pub const SetIdError = error{ResourceLimitReached} || SetEidError;
717
718pub fn setuid(uid: uid_t) SetIdError!void {
719 switch (errno(system.setuid(uid))) {
720 .SUCCESS => return,
721 .AGAIN => return error.ResourceLimitReached,
722 .INVAL => return error.InvalidUserId,
723 .PERM => return error.PermissionDenied,
724 else => |err| return unexpectedErrno(err),
725 }
726}
727
728pub fn seteuid(uid: uid_t) SetEidError!void {
729 switch (errno(system.seteuid(uid))) {
730 .SUCCESS => return,
731 .INVAL => return error.InvalidUserId,
732 .PERM => return error.PermissionDenied,
733 else => |err| return unexpectedErrno(err),
734 }
735}
736
737pub fn setgid(gid: gid_t) SetIdError!void {
738 switch (errno(system.setgid(gid))) {
739 .SUCCESS => return,
740 .AGAIN => return error.ResourceLimitReached,
741 .INVAL => return error.InvalidUserId,
742 .PERM => return error.PermissionDenied,
743 else => |err| return unexpectedErrno(err),
744 }
745}
746
747pub fn setegid(uid: uid_t) SetEidError!void {
748 switch (errno(system.setegid(uid))) {
749 .SUCCESS => return,
750 .INVAL => return error.InvalidUserId,
751 .PERM => return error.PermissionDenied,
752 else => |err| return unexpectedErrno(err),
753 }
754}
755
756pub fn getuid() uid_t {
757 return system.getuid();
758}
759
760pub fn geteuid() uid_t {
761 return system.geteuid();
762}
763
764pub fn getgid() gid_t {
765 return system.getgid();
766}
767
768pub fn getegid() gid_t {
769 return system.getegid();
770}
771
772pub const SocketError = error{554pub const SocketError = error{
773 /// Permission to create a socket of the specified type and/or555 /// Permission to create a socket of the specified type and/or
774 /// pro‐tocol is denied.556 /// pro‐tocol is denied.
...@@ -797,35 +579,6 @@ pub const SocketError = error{...@@ -797,35 +579,6 @@ pub const SocketError = error{
797 SocketTypeNotSupported,579 SocketTypeNotSupported,
798} || UnexpectedError;580} || UnexpectedError;
799581
800pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!socket_t {
801 const have_sock_flags = !builtin.target.os.tag.isDarwin() and native_os != .haiku;
802 const filtered_sock_type = if (!have_sock_flags)
803 socket_type & ~@as(u32, SOCK.NONBLOCK | SOCK.CLOEXEC)
804 else
805 socket_type;
806 const rc = system.socket(domain, filtered_sock_type, protocol);
807 switch (errno(rc)) {
808 .SUCCESS => {
809 const fd: fd_t = @intCast(rc);
810 errdefer close(fd);
811 if (!have_sock_flags) {
812 try setSockFlags(fd, socket_type);
813 }
814 return fd;
815 },
816 .ACCES => return error.AccessDenied,
817 .AFNOSUPPORT => return error.AddressFamilyUnsupported,
818 .INVAL => return error.ProtocolFamilyNotAvailable,
819 .MFILE => return error.ProcessFdQuotaExceeded,
820 .NFILE => return error.SystemFdQuotaExceeded,
821 .NOBUFS => return error.SystemResources,
822 .NOMEM => return error.SystemResources,
823 .PROTONOSUPPORT => return error.ProtocolNotSupported,
824 .PROTOTYPE => return error.SocketTypeNotSupported,
825 else => |err| return unexpectedErrno(err),
826 }
827}
828
829pub fn socketpair(domain: u32, socket_type: u32, protocol: u32) SocketError![2]socket_t {582pub fn socketpair(domain: u32, socket_type: u32, protocol: u32) SocketError![2]socket_t {
830 // Note to the future: we could provide a shim here for e.g. windows which583 // Note to the future: we could provide a shim here for e.g. windows which
831 // creates a listening socket, then creates a second socket and connects it584 // creates a listening socket, then creates a second socket and connects it
...@@ -866,168 +619,6 @@ pub fn socketpair(domain: u32, socket_type: u32, protocol: u32) SocketError![2]s...@@ -866,168 +619,6 @@ pub fn socketpair(domain: u32, socket_type: u32, protocol: u32) SocketError![2]s
866 }619 }
867}620}
868621
869pub const ShutdownError = error{
870 ConnectionAborted,
871
872 /// Connection was reset by peer, application should close socket as it is no longer usable.
873 ConnectionResetByPeer,
874 BlockingOperationInProgress,
875
876 /// The network subsystem has failed.
877 NetworkDown,
878
879 /// The socket is not connected (connection-oriented sockets only).
880 SocketUnconnected,
881 SystemResources,
882} || UnexpectedError;
883
884pub const ShutdownHow = enum { recv, send, both };
885
886/// Shutdown socket send/receive operations
887pub fn shutdown(sock: socket_t, how: ShutdownHow) ShutdownError!void {
888 if (native_os == .windows) {
889 const result = windows.ws2_32.shutdown(sock, switch (how) {
890 .recv => windows.ws2_32.SD_RECEIVE,
891 .send => windows.ws2_32.SD_SEND,
892 .both => windows.ws2_32.SD_BOTH,
893 });
894 if (0 != result) switch (windows.ws2_32.WSAGetLastError()) {
895 .ECONNABORTED => return error.ConnectionAborted,
896 .ECONNRESET => return error.ConnectionResetByPeer,
897 .EINPROGRESS => return error.BlockingOperationInProgress,
898 .EINVAL => unreachable,
899 .ENETDOWN => return error.NetworkDown,
900 .ENOTCONN => return error.SocketUnconnected,
901 .ENOTSOCK => unreachable,
902 .NOTINITIALISED => unreachable,
903 else => |err| return windows.unexpectedWSAError(err),
904 };
905 } else {
906 const rc = system.shutdown(sock, switch (how) {
907 .recv => SHUT.RD,
908 .send => SHUT.WR,
909 .both => SHUT.RDWR,
910 });
911 switch (errno(rc)) {
912 .SUCCESS => return,
913 .BADF => unreachable,
914 .INVAL => unreachable,
915 .NOTCONN => return error.SocketUnconnected,
916 .NOTSOCK => unreachable,
917 .NOBUFS => return error.SystemResources,
918 else => |err| return unexpectedErrno(err),
919 }
920 }
921}
922
923pub const BindError = error{
924 SymLinkLoop,
925 NameTooLong,
926 FileNotFound,
927 NotDir,
928 ReadOnlyFileSystem,
929 AccessDenied,
930} || std.Io.net.IpAddress.BindError;
931
932pub fn bind(sock: socket_t, addr: *const sockaddr, len: socklen_t) BindError!void {
933 if (native_os == .windows) {
934 @compileError("use std.Io instead");
935 } else {
936 const rc = system.bind(sock, addr, len);
937 switch (errno(rc)) {
938 .SUCCESS => return,
939 .ACCES, .PERM => return error.AccessDenied,
940 .ADDRINUSE => return error.AddressInUse,
941 .BADF => unreachable, // always a race condition if this error is returned
942 .INVAL => unreachable, // invalid parameters
943 .NOTSOCK => unreachable, // invalid `sockfd`
944 .AFNOSUPPORT => return error.AddressFamilyUnsupported,
945 .ADDRNOTAVAIL => return error.AddressUnavailable,
946 .FAULT => unreachable, // invalid `addr` pointer
947 .LOOP => return error.SymLinkLoop,
948 .NAMETOOLONG => return error.NameTooLong,
949 .NOENT => return error.FileNotFound,
950 .NOMEM => return error.SystemResources,
951 .NOTDIR => return error.NotDir,
952 .ROFS => return error.ReadOnlyFileSystem,
953 else => |err| return unexpectedErrno(err),
954 }
955 }
956 unreachable;
957}
958
959pub const ListenError = error{
960 FileDescriptorNotASocket,
961 OperationUnsupported,
962} || std.Io.net.IpAddress.ListenError || std.Io.net.UnixAddress.ListenError;
963
964pub fn listen(sock: socket_t, backlog: u31) ListenError!void {
965 if (native_os == .windows) {
966 @compileError("use std.Io instead");
967 } else {
968 const rc = system.listen(sock, backlog);
969 switch (errno(rc)) {
970 .SUCCESS => return,
971 .ADDRINUSE => return error.AddressInUse,
972 .BADF => unreachable,
973 .NOTSOCK => return error.FileDescriptorNotASocket,
974 .OPNOTSUPP => return error.OperationUnsupported,
975 else => |err| return unexpectedErrno(err),
976 }
977 }
978}
979
980pub const AcceptError = std.Io.net.Server.AcceptError;
981
982pub fn accept(
983 sock: socket_t,
984 addr: ?*sockaddr,
985 addr_size: ?*socklen_t,
986 flags: u32,
987) AcceptError!socket_t {
988 const have_accept4 = !(builtin.target.os.tag.isDarwin() or native_os == .windows or native_os == .haiku);
989 assert(0 == (flags & ~@as(u32, SOCK.NONBLOCK | SOCK.CLOEXEC))); // Unsupported flag(s)
990
991 const accepted_sock: socket_t = while (true) {
992 const rc = if (have_accept4)
993 system.accept4(sock, addr, addr_size, flags)
994 else
995 system.accept(sock, addr, addr_size);
996
997 if (native_os == .windows) {
998 @compileError("use std.Io instead");
999 } else {
1000 switch (errno(rc)) {
1001 .SUCCESS => break @intCast(rc),
1002 .INTR => continue,
1003 .AGAIN => return error.WouldBlock,
1004 .BADF => unreachable, // always a race condition
1005 .CONNABORTED => return error.ConnectionAborted,
1006 .FAULT => unreachable,
1007 .INVAL => return error.SocketNotListening,
1008 .NOTSOCK => unreachable,
1009 .MFILE => return error.ProcessFdQuotaExceeded,
1010 .NFILE => return error.SystemFdQuotaExceeded,
1011 .NOBUFS => return error.SystemResources,
1012 .NOMEM => return error.SystemResources,
1013 .OPNOTSUPP => unreachable,
1014 .PROTO => return error.ProtocolFailure,
1015 .PERM => return error.BlockedByFirewall,
1016 else => |err| return unexpectedErrno(err),
1017 }
1018 }
1019 };
1020
1021 errdefer switch (native_os) {
1022 .windows => windows.closesocket(accepted_sock) catch unreachable,
1023 else => close(accepted_sock),
1024 };
1025 if (!have_accept4) {
1026 try setSockFlags(accepted_sock, flags);
1027 }
1028 return accepted_sock;
1029}
1030
1031fn setSockFlags(sock: socket_t, flags: u32) !void {622fn setSockFlags(sock: socket_t, flags: u32) !void {
1032 if ((flags & SOCK.CLOEXEC) != 0) {623 if ((flags & SOCK.CLOEXEC) != 0) {
1033 if (native_os == .windows) {624 if (native_os == .windows) {
...@@ -1086,94 +677,6 @@ fn setSockFlags(sock: socket_t, flags: u32) !void {...@@ -1086,94 +677,6 @@ fn setSockFlags(sock: socket_t, flags: u32) !void {
1086 }677 }
1087}678}
1088679
1089pub const EpollCreateError = error{
1090 /// The per-user limit on the number of epoll instances imposed by
1091 /// /proc/sys/fs/epoll/max_user_instances was encountered. See epoll(7) for further
1092 /// details.
1093 /// Or, The per-process limit on the number of open file descriptors has been reached.
1094 ProcessFdQuotaExceeded,
1095
1096 /// The system-wide limit on the total number of open files has been reached.
1097 SystemFdQuotaExceeded,
1098
1099 /// There was insufficient memory to create the kernel object.
1100 SystemResources,
1101} || UnexpectedError;
1102
1103pub fn epoll_create1(flags: u32) EpollCreateError!i32 {
1104 const rc = system.epoll_create1(flags);
1105 switch (errno(rc)) {
1106 .SUCCESS => return @intCast(rc),
1107 else => |err| return unexpectedErrno(err),
1108
1109 .INVAL => unreachable,
1110 .MFILE => return error.ProcessFdQuotaExceeded,
1111 .NFILE => return error.SystemFdQuotaExceeded,
1112 .NOMEM => return error.SystemResources,
1113 }
1114}
1115
1116pub const EpollCtlError = error{
1117 /// op was EPOLL_CTL_ADD, and the supplied file descriptor fd is already registered
1118 /// with this epoll instance.
1119 FileDescriptorAlreadyPresentInSet,
1120
1121 /// fd refers to an epoll instance and this EPOLL_CTL_ADD operation would result in a
1122 /// circular loop of epoll instances monitoring one another.
1123 OperationCausesCircularLoop,
1124
1125 /// op was EPOLL_CTL_MOD or EPOLL_CTL_DEL, and fd is not registered with this epoll
1126 /// instance.
1127 FileDescriptorNotRegistered,
1128
1129 /// There was insufficient memory to handle the requested op control operation.
1130 SystemResources,
1131
1132 /// The limit imposed by /proc/sys/fs/epoll/max_user_watches was encountered while
1133 /// trying to register (EPOLL_CTL_ADD) a new file descriptor on an epoll instance.
1134 /// See epoll(7) for further details.
1135 UserResourceLimitReached,
1136
1137 /// The target file fd does not support epoll. This error can occur if fd refers to,
1138 /// for example, a regular file or a directory.
1139 FileDescriptorIncompatibleWithEpoll,
1140} || UnexpectedError;
1141
1142pub fn epoll_ctl(epfd: i32, op: u32, fd: i32, event: ?*system.epoll_event) EpollCtlError!void {
1143 const rc = system.epoll_ctl(epfd, op, fd, event);
1144 switch (errno(rc)) {
1145 .SUCCESS => return,
1146 else => |err| return unexpectedErrno(err),
1147
1148 .BADF => unreachable, // always a race condition if this happens
1149 .EXIST => return error.FileDescriptorAlreadyPresentInSet,
1150 .INVAL => unreachable,
1151 .LOOP => return error.OperationCausesCircularLoop,
1152 .NOENT => return error.FileDescriptorNotRegistered,
1153 .NOMEM => return error.SystemResources,
1154 .NOSPC => return error.UserResourceLimitReached,
1155 .PERM => return error.FileDescriptorIncompatibleWithEpoll,
1156 }
1157}
1158
1159/// Waits for an I/O event on an epoll file descriptor.
1160/// Returns the number of file descriptors ready for the requested I/O,
1161/// or zero if no file descriptor became ready during the requested timeout milliseconds.
1162pub fn epoll_wait(epfd: i32, events: []system.epoll_event, timeout: i32) usize {
1163 while (true) {
1164 // TODO get rid of the @intCast
1165 const rc = system.epoll_wait(epfd, events.ptr, @intCast(events.len), timeout);
1166 switch (errno(rc)) {
1167 .SUCCESS => return @intCast(rc),
1168 .INTR => continue,
1169 .BADF => unreachable,
1170 .FAULT => unreachable,
1171 .INVAL => unreachable,
1172 else => unreachable,
1173 }
1174 }
1175}
1176
1177pub const EventFdError = error{680pub const EventFdError = error{
1178 SystemResources,681 SystemResources,
1179 ProcessFdQuotaExceeded,682 ProcessFdQuotaExceeded,
...@@ -1207,35 +710,6 @@ pub const GetSockNameError = error{...@@ -1207,35 +710,6 @@ pub const GetSockNameError = error{
1207 FileDescriptorNotASocket,710 FileDescriptorNotASocket,
1208} || UnexpectedError;711} || UnexpectedError;
1209712
1210pub fn getsockname(sock: socket_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void {
1211 if (native_os == .windows) {
1212 const rc = windows.getsockname(sock, addr, addrlen);
1213 if (rc == windows.ws2_32.SOCKET_ERROR) {
1214 switch (windows.ws2_32.WSAGetLastError()) {
1215 .NOTINITIALISED => unreachable,
1216 .ENETDOWN => return error.NetworkDown,
1217 .EFAULT => unreachable, // addr or addrlen have invalid pointers or addrlen points to an incorrect value
1218 .ENOTSOCK => return error.FileDescriptorNotASocket,
1219 .EINVAL => return error.SocketNotBound,
1220 else => |err| return windows.unexpectedWSAError(err),
1221 }
1222 }
1223 return;
1224 } else {
1225 const rc = system.getsockname(sock, addr, addrlen);
1226 switch (errno(rc)) {
1227 .SUCCESS => return,
1228 else => |err| return unexpectedErrno(err),
1229
1230 .BADF => unreachable, // always a race condition
1231 .FAULT => unreachable,
1232 .INVAL => unreachable, // invalid parameters
1233 .NOTSOCK => return error.FileDescriptorNotASocket,
1234 .NOBUFS => return error.SystemResources,
1235 }
1236 }
1237}
1238
1239pub fn getpeername(sock: socket_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void {713pub fn getpeername(sock: socket_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void {
1240 if (native_os == .windows) {714 if (native_os == .windows) {
1241 const rc = windows.getpeername(sock, addr, addrlen);715 const rc = windows.getpeername(sock, addr, addrlen);
...@@ -1295,72 +769,7 @@ pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) Conne...@@ -1295,72 +769,7 @@ pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) Conne
1295 .NOENT => return error.FileNotFound, // Returned when socket is AF.UNIX and the given path does not exist.769 .NOENT => return error.FileNotFound, // Returned when socket is AF.UNIX and the given path does not exist.
1296 .CONNABORTED => unreachable, // Tried to reuse socket that previously received error.ConnectionRefused.770 .CONNABORTED => unreachable, // Tried to reuse socket that previously received error.ConnectionRefused.
1297 else => |err| return unexpectedErrno(err),771 else => |err| return unexpectedErrno(err),
1298 }772 }
1299 }
1300}
1301
1302pub const GetSockOptError = error{
1303 /// The calling process does not have the appropriate privileges.
1304 AccessDenied,
1305
1306 /// The option is not supported by the protocol.
1307 InvalidProtocolOption,
1308
1309 /// Insufficient resources are available in the system to complete the call.
1310 SystemResources,
1311} || UnexpectedError;
1312
1313pub fn getsockopt(fd: socket_t, level: i32, optname: u32, opt: []u8) GetSockOptError!void {
1314 var len: socklen_t = @intCast(opt.len);
1315 switch (errno(system.getsockopt(fd, level, optname, opt.ptr, &len))) {
1316 .SUCCESS => {
1317 std.debug.assert(len == opt.len);
1318 },
1319 .BADF => unreachable,
1320 .NOTSOCK => unreachable,
1321 .INVAL => unreachable,
1322 .FAULT => unreachable,
1323 .NOPROTOOPT => return error.InvalidProtocolOption,
1324 .NOMEM => return error.SystemResources,
1325 .NOBUFS => return error.SystemResources,
1326 .ACCES => return error.AccessDenied,
1327 else => |err| return unexpectedErrno(err),
1328 }
1329}
1330
1331pub fn getsockoptError(sockfd: fd_t) ConnectError!void {
1332 var err_code: i32 = undefined;
1333 var size: u32 = @sizeOf(u32);
1334 const rc = system.getsockopt(sockfd, SOL.SOCKET, SO.ERROR, @ptrCast(&err_code), &size);
1335 assert(size == 4);
1336 switch (errno(rc)) {
1337 .SUCCESS => switch (@as(E, @enumFromInt(err_code))) {
1338 .SUCCESS => return,
1339 .ACCES => return error.AccessDenied,
1340 .PERM => return error.PermissionDenied,
1341 .ADDRINUSE => return error.AddressInUse,
1342 .ADDRNOTAVAIL => return error.AddressUnavailable,
1343 .AFNOSUPPORT => return error.AddressFamilyUnsupported,
1344 .AGAIN => return error.SystemResources,
1345 .ALREADY => return error.ConnectionPending,
1346 .BADF => unreachable, // sockfd is not a valid open file descriptor.
1347 .CONNREFUSED => return error.ConnectionRefused,
1348 .FAULT => unreachable, // The socket structure address is outside the user's address space.
1349 .ISCONN => return error.AlreadyConnected, // The socket is already connected.
1350 .HOSTUNREACH => return error.NetworkUnreachable,
1351 .NETUNREACH => return error.NetworkUnreachable,
1352 .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
1353 .PROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
1354 .TIMEDOUT => return error.Timeout,
1355 .CONNRESET => return error.ConnectionResetByPeer,
1356 else => |err| return unexpectedErrno(err),
1357 },
1358 .BADF => unreachable, // The argument sockfd is not a valid file descriptor.
1359 .FAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space.
1360 .INVAL => unreachable,
1361 .NOPROTOOPT => unreachable, // The option is unknown at the level indicated.
1362 .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
1363 else => |err| return unexpectedErrno(err),
1364 }773 }
1365}774}
1366775
...@@ -1383,120 +792,6 @@ pub fn fstat(fd: fd_t) FStatError!Stat {...@@ -1383,120 +792,6 @@ pub fn fstat(fd: fd_t) FStatError!Stat {
1383 }792 }
1384}793}
1385794
1386pub const FStatAtError = FStatError || error{
1387 NameTooLong,
1388 FileNotFound,
1389 SymLinkLoop,
1390 BadPathName,
1391};
1392
1393/// Similar to `fstat`, but returns stat of a resource pointed to by `pathname`
1394/// which is relative to `dirfd` handle.
1395/// On WASI, `pathname` should be encoded as valid UTF-8.
1396/// On other platforms, `pathname` is an opaque sequence of bytes with no particular encoding.
1397/// See also `fstatatZ`.
1398pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat {
1399 if (native_os == .wasi and !builtin.link_libc) {
1400 @compileError("use std.Io instead");
1401 } else if (native_os == .windows) {
1402 @compileError("fstatat is not yet implemented on Windows");
1403 } else {
1404 const pathname_c = try toPosixPath(pathname);
1405 return fstatatZ(dirfd, &pathname_c, flags);
1406 }
1407}
1408
1409/// Same as `fstatat` but `pathname` is null-terminated.
1410/// See also `fstatat`.
1411pub fn fstatatZ(dirfd: fd_t, pathname: [*:0]const u8, flags: u32) FStatAtError!Stat {
1412 if (native_os == .wasi and !builtin.link_libc) {
1413 @compileError("use std.Io instead");
1414 }
1415
1416 var stat = mem.zeroes(Stat);
1417 switch (errno(system.fstatat(dirfd, pathname, &stat, flags))) {
1418 .SUCCESS => return stat,
1419 .INVAL => unreachable,
1420 .BADF => unreachable, // Always a race condition.
1421 .NOMEM => return error.SystemResources,
1422 .ACCES => return error.AccessDenied,
1423 .PERM => return error.PermissionDenied,
1424 .FAULT => unreachable,
1425 .NAMETOOLONG => return error.NameTooLong,
1426 .LOOP => return error.SymLinkLoop,
1427 .NOENT => return error.FileNotFound,
1428 .NOTDIR => return error.FileNotFound,
1429 .ILSEQ => return error.BadPathName,
1430 else => |err| return unexpectedErrno(err),
1431 }
1432}
1433
1434pub const KQueueError = error{
1435 /// The per-process limit on the number of open file descriptors has been reached.
1436 ProcessFdQuotaExceeded,
1437
1438 /// The system-wide limit on the total number of open files has been reached.
1439 SystemFdQuotaExceeded,
1440} || UnexpectedError;
1441
1442pub fn kqueue() KQueueError!i32 {
1443 const rc = system.kqueue();
1444 switch (errno(rc)) {
1445 .SUCCESS => return @intCast(rc),
1446 .MFILE => return error.ProcessFdQuotaExceeded,
1447 .NFILE => return error.SystemFdQuotaExceeded,
1448 else => |err| return unexpectedErrno(err),
1449 }
1450}
1451
1452pub const KEventError = error{
1453 /// The process does not have permission to register a filter.
1454 AccessDenied,
1455
1456 /// The event could not be found to be modified or deleted.
1457 EventNotFound,
1458
1459 /// No memory was available to register the event.
1460 SystemResources,
1461
1462 /// The specified process to attach to does not exist.
1463 ProcessNotFound,
1464
1465 /// changelist or eventlist had too many items on it.
1466 /// TODO remove this possibility
1467 Overflow,
1468};
1469
1470pub fn kevent(
1471 kq: i32,
1472 changelist: []const Kevent,
1473 eventlist: []Kevent,
1474 timeout: ?*const timespec,
1475) KEventError!usize {
1476 while (true) {
1477 const rc = system.kevent(
1478 kq,
1479 changelist.ptr,
1480 cast(c_int, changelist.len) orelse return error.Overflow,
1481 eventlist.ptr,
1482 cast(c_int, eventlist.len) orelse return error.Overflow,
1483 timeout,
1484 );
1485 switch (errno(rc)) {
1486 .SUCCESS => return @intCast(rc),
1487 .ACCES => return error.AccessDenied,
1488 .FAULT => unreachable,
1489 .BADF => unreachable, // Always a race condition.
1490 .INTR => continue,
1491 .INVAL => unreachable,
1492 .NOENT => return error.EventNotFound,
1493 .NOMEM => return error.SystemResources,
1494 .SRCH => return error.ProcessNotFound,
1495 else => unreachable,
1496 }
1497 }
1498}
1499
1500pub const INotifyInitError = error{795pub const INotifyInitError = error{
1501 ProcessFdQuotaExceeded,796 ProcessFdQuotaExceeded,
1502 SystemFdQuotaExceeded,797 SystemFdQuotaExceeded,
...@@ -1751,18 +1046,6 @@ pub fn mprotect(memory: []align(page_size_min) u8, protection: u32) MProtectErro...@@ -1751,18 +1046,6 @@ pub fn mprotect(memory: []align(page_size_min) u8, protection: u32) MProtectErro
1751 }1046 }
1752}1047}
17531048
1754pub const ForkError = error{SystemResources} || UnexpectedError;
1755
1756pub fn fork() ForkError!pid_t {
1757 const rc = system.fork();
1758 switch (errno(rc)) {
1759 .SUCCESS => return @intCast(rc),
1760 .AGAIN => return error.SystemResources,
1761 .NOMEM => return error.SystemResources,
1762 else => |err| return unexpectedErrno(err),
1763 }
1764}
1765
1766pub const MMapError = error{1049pub const MMapError = error{
1767 /// The underlying filesystem of the specified file does not support memory mapping.1050 /// The underlying filesystem of the specified file does not support memory mapping.
1768 MemoryMappingNotSupported,1051 MemoryMappingNotSupported,
...@@ -1991,31 +1274,6 @@ pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize {...@@ -1991,31 +1274,6 @@ pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize {
1991 }1274 }
1992}1275}
19931276
1994/// Spurious wakeups are possible and no precision of timing is guaranteed.
1995pub fn nanosleep(seconds: u64, nanoseconds: u64) void {
1996 var req = timespec{
1997 .sec = cast(isize, seconds) orelse maxInt(isize),
1998 .nsec = cast(isize, nanoseconds) orelse maxInt(isize),
1999 };
2000 var rem: timespec = undefined;
2001 while (true) {
2002 switch (errno(system.nanosleep(&req, &rem))) {
2003 .FAULT => unreachable,
2004 .INVAL => {
2005 // Sometimes Darwin returns EINVAL for no reason.
2006 // We treat it as a spurious wakeup.
2007 return;
2008 },
2009 .INTR => {
2010 req = rem;
2011 continue;
2012 },
2013 // This prong handles success as well as unexpected errors.
2014 else => return,
2015 }
2016 }
2017}
2018
2019pub fn getSelfPhdrs() []std.elf.ElfN.Phdr {1277pub fn getSelfPhdrs() []std.elf.ElfN.Phdr {
2020 const getauxval = if (builtin.link_libc) std.c.getauxval else std.os.linux.getauxval;1278 const getauxval = if (builtin.link_libc) std.c.getauxval else std.os.linux.getauxval;
2021 assert(getauxval(std.elf.AT_PHENT) == @sizeOf(std.elf.ElfN.Phdr));1279 assert(getauxval(std.elf.AT_PHENT) == @sizeOf(std.elf.ElfN.Phdr));
...@@ -2287,289 +1545,6 @@ pub fn uname() utsname {...@@ -2287,289 +1545,6 @@ pub fn uname() utsname {
2287 }1545 }
2288}1546}
22891547
2290pub const SendError = error{
2291 /// (For UNIX domain sockets, which are identified by pathname) Write permission is denied
2292 /// on the destination socket file, or search permission is denied for one of the
2293 /// directories the path prefix. (See path_resolution(7).)
2294 /// (For UDP sockets) An attempt was made to send to a network/broadcast address as though
2295 /// it was a unicast address.
2296 AccessDenied,
2297
2298 /// The socket is marked nonblocking and the requested operation would block, and
2299 /// there is no global event loop configured.
2300 /// It's also possible to get this error under the following condition:
2301 /// (Internet domain datagram sockets) The socket referred to by sockfd had not previously
2302 /// been bound to an address and, upon attempting to bind it to an ephemeral port, it was
2303 /// determined that all port numbers in the ephemeral port range are currently in use. See
2304 /// the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7).
2305 WouldBlock,
2306
2307 /// Another Fast Open is already in progress.
2308 FastOpenAlreadyInProgress,
2309
2310 /// Connection reset by peer.
2311 ConnectionResetByPeer,
2312
2313 /// The socket type requires that message be sent atomically, and the size of the message
2314 /// to be sent made this impossible. The message is not transmitted.
2315 MessageOversize,
2316
2317 /// The output queue for a network interface was full. This generally indicates that the
2318 /// interface has stopped sending, but may be caused by transient congestion. (Normally,
2319 /// this does not occur in Linux. Packets are just silently dropped when a device queue
2320 /// overflows.)
2321 /// This is also caused when there is not enough kernel memory available.
2322 SystemResources,
2323
2324 /// The local end has been shut down on a connection oriented socket. In this case, the
2325 /// process will also receive a SIGPIPE unless MSG.NOSIGNAL is set.
2326 BrokenPipe,
2327
2328 FileDescriptorNotASocket,
2329
2330 /// Network is unreachable.
2331 NetworkUnreachable,
2332
2333 /// The local network interface used to reach the destination is down.
2334 NetworkDown,
2335
2336 /// The destination address is not listening.
2337 ConnectionRefused,
2338} || UnexpectedError;
2339
2340pub const SendMsgError = SendError || error{
2341 /// The passed address didn't have the correct address family in its sa_family field.
2342 AddressFamilyUnsupported,
2343
2344 /// Returned when socket is AF.UNIX and the given path has a symlink loop.
2345 SymLinkLoop,
2346
2347 /// Returned when socket is AF.UNIX and the given path length exceeds `max_path_bytes` bytes.
2348 NameTooLong,
2349
2350 /// Returned when socket is AF.UNIX and the given path does not point to an existing file.
2351 FileNotFound,
2352 NotDir,
2353
2354 /// The socket is not connected (connection-oriented sockets only).
2355 SocketUnconnected,
2356 AddressUnavailable,
2357};
2358
2359pub fn sendmsg(
2360 /// The file descriptor of the sending socket.
2361 sockfd: socket_t,
2362 /// Message header and iovecs
2363 msg: *const msghdr_const,
2364 flags: u32,
2365) SendMsgError!usize {
2366 while (true) {
2367 const rc = system.sendmsg(sockfd, msg, flags);
2368 if (native_os == .windows) {
2369 if (rc == windows.ws2_32.SOCKET_ERROR) {
2370 switch (windows.ws2_32.WSAGetLastError()) {
2371 .EACCES => return error.AccessDenied,
2372 .EADDRNOTAVAIL => return error.AddressUnavailable,
2373 .ECONNRESET => return error.ConnectionResetByPeer,
2374 .EMSGSIZE => return error.MessageOversize,
2375 .ENOBUFS => return error.SystemResources,
2376 .ENOTSOCK => return error.FileDescriptorNotASocket,
2377 .EAFNOSUPPORT => return error.AddressFamilyUnsupported,
2378 .EDESTADDRREQ => unreachable, // A destination address is required.
2379 .EFAULT => unreachable, // The lpBuffers, lpTo, lpOverlapped, lpNumberOfBytesSent, or lpCompletionRoutine parameters are not part of the user address space, or the lpTo parameter is too small.
2380 .EHOSTUNREACH => return error.NetworkUnreachable,
2381 // TODO: EINPROGRESS, EINTR
2382 .EINVAL => unreachable,
2383 .ENETDOWN => return error.NetworkDown,
2384 .ENETRESET => return error.ConnectionResetByPeer,
2385 .ENETUNREACH => return error.NetworkUnreachable,
2386 .ENOTCONN => return error.SocketUnconnected,
2387 .ESHUTDOWN => unreachable, // The socket has been shut down; it is not possible to WSASendTo on a socket after shutdown has been invoked with how set to SD_SEND or SD_BOTH.
2388 .EWOULDBLOCK => return error.WouldBlock,
2389 .NOTINITIALISED => unreachable, // A successful WSAStartup call must occur before using this function.
2390 else => |err| return windows.unexpectedWSAError(err),
2391 }
2392 } else {
2393 return @intCast(rc);
2394 }
2395 } else {
2396 switch (errno(rc)) {
2397 .SUCCESS => return @intCast(rc),
2398
2399 .ACCES => return error.AccessDenied,
2400 .AGAIN => return error.WouldBlock,
2401 .ALREADY => return error.FastOpenAlreadyInProgress,
2402 .BADF => unreachable, // always a race condition
2403 .CONNRESET => return error.ConnectionResetByPeer,
2404 .DESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set.
2405 .FAULT => unreachable, // An invalid user space address was specified for an argument.
2406 .INTR => continue,
2407 .INVAL => unreachable, // Invalid argument passed.
2408 .ISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified
2409 .MSGSIZE => return error.MessageOversize,
2410 .NOBUFS => return error.SystemResources,
2411 .NOMEM => return error.SystemResources,
2412 .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
2413 .OPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type.
2414 .PIPE => return error.BrokenPipe,
2415 .AFNOSUPPORT => return error.AddressFamilyUnsupported,
2416 .LOOP => return error.SymLinkLoop,
2417 .NAMETOOLONG => return error.NameTooLong,
2418 .NOENT => return error.FileNotFound,
2419 .NOTDIR => return error.NotDir,
2420 .HOSTUNREACH => return error.NetworkUnreachable,
2421 .NETUNREACH => return error.NetworkUnreachable,
2422 .NOTCONN => return error.SocketUnconnected,
2423 .NETDOWN => return error.NetworkDown,
2424 else => |err| return unexpectedErrno(err),
2425 }
2426 }
2427 }
2428}
2429
2430pub const SendToError = SendMsgError || error{
2431 /// The destination address is not reachable by the bound address.
2432 UnreachableAddress,
2433 /// The destination address is not listening.
2434 ConnectionRefused,
2435};
2436
2437/// Transmit a message to another socket.
2438///
2439/// The `sendto` call may be used only when the socket is in a connected state (so that the intended
2440/// recipient is known). The following call
2441///
2442/// send(sockfd, buf, len, flags);
2443///
2444/// is equivalent to
2445///
2446/// sendto(sockfd, buf, len, flags, NULL, 0);
2447///
2448/// If sendto() is used on a connection-mode (`SOCK.STREAM`, `SOCK.SEQPACKET`) socket, the arguments
2449/// `dest_addr` and `addrlen` are asserted to be `null` and `0` respectively, and asserted
2450/// that the socket was actually connected.
2451/// Otherwise, the address of the target is given by `dest_addr` with `addrlen` specifying its size.
2452///
2453/// If the message is too long to pass atomically through the underlying protocol,
2454/// `SendError.MessageOversize` is returned, and the message is not transmitted.
2455///
2456/// There is no indication of failure to deliver.
2457///
2458/// When the message does not fit into the send buffer of the socket, `sendto` normally blocks,
2459/// unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail
2460/// with `SendError.WouldBlock`. The `select` call may be used to determine when it is
2461/// possible to send more data.
2462pub fn sendto(
2463 /// The file descriptor of the sending socket.
2464 sockfd: socket_t,
2465 /// Message to send.
2466 buf: []const u8,
2467 flags: u32,
2468 dest_addr: ?*const sockaddr,
2469 addrlen: socklen_t,
2470) SendToError!usize {
2471 if (native_os == .windows) {
2472 switch (windows.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen)) {
2473 windows.ws2_32.SOCKET_ERROR => switch (windows.ws2_32.WSAGetLastError()) {
2474 .EACCES => return error.AccessDenied,
2475 .EADDRNOTAVAIL => return error.AddressUnavailable,
2476 .ECONNRESET => return error.ConnectionResetByPeer,
2477 .EMSGSIZE => return error.MessageOversize,
2478 .ENOBUFS => return error.SystemResources,
2479 .ENOTSOCK => return error.FileDescriptorNotASocket,
2480 .EAFNOSUPPORT => return error.AddressFamilyUnsupported,
2481 .EDESTADDRREQ => unreachable, // A destination address is required.
2482 .EFAULT => unreachable, // The lpBuffers, lpTo, lpOverlapped, lpNumberOfBytesSent, or lpCompletionRoutine parameters are not part of the user address space, or the lpTo parameter is too small.
2483 .EHOSTUNREACH => return error.NetworkUnreachable,
2484 // TODO: EINPROGRESS, EINTR
2485 .EINVAL => unreachable,
2486 .ENETDOWN => return error.NetworkDown,
2487 .ENETRESET => return error.ConnectionResetByPeer,
2488 .ENETUNREACH => return error.NetworkUnreachable,
2489 .ENOTCONN => return error.SocketUnconnected,
2490 .ESHUTDOWN => unreachable, // The socket has been shut down; it is not possible to WSASendTo on a socket after shutdown has been invoked with how set to SD_SEND or SD_BOTH.
2491 .EWOULDBLOCK => return error.WouldBlock,
2492 .NOTINITIALISED => unreachable, // A successful WSAStartup call must occur before using this function.
2493 else => |err| return windows.unexpectedWSAError(err),
2494 },
2495 else => |rc| return @intCast(rc),
2496 }
2497 }
2498 while (true) {
2499 const rc = system.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen);
2500 switch (errno(rc)) {
2501 .SUCCESS => return @intCast(rc),
2502
2503 .ACCES => return error.AccessDenied,
2504 .AGAIN => return error.WouldBlock,
2505 .ALREADY => return error.FastOpenAlreadyInProgress,
2506 .BADF => unreachable, // always a race condition
2507 .CONNREFUSED => return error.ConnectionRefused,
2508 .CONNRESET => return error.ConnectionResetByPeer,
2509 .DESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set.
2510 .FAULT => unreachable, // An invalid user space address was specified for an argument.
2511 .INTR => continue,
2512 .INVAL => return error.UnreachableAddress,
2513 .ISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified
2514 .MSGSIZE => return error.MessageOversize,
2515 .NOBUFS => return error.SystemResources,
2516 .NOMEM => return error.SystemResources,
2517 .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
2518 .OPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type.
2519 .PIPE => return error.BrokenPipe,
2520 .AFNOSUPPORT => return error.AddressFamilyUnsupported,
2521 .LOOP => return error.SymLinkLoop,
2522 .NAMETOOLONG => return error.NameTooLong,
2523 .NOENT => return error.FileNotFound,
2524 .NOTDIR => return error.NotDir,
2525 .HOSTUNREACH => return error.NetworkUnreachable,
2526 .NETUNREACH => return error.NetworkUnreachable,
2527 .NOTCONN => return error.SocketUnconnected,
2528 .NETDOWN => return error.NetworkDown,
2529 else => |err| return unexpectedErrno(err),
2530 }
2531 }
2532}
2533
2534/// Transmit a message to another socket.
2535///
2536/// The `send` call may be used only when the socket is in a connected state (so that the intended
2537/// recipient is known). The only difference between `send` and `write` is the presence of
2538/// flags. With a zero flags argument, `send` is equivalent to `write`. Also, the following
2539/// call
2540///
2541/// send(sockfd, buf, len, flags);
2542///
2543/// is equivalent to
2544///
2545/// sendto(sockfd, buf, len, flags, NULL, 0);
2546///
2547/// There is no indication of failure to deliver.
2548///
2549/// When the message does not fit into the send buffer of the socket, `send` normally blocks,
2550/// unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail
2551/// with `SendError.WouldBlock`. The `select` call may be used to determine when it is
2552/// possible to send more data.
2553pub fn send(
2554 /// The file descriptor of the sending socket.
2555 sockfd: socket_t,
2556 buf: []const u8,
2557 flags: u32,
2558) SendError!usize {
2559 return sendto(sockfd, buf, flags, null, 0) catch |err| switch (err) {
2560 error.AddressFamilyUnsupported => unreachable,
2561 error.SymLinkLoop => unreachable,
2562 error.NameTooLong => unreachable,
2563 error.FileNotFound => unreachable,
2564 error.NotDir => unreachable,
2565 error.NetworkUnreachable => unreachable,
2566 error.AddressUnavailable => unreachable,
2567 error.SocketUnconnected => unreachable,
2568 error.UnreachableAddress => unreachable,
2569 else => |e| return e,
2570 };
2571}
2572
2573pub const PollError = error{1548pub const PollError = error{
2574 /// The network subsystem has failed.1549 /// The network subsystem has failed.
2575 NetworkDown,1550 NetworkDown,
...@@ -2633,139 +1608,6 @@ pub fn ppoll(fds: []pollfd, timeout: ?*const timespec, mask: ?*const sigset_t) P...@@ -2633,139 +1608,6 @@ pub fn ppoll(fds: []pollfd, timeout: ?*const timespec, mask: ?*const sigset_t) P
2633 }1608 }
2634}1609}
26351610
2636pub const RecvFromError = error{
2637 /// The socket is marked nonblocking and the requested operation would block, and
2638 /// there is no global event loop configured.
2639 WouldBlock,
2640
2641 /// A remote host refused to allow the network connection, typically because it is not
2642 /// running the requested service.
2643 ConnectionRefused,
2644
2645 /// Could not allocate kernel memory.
2646 SystemResources,
2647
2648 ConnectionResetByPeer,
2649 Timeout,
2650
2651 /// The socket has not been bound.
2652 SocketNotBound,
2653
2654 /// The UDP message was too big for the buffer and part of it has been discarded
2655 MessageOversize,
2656
2657 /// The network subsystem has failed.
2658 NetworkDown,
2659
2660 /// The socket is not connected (connection-oriented sockets only).
2661 SocketUnconnected,
2662
2663 /// The other end closed the socket unexpectedly or a read is executed on a shut down socket
2664 BrokenPipe,
2665} || UnexpectedError;
2666
2667pub fn recv(sock: socket_t, buf: []u8, flags: u32) RecvFromError!usize {
2668 return recvfrom(sock, buf, flags, null, null);
2669}
2670
2671/// If `sockfd` is opened in non blocking mode, the function will
2672/// return error.WouldBlock when EAGAIN is received.
2673pub fn recvfrom(
2674 sockfd: socket_t,
2675 buf: []u8,
2676 flags: u32,
2677 src_addr: ?*sockaddr,
2678 addrlen: ?*socklen_t,
2679) RecvFromError!usize {
2680 while (true) {
2681 const rc = system.recvfrom(sockfd, buf.ptr, buf.len, flags, src_addr, addrlen);
2682 if (native_os == .windows) {
2683 if (rc == windows.ws2_32.SOCKET_ERROR) {
2684 switch (windows.ws2_32.WSAGetLastError()) {
2685 .NOTINITIALISED => unreachable,
2686 .ECONNRESET => return error.ConnectionResetByPeer,
2687 .EINVAL => return error.SocketNotBound,
2688 .EMSGSIZE => return error.MessageOversize,
2689 .ENETDOWN => return error.NetworkDown,
2690 .ENOTCONN => return error.SocketUnconnected,
2691 .EWOULDBLOCK => return error.WouldBlock,
2692 .ETIMEDOUT => return error.Timeout,
2693 // TODO: handle more errors
2694 else => |err| return windows.unexpectedWSAError(err),
2695 }
2696 } else {
2697 return @intCast(rc);
2698 }
2699 } else {
2700 switch (errno(rc)) {
2701 .SUCCESS => return @intCast(rc),
2702 .BADF => unreachable, // always a race condition
2703 .FAULT => unreachable,
2704 .INVAL => unreachable,
2705 .NOTCONN => return error.SocketUnconnected,
2706 .NOTSOCK => unreachable,
2707 .INTR => continue,
2708 .AGAIN => return error.WouldBlock,
2709 .NOMEM => return error.SystemResources,
2710 .CONNREFUSED => return error.ConnectionRefused,
2711 .CONNRESET => return error.ConnectionResetByPeer,
2712 .TIMEDOUT => return error.Timeout,
2713 .PIPE => return error.BrokenPipe,
2714 else => |err| return unexpectedErrno(err),
2715 }
2716 }
2717 }
2718}
2719
2720pub const RecvMsgError = RecvFromError || error{
2721 /// Reception of SCM_RIGHTS fds via ancillary data in msg.control would
2722 /// exceed some system limit (generally this is retryable by trying to
2723 /// receive fewer fds or closing some existing fds)
2724 SystemFdQuotaExceeded,
2725
2726 /// Reception of SCM_RIGHTS fds via ancillary data in msg.control would
2727 /// exceed some process limit (generally this is retryable by trying to
2728 /// receive fewer fds, closing some existing fds, or changing the ulimit)
2729 ProcessFdQuotaExceeded,
2730};
2731
2732/// If `sockfd` is opened in non blocking mode, the function will
2733/// return error.WouldBlock when EAGAIN is received.
2734pub fn recvmsg(
2735 /// The file descriptor of the sending socket.
2736 sockfd: socket_t,
2737 /// Message header and iovecs
2738 msg: *msghdr,
2739 flags: u32,
2740) RecvMsgError!usize {
2741 if (@TypeOf(system.recvmsg) == void)
2742 @compileError("recvmsg() not supported on this OS");
2743 while (true) {
2744 const rc = system.recvmsg(sockfd, msg, flags);
2745 switch (errno(rc)) {
2746 .SUCCESS => return @intCast(rc),
2747 .AGAIN => return error.WouldBlock,
2748 .BADF => unreachable, // always a race condition
2749 .NFILE => return error.SystemFdQuotaExceeded,
2750 .MFILE => return error.ProcessFdQuotaExceeded,
2751 .INTR => continue,
2752 .FAULT => unreachable, // An invalid user space address was specified for an argument.
2753 .INVAL => unreachable, // Invalid argument passed.
2754 .ISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified
2755 .NOBUFS => return error.SystemResources,
2756 .NOMEM => return error.SystemResources,
2757 .NOTCONN => return error.SocketUnconnected,
2758 .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
2759 .MSGSIZE => return error.MessageOversize,
2760 .PIPE => return error.BrokenPipe,
2761 .OPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type.
2762 .CONNRESET => return error.ConnectionResetByPeer,
2763 .NETDOWN => return error.NetworkDown,
2764 else => |err| return unexpectedErrno(err),
2765 }
2766 }
2767}
2768
2769pub const SetSockOptError = error{1611pub const SetSockOptError = error{
2770 /// The socket is already connected, and a specified option cannot be set while the socket is connected.1612 /// The socket is already connected, and a specified option cannot be set while the socket is connected.
2771 AlreadyConnected,1613 AlreadyConnected,
...@@ -2957,20 +1799,6 @@ pub fn tcsetpgrp(handle: fd_t, pgrp: pid_t) TermioSetPgrpError!void {...@@ -2957,20 +1799,6 @@ pub fn tcsetpgrp(handle: fd_t, pgrp: pid_t) TermioSetPgrpError!void {
2957 }1799 }
2958}1800}
29591801
2960pub const SetSidError = error{
2961 /// The calling process is already a process group leader, or the process group ID of a process other than the calling process matches the process ID of the calling process.
2962 PermissionDenied,
2963} || UnexpectedError;
2964
2965pub fn setsid() SetSidError!pid_t {
2966 const rc = system.setsid();
2967 switch (errno(rc)) {
2968 .SUCCESS => return @intCast(rc),
2969 .PERM => return error.PermissionDenied,
2970 else => |err| return unexpectedErrno(err),
2971 }
2972}
2973
2974pub fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) !fd_t {1802pub fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) !fd_t {
2975 const rc = system.signalfd(fd, mask, flags);1803 const rc = system.signalfd(fd, mask, flags);
2976 switch (errno(rc)) {1804 switch (errno(rc)) {
lib/std/posix/test.zig+25-16
...@@ -35,14 +35,14 @@ test "check WASI CWD" {...@@ -35,14 +35,14 @@ test "check WASI CWD" {
3535
36test "getuid" {36test "getuid" {
37 if (native_os == .windows or native_os == .wasi) return error.SkipZigTest;37 if (native_os == .windows or native_os == .wasi) return error.SkipZigTest;
38 _ = posix.getuid();38 _ = posix.system.getuid();
39 _ = posix.geteuid();39 _ = posix.system.geteuid();
40}40}
4141
42test "getgid" {42test "getgid" {
43 if (native_os == .windows or native_os == .wasi) return error.SkipZigTest;43 if (native_os == .windows or native_os == .wasi) return error.SkipZigTest;
44 _ = posix.getgid();44 _ = posix.system.getgid();
45 _ = posix.getegid();45 _ = posix.system.getegid();
46}46}
4747
48test "sigaltstack" {48test "sigaltstack" {
...@@ -121,13 +121,18 @@ test "pipe" {...@@ -121,13 +121,18 @@ test "pipe" {
121 if (native_os == .windows or native_os == .wasi)121 if (native_os == .windows or native_os == .wasi)
122 return error.SkipZigTest;122 return error.SkipZigTest;
123123
124 const io = testing.io;
125
124 const fds = try std.Io.Threaded.pipe2(.{});126 const fds = try std.Io.Threaded.pipe2(.{});
125 try expect((try posix.write(fds[1], "hello")) == 5);127 const out: Io.File = .{ .handle = fds[0] };
128 const in: Io.File = .{ .handle = fds[1] };
129 try in.writeStreamingAll(io, "hello");
126 var buf: [16]u8 = undefined;130 var buf: [16]u8 = undefined;
127 try expect((try posix.read(fds[0], buf[0..])) == 5);131 try expect((try out.readStreaming(io, &.{&buf})) == 5);
132
128 try expectEqualSlices(u8, buf[0..5], "hello");133 try expectEqualSlices(u8, buf[0..5], "hello");
129 posix.close(fds[1]);134 out.close(io);
130 posix.close(fds[0]);135 in.close(io);
131}136}
132137
133test "memfd_create" {138test "memfd_create" {
...@@ -458,8 +463,12 @@ test "rename smoke test" {...@@ -458,8 +463,12 @@ test "rename smoke test" {
458 // Create some file using `open`.463 // Create some file using `open`.
459 const file_path = try Dir.path.join(gpa, &.{ base_path, "some_file" });464 const file_path = try Dir.path.join(gpa, &.{ base_path, "some_file" });
460 defer gpa.free(file_path);465 defer gpa.free(file_path);
461 const fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode);466 const file = try Io.Dir.cwd().createFile(io, file_path, .{
462 posix.close(fd);467 .read = true,
468 .exclusive = true,
469 .permissions = .fromMode(mode),
470 });
471 file.close(io);
463472
464 // Rename the file473 // Rename the file
465 const new_file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_file" });474 const new_file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_file" });
...@@ -471,15 +480,15 @@ test "rename smoke test" {...@@ -471,15 +480,15 @@ test "rename smoke test" {
471 // Try opening renamed file480 // Try opening renamed file
472 const file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_file" });481 const file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_file" });
473 defer gpa.free(file_path);482 defer gpa.free(file_path);
474 const fd = try posix.open(file_path, .{ .ACCMODE = .RDWR }, mode);483 const file = try Io.Dir.cwd().openFile(io, file_path, .{ .mode = .read_write });
475 posix.close(fd);484 file.close(io);
476 }485 }
477486
478 {487 {
479 // Try opening original file - should fail with error.FileNotFound488 // Try opening original file - should fail with error.FileNotFound
480 const file_path = try Dir.path.join(gpa, &.{ base_path, "some_file" });489 const file_path = try Dir.path.join(gpa, &.{ base_path, "some_file" });
481 defer gpa.free(file_path);490 defer gpa.free(file_path);
482 try expectError(error.FileNotFound, posix.open(file_path, .{ .ACCMODE = .RDWR }, mode));491 try expectError(error.FileNotFound, Io.Dir.cwd().openFile(io, file_path, .{ .mode = .read_write }));
483 }492 }
484493
485 {494 {
...@@ -498,15 +507,15 @@ test "rename smoke test" {...@@ -498,15 +507,15 @@ test "rename smoke test" {
498 // Try opening renamed directory507 // Try opening renamed directory
499 const file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_dir" });508 const file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_dir" });
500 defer gpa.free(file_path);509 defer gpa.free(file_path);
501 const fd = try posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode);510 const dir = try Io.Dir.cwd().openDir(io, file_path, .{});
502 posix.close(fd);511 dir.close(io);
503 }512 }
504513
505 {514 {
506 // Try opening original directory - should fail with error.FileNotFound515 // Try opening original directory - should fail with error.FileNotFound
507 const file_path = try Dir.path.join(gpa, &.{ base_path, "some_dir" });516 const file_path = try Dir.path.join(gpa, &.{ base_path, "some_dir" });
508 defer gpa.free(file_path);517 defer gpa.free(file_path);
509 try expectError(error.FileNotFound, posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode));518 try expectError(error.FileNotFound, Io.Dir.cwd().openDir(io, file_path, .{}));
510 }519 }
511}520}
512521
lib/std/testing.zig+2-2
...@@ -618,7 +618,7 @@ pub const TmpDir = struct {...@@ -618,7 +618,7 @@ pub const TmpDir = struct {
618 sub_path: [sub_path_len]u8,618 sub_path: [sub_path_len]u8,
619619
620 const random_bytes_count = 12;620 const random_bytes_count = 12;
621 const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count);621 const sub_path_len = std.base64.url_safe.Encoder.calcSize(random_bytes_count);
622622
623 pub fn cleanup(self: *TmpDir) void {623 pub fn cleanup(self: *TmpDir) void {
624 self.dir.close(io);624 self.dir.close(io);
...@@ -633,7 +633,7 @@ pub fn tmpDir(opts: Io.Dir.OpenOptions) TmpDir {...@@ -633,7 +633,7 @@ pub fn tmpDir(opts: Io.Dir.OpenOptions) TmpDir {
633 var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;633 var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;
634 io.random(&random_bytes);634 io.random(&random_bytes);
635 var sub_path: [TmpDir.sub_path_len]u8 = undefined;635 var sub_path: [TmpDir.sub_path_len]u8 = undefined;
636 _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);636 _ = std.base64.url_safe.Encoder.encode(&sub_path, &random_bytes);
637637
638 const cwd = Io.Dir.cwd();638 const cwd = Io.Dir.cwd();
639 var cache_dir = cwd.createDirPathOpen(io, ".zig-cache", .{}) catch639 var cache_dir = cwd.createDirPathOpen(io, ".zig-cache", .{}) catch
test/cases/function_pointers.zig deleted-30
...@@ -1,30 +0,0 @@
1const std = @import("std");
2
3const PrintFn = *const fn () void;
4
5pub fn main() void {
6 var printFn: PrintFn = stopSayingThat;
7 var i: u32 = 0;
8 while (i < 4) : (i += 1) printFn();
9
10 printFn = moveEveryZig;
11 printFn();
12}
13
14fn stopSayingThat() void {
15 _ = std.posix.write(1, "Hello, my name is Inigo Montoya; you killed my father, prepare to die.\n") catch {};
16}
17
18fn moveEveryZig() void {
19 _ = std.posix.write(1, "All your codebase are belong to us\n") catch {};
20}
21
22// run
23// target=x86_64-macos
24//
25// Hello, my name is Inigo Montoya; you killed my father, prepare to die.
26// Hello, my name is Inigo Montoya; you killed my father, prepare to die.
27// Hello, my name is Inigo Montoya; you killed my father, prepare to die.
28// Hello, my name is Inigo Montoya; you killed my father, prepare to die.
29// All your codebase are belong to us
30//
test/cases/print_u32s.zig deleted-28
...@@ -1,28 +0,0 @@
1const std = @import("std");
2
3pub fn main() void {
4 printNumberHex(0x00000000);
5 printNumberHex(0xaaaaaaaa);
6 printNumberHex(0xdeadbeef);
7 printNumberHex(0x31415926);
8}
9
10fn printNumberHex(x: u32) void {
11 const digit_chars = "0123456789abcdef";
12 var i: u5 = 28;
13 while (true) : (i -= 4) {
14 const digit = (x >> i) & 0xf;
15 _ = std.posix.write(1, &.{digit_chars[digit]}) catch {};
16 if (i == 0) break;
17 }
18 _ = std.posix.write(1, "\n") catch {};
19}
20
21// run
22// target=x86_64-macos
23//
24// 00000000
25// aaaaaaaa
26// deadbeef
27// 31415926
28//