Files
Zigmulator/src/io_interface.zig
T

1498 lines
51 KiB
Zig

const std = @import("std");
const Node = @import("node.zig");
const Io = std.Io;
const net = Io.net;
const Dir = Io.Dir;
const File = Io.File;
const Group = Io.Group;
const AnyFuture = Io.AnyFuture;
const ConcurrentError = Io.ConcurrentError;
const CancelProtection = Io.CancelProtection;
const Cancelable = Io.Cancelable;
const Timeout = Io.Timeout;
const Operation = Io.Operation;
const Batch = Io.Batch;
const Terminal = Io.Terminal;
const LockedStderr = Io.LockedStderr;
const Clock = Io.Clock;
const Timestamp = Io.Timestamp;
const Duration = Io.Duration;
const RandomSecureError = Io.RandomSecureError;
const Queue = Io.Queue;
const Allocator = std.mem.Allocator;
const TaskID = Node.TaskID;
pub fn buildIOInterfaceForNode(node: *Node) Io {
return .{
.userdata = node,
.vtable = &.{
.crashHandler = crashHandler,
.async = async,
.concurrent = concurrent,
.await = await,
.cancel = cancel,
.groupAsync = groupAsync,
.groupConcurrent = groupConcurrent,
.groupAwait = groupAwait,
.groupCancel = groupCancel,
.recancel = recancel,
.swapCancelProtection = swapCancelProtection,
.checkCancel = checkCancel,
.futexWait = futexWait,
.futexWaitUncancelable = futexWaitUncancelable,
.futexWake = futexWake,
.operate = operate,
.batchAwaitAsync = batchAwaitAsync,
.batchAwaitConcurrent = batchAwaitConcurrent,
.batchCancel = batchCancel,
.dirCreateDir = dirCreateDir,
.dirCreateDirPath = dirCreateDirPath,
.dirCreateDirPathOpen = dirCreateDirPathOpen,
.dirOpenDir = dirOpenDir,
.dirStat = dirStat,
.dirStatFile = dirStatFile,
.dirAccess = dirAccess,
.dirCreateFile = dirCreateFile,
.dirCreateFileAtomic = dirCreateFileAtomic,
.dirOpenFile = dirOpenFile,
.dirClose = dirClose,
.dirRead = dirRead,
.dirRealPath = dirRealPath,
.dirRealPathFile = dirRealPathFile,
.dirDeleteFile = dirDeleteFile,
.dirDeleteDir = dirDeleteDir,
.dirRename = dirRename,
.dirRenamePreserve = dirRenamePreserve,
.dirSymLink = dirSymLink,
.dirReadLink = dirReadLink,
.dirSetOwner = dirSetOwner,
.dirSetFileOwner = dirSetFileOwner,
.dirSetPermissions = dirSetPermissions,
.dirSetFilePermissions = dirSetFilePermissions,
.dirSetTimestamps = dirSetTimestamps,
.dirHardLink = dirHardLink,
.fileStat = fileStat,
.fileLength = fileLength,
.fileClose = fileClose,
.fileWritePositional = fileWritePositional,
.fileWriteFileStreaming = fileWriteFileStreaming,
.fileWriteFilePositional = fileWriteFilePositional,
.fileReadPositional = fileReadPositional,
.fileSeekBy = fileSeekBy,
.fileSeekTo = fileSeekTo,
.fileSync = fileSync,
.fileIsTty = fileIsTty,
.fileEnableAnsiEscapeCodes = fileEnableAnsiEscapeCodes,
.fileSupportsAnsiEscapeCodes = fileSupportsAnsiEscapeCodes,
.fileSetLength = fileSetLength,
.fileSetOwner = fileSetOwner,
.fileSetPermissions = fileSetPermissions,
.fileSetTimestamps = fileSetTimestamps,
.fileLock = fileLock,
.fileTryLock = fileTryLock,
.fileUnlock = fileUnlock,
.fileDowngradeLock = fileDowngradeLock,
.fileRealPath = fileRealPath,
.fileHardLink = fileHardLink,
.fileMemoryMapCreate = fileMemoryMapCreate,
.fileMemoryMapDestroy = fileMemoryMapDestroy,
.fileMemoryMapSetLength = fileMemoryMapSetLength,
.fileMemoryMapRead = fileMemoryMapRead,
.fileMemoryMapWrite = fileMemoryMapWrite,
.processExecutableOpen = processExecutableOpen,
.processExecutablePath = processExecutablePath,
.lockStderr = lockStderr,
.tryLockStderr = tryLockStderr,
.unlockStderr = unlockStderr,
.processCurrentPath = processCurrentPath,
.processSetCurrentDir = processSetCurrentDir,
.processSetCurrentPath = processSetCurrentPath,
.processReplace = processReplace,
.processReplacePath = processReplacePath,
.processSpawn = processSpawn,
.processSpawnPath = processSpawnPath,
.childWait = childWait,
.childKill = childKill,
.progressParentFile = progressParentFile,
.random = random,
.randomSecure = randomSecure,
.now = now,
.clockResolution = clockResolution,
.sleep = sleep,
.netListenIp = netListenIp,
.netAccept = netAccept,
.netBindIp = netBindIp,
.netConnectIp = netConnectIp,
.netListenUnix = netListenUnix,
.netConnectUnix = netConnectUnix,
.netSocketCreatePair = netSocketCreatePair,
.netSend = netSend,
.netRead = netRead,
.netWrite = netWrite,
.netWriteFile = netWriteFile,
.netClose = netClose,
.netShutdown = netShutdown,
.netInterfaceNameResolve = netInterfaceNameResolve,
.netInterfaceName = netInterfaceName,
.netLookup = netLookup,
},
};
}
fn crashHandler(userdata: ?*anyopaque) void {
_ = userdata;
@panic("Not implemented yet");
}
fn async(
userdata: ?*anyopaque,
result: []u8,
result_alignment: std.mem.Alignment,
context: []const u8,
context_alignment: std.mem.Alignment,
start: *const fn (context: *const anyopaque, result: *anyopaque) void,
) ?*AnyFuture {
_ = userdata;
_ = result;
_ = result_alignment;
_ = context;
_ = context_alignment;
_ = start;
@panic("Not implemented yet");
}
fn concurrent(
userdata: ?*anyopaque,
result_len: usize,
result_alignment: std.mem.Alignment,
context: []const u8,
context_alignment: std.mem.Alignment,
start: *const fn (context: *const anyopaque, result: *anyopaque) void,
) ConcurrentError!*AnyFuture {
_ = userdata;
_ = result_len;
_ = result_alignment;
_ = context;
_ = context_alignment;
_ = start;
@panic("Not implemented yet");
}
fn await(
userdata: ?*anyopaque,
future: *AnyFuture,
result: []u8,
result_alignment: std.mem.Alignment,
) void {
_ = userdata;
_ = future;
_ = result;
_ = result_alignment;
@panic("Not implemented yet");
}
fn cancel(
userdata: ?*anyopaque,
future: *AnyFuture,
result: []u8,
result_alignment: std.mem.Alignment,
) void {
_ = userdata;
_ = future;
_ = result;
_ = result_alignment;
@panic("Not implemented yet");
}
const TaskContext = struct {
context_alignment: std.mem.Alignment,
alloc_len: usize,
fn ptr(self: *TaskContext) [*]u8 {
const base: [*]u8 = @ptrCast(self);
const offset = self.context_alignment.forward(@intFromPtr(base) + @sizeOf(TaskContext)) - @intFromPtr(base);
return base + offset;
}
fn alloc(self: *TaskContext) []align(@alignOf(TaskContext)) u8 {
const base: [*]align(@alignOf(TaskContext)) u8 = @ptrCast(self);
return base[0..self.alloc_len];
}
};
const TaskGroup = struct {
gpa: Allocator,
ids: std.ArrayList(TaskID) = .empty,
contexts: std.ArrayList(*TaskContext) = .empty,
fn init(gpa: Allocator) TaskGroup {
return .{
.gpa = gpa,
};
}
fn add(self: *TaskGroup, id: TaskID, context: *TaskContext) !void {
try self.ids.append(self.gpa, id);
errdefer _ = self.ids.pop();
try self.contexts.append(self.gpa, context);
}
fn remove(self: *TaskGroup, id: TaskID) void {
var found: ?usize = null;
for (self.ids.items, 0..) |current_id, i| {
if (current_id == id) {
found = i;
break;
}
}
if (found) |i| {
const context = self.contexts.items[i];
self.gpa.free(context.alloc());
_ = self.ids.orderedRemove(i);
_ = self.contexts.orderedRemove(i);
}
}
fn deinit(self: *TaskGroup) void {
for (self.contexts.items) |context| {
self.gpa.free(context.alloc());
}
self.contexts.deinit(self.gpa);
self.ids.deinit(self.gpa);
}
};
fn getOrCreatePendingTaskGroup(gpa: Allocator, type_erased: *Group) !*TaskGroup {
const token = type_erased.token.load(.acquire);
if (token) |ptr| return @ptrCast(@alignCast(ptr));
const group = try gpa.create(TaskGroup);
errdefer gpa.destroy(group);
group.* = .init(gpa);
type_erased.token.store(@ptrCast(group), .release);
type_erased.state = 0;
return group;
}
fn groupAsync(
userdata: ?*anyopaque,
type_erased: *Group,
context: []const u8,
context_alignment: std.mem.Alignment,
start: *const fn (context: *const anyopaque) void,
) void {
const node: *Node = @ptrCast(@alignCast(userdata.?));
const group = getOrCreatePendingTaskGroup(node.gpa, type_erased) catch {
start(context.ptr);
return;
};
const max_context_misalignment = context_alignment.toByteUnits() -| @alignOf(TaskContext);
const worst_case_context_offset = context_alignment.forward(@sizeOf(TaskContext) + max_context_misalignment);
const alloc_len = worst_case_context_offset + context.len;
const alloc = node.gpa.alignedAlloc(u8, .of(TaskContext), alloc_len) catch {
start(context.ptr);
return;
};
const task_context: *TaskContext = @ptrCast(@alignCast(alloc.ptr));
task_context.* = .{
.context_alignment = context_alignment,
.alloc_len = alloc_len,
};
@memcpy(task_context.ptr()[0..context.len], context);
const id = node.spawn(start, task_context.ptr()) catch {
node.gpa.free(task_context.alloc());
start(context.ptr);
return;
};
group.add(id, task_context) catch {
node.despawn(id);
node.gpa.free(task_context.alloc());
start(context.ptr);
return;
};
}
fn groupConcurrent(
userdata: ?*anyopaque,
type_erased: *Group,
context: []const u8,
context_alignment: std.mem.Alignment,
start: *const fn (context: *const anyopaque) void,
) ConcurrentError!void {
_ = userdata;
_ = type_erased;
_ = context;
_ = context_alignment;
_ = start;
@panic("Not implemented yet\n");
}
fn cancelGroupTasks(node: *Node, group: *TaskGroup) void {
for (group.ids.items) |id| {
node.cancel(id);
}
}
fn drainGroup(node: *Node, group: *TaskGroup) Cancelable!void {
while (group.ids.items.len > 0) {
const id = node.wait(group.ids.items) catch |err| switch (err) {
error.Canceled => return error.Canceled,
else => @panic("TODO"),
};
group.remove(id);
node.despawn(id);
}
}
fn drainGroupUncancelable(node: *Node, group: *TaskGroup) void {
while (group.ids.items.len > 0) {
const id = node.wait(group.ids.items) catch |err| switch (err) {
error.Canceled => continue,
else => @panic("TODO"),
};
group.remove(id);
node.despawn(id);
}
}
fn groupAwait(userdata: ?*anyopaque, type_erased: *Group, initial_token: *anyopaque) Cancelable!void {
const node: *Node = @ptrCast(@alignCast(userdata.?));
const group: *TaskGroup = @ptrCast(@alignCast(initial_token));
defer {
group.deinit();
node.gpa.destroy(group);
type_erased.token.store(null, .release);
type_erased.state = 0;
}
drainGroup(node, group) catch |err| switch (err) {
error.Canceled => {
cancelGroupTasks(node, group);
drainGroupUncancelable(node, group);
return error.Canceled;
},
};
}
fn groupCancel(userdata: ?*anyopaque, type_erased: *Group, initial_token: *anyopaque) void {
const node: *Node = @ptrCast(@alignCast(userdata.?));
const group: *TaskGroup = @ptrCast(@alignCast(initial_token));
defer {
group.deinit();
node.gpa.destroy(group);
type_erased.token.store(null, .release);
type_erased.state = 0;
}
cancelGroupTasks(node, group);
drainGroupUncancelable(node, group);
}
fn recancel(userdata: ?*anyopaque) void {
const node: *Node = @ptrCast(@alignCast(userdata.?));
node.recancel();
}
fn swapCancelProtection(userdata: ?*anyopaque, new: CancelProtection) CancelProtection {
_ = userdata;
_ = new;
@panic("TODO");
}
fn checkCancel(userdata: ?*anyopaque) Cancelable!void {
const node: *Node = @ptrCast(@alignCast(userdata.?));
try node.checkCancel();
}
fn futexWait(userdata: ?*anyopaque, ptr: *const u32, expected: u32, timeout: Timeout) Cancelable!void {
_ = timeout;
const node: *Node = @ptrCast(@alignCast(userdata.?));
try node.futexWait(ptr, expected);
}
fn futexWaitUncancelable(userdata: ?*anyopaque, ptr: *const u32, expected: u32) void {
const node: *Node = @ptrCast(@alignCast(userdata.?));
node.futexWaitUncancelable(ptr, expected);
}
fn futexWake(userdata: ?*anyopaque, ptr: *const u32, max_waiters: u32) void {
const node: *Node = @ptrCast(@alignCast(userdata.?));
node.futexWake(ptr, max_waiters);
}
fn operate(userdata: ?*anyopaque, operation: Operation) Cancelable!Operation.Result {
const node: *Node = @ptrCast(@alignCast(userdata.?));
return switch (operation) {
.file_write_streaming => |op| .{
.file_write_streaming = fileWriteStreaming(node, op),
},
else => @panic("Not implemented yet"),
};
}
fn fileWriteStreaming(node: *Node, op: Operation.FileWriteStreaming) Operation.FileWriteStreaming.Result {
var copied: usize = 0;
copied += writeFile(node, op.file, null, op.header, op.data[0 .. op.data.len - 1]) catch |err| return err;
const pattern = op.data[op.data.len - 1];
for (0..op.splat) |_| {
copied += writeFile(node, op.file, null, &.{}, &.{pattern}) catch |err| return err;
}
return copied;
}
fn writeFile(node: *Node, file: File, offset: ?usize, header: []const u8, data: []const []const u8) Operation.FileWriteStreaming.Error!usize {
return node.writeFile(file.handle, offset, header, data) catch |err| switch (err) {
error.InvalidHandle => error.NotOpenForWriting,
error.OutOfMemory => error.SystemResources,
error.Canceled => @panic("TODO"),
};
}
fn batchAwaitAsync(userdata: ?*anyopaque, batch: *Batch) Cancelable!void {
_ = userdata;
_ = batch;
@panic("Not implemented yet");
}
fn batchAwaitConcurrent(userdata: ?*anyopaque, batch: *Batch, timeout: Timeout) Batch.AwaitConcurrentError!void {
_ = userdata;
_ = batch;
_ = timeout;
@panic("Not implemented yet");
}
fn batchCancel(userdata: ?*anyopaque, batch: *Batch) void {
_ = userdata;
_ = batch;
@panic("Not implemented yet");
}
fn dirCreateDir(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions) Dir.CreateDirError!void {
_ = permissions; // TODO: Use permissions
const node: *Node = @ptrCast(@alignCast(userdata.?));
const parent = if (dir.handle == Dir.cwd().handle) null else dir.handle;
node.createDir(parent, sub_path) catch |e| {
return switch (e) {
Node.CreateDirError.InvalidHandle => unreachable,
Node.CreateDirError.ExistsAlready => Dir.CreateDirError.PathAlreadyExists,
Node.CreateDirError.EmptyPath => Dir.CreateDirError.BadPathName,
Node.CreateDirError.NoRootParent => Dir.CreateDirError.FileNotFound,
Node.CreateDirError.TooManyComponents => Dir.CreateDirError.NameTooLong,
Node.CreateDirError.ResolutionLimit => Dir.CreateDirError.NameTooLong,
Node.CreateDirError.ComponentNotDirectory => Dir.CreateDirError.NotDir,
Node.CreateDirError.ComponentNotFound => Dir.CreateDirError.FileNotFound,
Node.CreateDirError.OutOfMemory => Dir.CreateDirError.SystemResources,
Node.CreateDirError.Canceled => return error.Canceled,
};
};
}
fn dirCreateDirPath(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions) Dir.CreateDirPathError!Dir.CreatePathStatus {
_ = userdata;
_ = dir;
_ = sub_path;
_ = permissions;
@panic("Not implemented yet");
}
fn dirCreateDirPathOpen(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions, options: Dir.OpenOptions) Dir.CreateDirPathOpenError!Dir {
_ = userdata;
_ = dir;
_ = sub_path;
_ = permissions;
_ = options;
@panic("Not implemented yet");
}
fn dirOpenDir(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, options: Dir.OpenOptions) Dir.OpenError!Dir {
_ = options; // TODO: Use options
const node: *Node = @ptrCast(@alignCast(userdata.?));
const parent = if (dir.handle == Dir.cwd().handle) null else dir.handle;
const handle = node.openDir(parent, sub_path) catch |e| {
return switch (e) {
Node.OpenDirError.InvalidHandle => unreachable,
Node.OpenDirError.DescriptorLimit => Dir.OpenError.ProcessFdQuotaExceeded,
Node.OpenDirError.IsDirectory => unreachable,
Node.OpenDirError.NotDirectory => Dir.OpenError.NotDir,
Node.OpenDirError.EmptyPath => Dir.OpenError.BadPathName,
Node.OpenDirError.NoRootParent => Dir.OpenError.FileNotFound,
Node.OpenDirError.TooManyComponents => Dir.OpenError.NameTooLong,
Node.OpenDirError.ResolutionLimit => Dir.OpenError.NameTooLong,
Node.OpenDirError.ComponentNotDirectory => Dir.OpenError.NotDir,
Node.OpenDirError.ComponentNotFound => Dir.OpenError.FileNotFound,
Node.OpenDirError.Canceled => return error.Canceled,
};
};
return .{ .handle = handle };
}
fn dirStat(userdata: ?*anyopaque, dir: Dir) Dir.StatError!Dir.Stat {
_ = userdata;
_ = dir;
@panic("Not implemented yet");
}
fn dirStatFile(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, options: Dir.StatFileOptions) Dir.StatFileError!File.Stat {
_ = userdata;
_ = dir;
_ = sub_path;
_ = options;
@panic("Not implemented yet");
}
fn dirAccess(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, options: Dir.AccessOptions) Dir.AccessError!void {
_ = userdata;
_ = dir;
_ = sub_path;
_ = options;
@panic("Not implemented yet");
}
fn dirCreateFile(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, flags: Dir.CreateFileOptions) File.OpenError!File {
_ = flags; // TODO: Use flags
const node: *Node = @ptrCast(@alignCast(userdata.?));
const parent = if (dir.handle == Dir.cwd().handle) null else dir.handle;
node.createFile(parent, sub_path) catch |e| {
return switch (e) {
Node.CreateFileError.InvalidHandle => unreachable,
Node.CreateFileError.ExistsAlready => File.OpenError.PathAlreadyExists,
Node.CreateFileError.EmptyPath => File.OpenError.BadPathName,
Node.CreateFileError.NoRootParent => File.OpenError.FileNotFound,
Node.CreateFileError.TooManyComponents => File.OpenError.NameTooLong,
Node.CreateFileError.ResolutionLimit => File.OpenError.NameTooLong,
Node.CreateFileError.ComponentNotDirectory => File.OpenError.NotDir,
Node.CreateFileError.ComponentNotFound => File.OpenError.FileNotFound,
Node.CreateFileError.OutOfMemory => File.OpenError.SystemResources,
Node.CreateFileError.Canceled => return error.Canceled,
};
};
const handle = node.openFile(parent, sub_path) catch |e| {
return switch (e) {
Node.OpenFileError.InvalidHandle => unreachable,
Node.OpenFileError.DescriptorLimit => File.OpenError.ProcessFdQuotaExceeded,
Node.OpenFileError.IsDirectory => File.OpenError.IsDir,
Node.OpenFileError.NotDirectory => File.OpenError.NotDir,
Node.OpenFileError.EmptyPath => File.OpenError.BadPathName,
Node.OpenFileError.NoRootParent => File.OpenError.FileNotFound,
Node.OpenFileError.TooManyComponents => File.OpenError.NameTooLong,
Node.OpenFileError.ResolutionLimit => File.OpenError.NameTooLong,
Node.OpenFileError.ComponentNotDirectory => File.OpenError.NotDir,
Node.OpenFileError.ComponentNotFound => File.OpenError.FileNotFound,
Node.OpenFileError.Canceled => return error.Canceled,
};
};
return .{
.handle = handle,
.flags = .{ .nonblocking = false },
};
}
fn dirCreateFileAtomic(userdata: ?*anyopaque, dir: Dir, dest_path: []const u8, options: Dir.CreateFileAtomicOptions) Dir.CreateFileAtomicError!File.Atomic {
_ = userdata;
_ = dir;
_ = dest_path;
_ = options;
@panic("Not implemented yet");
}
fn dirOpenFile(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, flags: Dir.OpenFileOptions) File.OpenError!File {
_ = flags; // TODO: Use flags
const node: *Node = @ptrCast(@alignCast(userdata.?));
const parent = if (dir.handle == Dir.cwd().handle) null else dir.handle;
const handle = node.openFile(parent, sub_path) catch |e| {
return switch (e) {
Node.OpenFileError.InvalidHandle => unreachable,
Node.OpenFileError.DescriptorLimit => File.OpenError.ProcessFdQuotaExceeded,
Node.OpenFileError.IsDirectory => File.OpenError.IsDir,
Node.OpenFileError.NotDirectory => File.OpenError.NotDir,
Node.OpenFileError.EmptyPath => File.OpenError.BadPathName,
Node.OpenFileError.NoRootParent => File.OpenError.FileNotFound,
Node.OpenFileError.TooManyComponents => File.OpenError.NameTooLong,
Node.OpenFileError.ResolutionLimit => File.OpenError.NameTooLong,
Node.OpenFileError.ComponentNotDirectory => File.OpenError.NotDir,
Node.OpenFileError.ComponentNotFound => File.OpenError.FileNotFound,
Node.OpenFileError.Canceled => return error.Canceled,
};
};
return .{
.handle = handle,
.flags = .{ .nonblocking = false },
};
}
fn dirClose(userdata: ?*anyopaque, dirs: []const Dir) void {
const node: *Node = @ptrCast(@alignCast(userdata.?));
for (dirs) |dir| {
if (dir.handle == Dir.cwd().handle)
continue;
node.closeDir(dir.handle) catch {};
}
}
fn dirRead(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize {
const node: *Node = @ptrCast(@alignCast(userdata.?));
if (dr.state == .finished)
return 0;
if (dr.state == .reset) {
node.resetDir(dr.dir.handle) catch |e| switch (e) {
error.InvalidHandle => return Dir.Reader.Error.AccessDenied,
error.Canceled => return error.Canceled,
};
dr.state = .reading;
}
var count: usize = 0;
while (count < buffer.len) {
const entry = node.readDir(dr.dir.handle) catch |e| switch (e) {
error.InvalidHandle => return Dir.Reader.Error.AccessDenied,
error.Canceled => return error.Canceled,
error.NoMoreItems => {
dr.state = .finished;
return count;
},
};
if (std.mem.eql(u8, entry.name, ".") or std.mem.eql(u8, entry.name, ".."))
continue;
buffer[count] = .{
.name = entry.name,
.kind = if (entry.is_dir) .directory else .file,
.inode = 0,
};
count += 1;
}
return count;
}
fn dirRealPath(userdata: ?*anyopaque, dir: Dir, out_buffer: []u8) Dir.RealPathError!usize {
_ = userdata;
_ = dir;
_ = out_buffer;
@panic("Not implemented yet");
}
fn dirRealPathFile(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, out_buffer: []u8) Dir.RealPathFileError!usize {
_ = userdata;
_ = dir;
_ = sub_path;
_ = out_buffer;
@panic("Not implemented yet");
}
fn dirDeleteFile(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.DeleteFileError!void {
const node: *Node = @ptrCast(@alignCast(userdata.?));
const parent = if (dir.handle == Dir.cwd().handle) null else dir.handle;
node.deleteFile(parent, sub_path) catch |e| {
return switch (e) {
Node.DeleteFileError.InvalidHandle => unreachable,
Node.DeleteFileError.EmptyPath => Dir.DeleteFileError.BadPathName,
Node.DeleteFileError.NoRootParent => Dir.DeleteFileError.FileNotFound,
Node.DeleteFileError.TooManyComponents => Dir.DeleteFileError.NameTooLong,
Node.DeleteFileError.ResolutionLimit => Dir.DeleteFileError.NameTooLong,
Node.DeleteFileError.ComponentNotDirectory => Dir.DeleteFileError.NotDir,
Node.DeleteFileError.ComponentNotFound => Dir.DeleteFileError.FileNotFound,
Node.DeleteFileError.NotFound => Dir.DeleteFileError.FileNotFound,
Node.DeleteFileError.IsDirectory => Dir.DeleteFileError.IsDir,
Node.DeleteFileError.Canceled => return error.Canceled,
};
};
}
fn dirDeleteDir(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.DeleteDirError!void {
const node: *Node = @ptrCast(@alignCast(userdata.?));
const parent = if (dir.handle == Dir.cwd().handle) null else dir.handle;
node.deleteDir(parent, sub_path) catch |e| {
return switch (e) {
Node.DeleteDirError.InvalidHandle => unreachable,
Node.DeleteDirError.EmptyPath => Dir.DeleteDirError.BadPathName,
Node.DeleteDirError.NoRootParent => Dir.DeleteDirError.FileNotFound,
Node.DeleteDirError.TooManyComponents => Dir.DeleteDirError.NameTooLong,
Node.DeleteDirError.ResolutionLimit => Dir.DeleteDirError.NameTooLong,
Node.DeleteDirError.ComponentNotDirectory => Dir.DeleteDirError.NotDir,
Node.DeleteDirError.ComponentNotFound => Dir.DeleteDirError.FileNotFound,
Node.DeleteDirError.NotFound => Dir.DeleteDirError.FileNotFound,
Node.DeleteDirError.NotDirectory => Dir.DeleteDirError.NotDir,
Node.DeleteDirError.DirNotEmpty => Dir.DeleteDirError.DirNotEmpty,
Node.DeleteDirError.Canceled => return error.Canceled,
};
};
}
fn dirRename(userdata: ?*anyopaque, old_dir: Dir, old_sub_path: []const u8, new_dir: Dir, new_sub_path: []const u8) Dir.RenameError!void {
const node: *Node = @ptrCast(@alignCast(userdata.?));
const old_parent = if (old_dir.handle == Dir.cwd().handle) null else old_dir.handle;
const new_parent = if (new_dir.handle == Dir.cwd().handle) null else new_dir.handle;
node.rename(old_parent, old_sub_path, new_parent, new_sub_path) catch |e| {
return switch (e) {
Node.RenameError.InvalidHandle => unreachable,
Node.RenameError.EmptyPath => Dir.RenameError.BadPathName,
Node.RenameError.NoRootParent => Dir.RenameError.FileNotFound,
Node.RenameError.TooManyComponents => Dir.RenameError.NameTooLong,
Node.RenameError.ResolutionLimit => Dir.RenameError.NameTooLong,
Node.RenameError.ComponentNotDirectory => Dir.RenameError.NotDir,
Node.RenameError.ComponentNotFound => Dir.RenameError.FileNotFound,
Node.RenameError.NotFound => Dir.RenameError.FileNotFound,
Node.RenameError.IsDirectory => Dir.RenameError.IsDir,
Node.RenameError.NotDirectory => Dir.RenameError.NotDir,
Node.RenameError.DirNotEmpty => Dir.RenameError.DirNotEmpty,
Node.RenameError.OutOfMemory => Dir.RenameError.SystemResources,
Node.RenameError.AccessDenied => Dir.RenameError.AccessDenied,
Node.RenameError.Canceled => return error.Canceled,
};
};
}
fn dirRenamePreserve(userdata: ?*anyopaque, old_dir: Dir, old_sub_path: []const u8, new_dir: Dir, new_sub_path: []const u8) Dir.RenamePreserveError!void {
_ = userdata;
_ = old_dir;
_ = old_sub_path;
_ = new_dir;
_ = new_sub_path;
@panic("Not implemented yet");
}
fn dirSymLink(userdata: ?*anyopaque, dir: Dir, target_path: []const u8, sym_link_path: []const u8, flags: Dir.SymLinkFlags) Dir.SymLinkError!void {
_ = userdata;
_ = dir;
_ = target_path;
_ = sym_link_path;
_ = flags;
@panic("Not implemented yet");
}
fn dirReadLink(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
_ = userdata;
_ = dir;
_ = sub_path;
_ = buffer;
@panic("Not implemented yet");
}
fn dirSetOwner(userdata: ?*anyopaque, dir: Dir, owner: ?File.Uid, group: ?File.Gid) Dir.SetOwnerError!void {
_ = userdata;
_ = dir;
_ = owner;
_ = group;
@panic("Not implemented yet");
}
fn dirSetFileOwner(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, owner: ?File.Uid, group: ?File.Gid, options: Dir.SetFileOwnerOptions) Dir.SetFileOwnerError!void {
_ = userdata;
_ = dir;
_ = sub_path;
_ = owner;
_ = group;
_ = options;
@panic("Not implemented yet");
}
fn dirSetPermissions(userdata: ?*anyopaque, dir: Dir, permissions: Dir.Permissions) Dir.SetPermissionsError!void {
_ = userdata;
_ = dir;
_ = permissions;
@panic("Not implemented yet");
}
fn dirSetFilePermissions(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: File.Permissions, options: Dir.SetFilePermissionsOptions) Dir.SetFilePermissionsError!void {
_ = userdata;
_ = dir;
_ = sub_path;
_ = permissions;
_ = options;
@panic("Not implemented yet");
}
fn dirSetTimestamps(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, options: Dir.SetTimestampsOptions) Dir.SetTimestampsError!void {
_ = userdata;
_ = dir;
_ = sub_path;
_ = options;
@panic("Not implemented yet");
}
fn dirHardLink(userdata: ?*anyopaque, old_dir: Dir, old_sub_path: []const u8, new_dir: Dir, new_sub_path: []const u8, options: Dir.HardLinkOptions) Dir.HardLinkError!void {
_ = userdata;
_ = old_dir;
_ = old_sub_path;
_ = new_dir;
_ = new_sub_path;
_ = options;
@panic("Not implemented yet");
}
fn fileStat(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
_ = userdata;
_ = file;
@panic("Not implemented yet");
}
fn fileLength(userdata: ?*anyopaque, file: File) File.LengthError!u64 {
const node: *Node = @ptrCast(@alignCast(userdata.?));
return node.fileSize(file.handle) catch |e| switch (e) {
error.InvalidHandle => File.LengthError.Streaming,
error.Canceled => return error.Canceled,
};
}
fn fileClose(userdata: ?*anyopaque, files: []const File) void {
const node: *Node = @ptrCast(@alignCast(userdata.?));
for (files) |file| {
node.closeFile(file.handle) catch {};
}
}
fn fileWritePositional(userdata: ?*anyopaque, file: File, header: []const u8, data: []const []const u8, splat: usize, offset: u64) File.WritePositionalError!usize {
const node: *Node = @ptrCast(@alignCast(userdata.?));
var cursor = std.math.cast(usize, offset) orelse return File.WritePositionalError.FileTooBig;
var copied: usize = 0;
if (header.len != 0) {
const written = node.writeFile(file.handle, cursor, header, &.{}) catch |err| switch (err) {
error.InvalidHandle => return File.WritePositionalError.NotOpenForWriting,
error.OutOfMemory => return File.WritePositionalError.SystemResources,
error.Canceled => return error.Canceled,
};
cursor = std.math.add(usize, cursor, written) catch return File.WritePositionalError.FileTooBig;
copied += written;
}
if (data.len != 0) {
for (data[0 .. data.len - 1]) |bytes| {
if (bytes.len == 0)
continue;
const written = node.writeFile(file.handle, cursor, bytes, &.{}) catch |err| switch (err) {
error.InvalidHandle => return File.WritePositionalError.NotOpenForWriting,
error.OutOfMemory => return File.WritePositionalError.SystemResources,
error.Canceled => return error.Canceled,
};
cursor = std.math.add(usize, cursor, written) catch return File.WritePositionalError.FileTooBig;
copied += written;
}
const pattern = data[data.len - 1];
for (0..splat) |_| {
if (pattern.len == 0)
break;
const written = node.writeFile(file.handle, cursor, pattern, &.{}) catch |err| switch (err) {
error.InvalidHandle => return File.WritePositionalError.NotOpenForWriting,
error.OutOfMemory => return File.WritePositionalError.SystemResources,
error.Canceled => return error.Canceled,
};
cursor = std.math.add(usize, cursor, written) catch return File.WritePositionalError.FileTooBig;
copied += written;
}
}
return copied;
}
fn fileWriteFileStreaming(userdata: ?*anyopaque, file: File, header: []const u8, file_reader: *Io.File.Reader, limit: Io.Limit) File.Writer.WriteFileError!usize {
_ = userdata;
_ = file;
_ = header;
_ = file_reader;
_ = limit;
@panic("Not implemented yet");
}
fn fileWriteFilePositional(userdata: ?*anyopaque, file: File, header: []const u8, file_reader: *Io.File.Reader, limit: Io.Limit, offset: u64) File.WriteFilePositionalError!usize {
_ = userdata;
_ = file;
_ = header;
_ = file_reader;
_ = limit;
_ = offset;
@panic("Not implemented yet");
}
fn fileReadPositional(userdata: ?*anyopaque, file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize {
const node: *Node = @ptrCast(@alignCast(userdata.?));
var copied: usize = 0;
for (data) |buffer| {
const bytes_read = node.readFile(file.handle, @intCast(offset + copied), buffer) catch |e| {
return switch (e) {
error.InvalidHandle => File.ReadPositionalError.NotOpenForReading,
error.Canceled => return error.Canceled,
};
};
copied += bytes_read;
if (bytes_read < buffer.len)
break;
}
return copied;
}
fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!void {
const node: *Node = @ptrCast(@alignCast(userdata.?));
node.seekFileBy(file.handle, offset) catch |err| switch (err) {
error.InvalidHandle => return File.SeekError.AccessDenied,
error.NegativeOffset, error.Overflow => return File.SeekError.Unseekable,
error.Canceled => return error.Canceled,
};
}
fn fileSeekTo(userdata: ?*anyopaque, file: File, offset: u64) File.SeekError!void {
const node: *Node = @ptrCast(@alignCast(userdata.?));
const offset_usize = std.math.cast(usize, offset) orelse return File.SeekError.Unseekable;
node.seekFileTo(file.handle, offset_usize) catch |err| switch (err) {
error.InvalidHandle => return File.SeekError.AccessDenied,
error.NegativeOffset, error.Overflow => return File.SeekError.Unseekable,
error.Canceled => return error.Canceled,
};
}
fn fileSync(userdata: ?*anyopaque, file: File) File.SyncError!void {
const node: *Node = @ptrCast(@alignCast(userdata.?));
node.syncFile(file.handle) catch |err| switch (err) {
error.InvalidHandle => return error.AccessDenied,
error.Canceled => return error.Canceled,
};
}
fn fileIsTty(userdata: ?*anyopaque, file: File) Cancelable!bool {
_ = userdata;
_ = file;
@panic("Not implemented yet");
}
fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiEscapeCodesError!void {
_ = userdata;
_ = file;
@panic("Not implemented yet");
}
fn fileSupportsAnsiEscapeCodes(userdata: ?*anyopaque, file: File) Cancelable!bool {
_ = userdata;
_ = file;
@panic("Not implemented yet");
}
fn fileSetLength(userdata: ?*anyopaque, file: File, length: u64) File.SetLengthError!void {
_ = userdata;
_ = file;
_ = length;
@panic("Not implemented yet");
}
fn fileSetOwner(userdata: ?*anyopaque, file: File, owner: ?File.Uid, group: ?File.Gid) File.SetOwnerError!void {
_ = userdata;
_ = file;
_ = owner;
_ = group;
@panic("Not implemented yet");
}
fn fileSetPermissions(userdata: ?*anyopaque, file: File, permissions: File.Permissions) File.SetPermissionsError!void {
_ = userdata;
_ = file;
_ = permissions;
@panic("Not implemented yet");
}
fn fileSetTimestamps(userdata: ?*anyopaque, file: File, options: File.SetTimestampsOptions) File.SetTimestampsError!void {
_ = userdata;
_ = file;
_ = options;
@panic("Not implemented yet");
}
fn fileLock(userdata: ?*anyopaque, file: File, lock: File.Lock) File.LockError!void {
_ = userdata;
_ = file;
_ = lock;
@panic("Not implemented yet");
}
fn fileTryLock(userdata: ?*anyopaque, file: File, lock: File.Lock) File.LockError!bool {
_ = userdata;
_ = file;
_ = lock;
@panic("Not implemented yet");
}
fn fileUnlock(userdata: ?*anyopaque, file: File) void {
_ = userdata;
_ = file;
@panic("Not implemented yet");
}
fn fileDowngradeLock(userdata: ?*anyopaque, file: File) File.DowngradeLockError!void {
_ = userdata;
_ = file;
@panic("Not implemented yet");
}
fn fileRealPath(userdata: ?*anyopaque, file: File, out_buffer: []u8) File.RealPathError!usize {
_ = userdata;
_ = file;
_ = out_buffer;
@panic("Not implemented yet");
}
fn fileHardLink(userdata: ?*anyopaque, file: File, new_dir: Dir, new_sub_path: []const u8, options: File.HardLinkOptions) File.HardLinkError!void {
_ = userdata;
_ = file;
_ = new_dir;
_ = new_sub_path;
_ = options;
@panic("Not implemented yet");
}
fn fileMemoryMapCreate(userdata: ?*anyopaque, file: File, options: File.MemoryMap.CreateOptions) File.MemoryMap.CreateError!File.MemoryMap {
_ = userdata;
_ = file;
_ = options;
@panic("Not implemented yet");
}
fn fileMemoryMapDestroy(userdata: ?*anyopaque, mm: *File.MemoryMap) void {
_ = userdata;
_ = mm;
@panic("Not implemented yet");
}
fn fileMemoryMapSetLength(userdata: ?*anyopaque, mm: *File.MemoryMap, new_len: usize) File.MemoryMap.SetLengthError!void {
_ = userdata;
_ = mm;
_ = new_len;
@panic("Not implemented yet");
}
fn fileMemoryMapRead(userdata: ?*anyopaque, mm: *File.MemoryMap) File.ReadPositionalError!void {
_ = userdata;
_ = mm;
@panic("Not implemented yet");
}
fn fileMemoryMapWrite(userdata: ?*anyopaque, mm: *File.MemoryMap) File.WritePositionalError!void {
_ = userdata;
_ = mm;
@panic("Not implemented yet");
}
fn processExecutableOpen(userdata: ?*anyopaque, flags: Dir.OpenFileOptions) std.process.OpenExecutableError!File {
_ = userdata;
_ = flags;
@panic("Not implemented yet");
}
fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.ExecutablePathError!usize {
_ = userdata;
_ = out_buffer;
@panic("Not implemented yet");
}
fn lockStderr(userdata: ?*anyopaque, terminal_mode: ?Terminal.Mode) Cancelable!LockedStderr {
_ = userdata;
_ = terminal_mode;
@panic("Not implemented yet");
}
fn tryLockStderr(userdata: ?*anyopaque, terminal_mode: ?Terminal.Mode) Cancelable!?LockedStderr {
_ = userdata;
_ = terminal_mode;
@panic("Not implemented yet");
}
fn unlockStderr(userdata: ?*anyopaque) void {
_ = userdata;
@panic("Not implemented yet");
}
fn processCurrentPath(userdata: ?*anyopaque, buffer: []u8) std.process.CurrentPathError!usize {
_ = userdata;
_ = buffer;
@panic("Not implemented yet");
}
fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) std.process.SetCurrentDirError!void {
_ = userdata;
_ = dir;
@panic("Not implemented yet");
}
fn processSetCurrentPath(userdata: ?*anyopaque, dir_path: []const u8) std.process.SetCurrentPathError!void {
_ = userdata;
_ = dir_path;
@panic("Not implemented yet");
}
fn processReplace(userdata: ?*anyopaque, options: std.process.ReplaceOptions) std.process.ReplaceError {
_ = userdata;
_ = options;
@panic("Not implemented yet");
}
fn processReplacePath(userdata: ?*anyopaque, dir: Dir, options: std.process.ReplaceOptions) std.process.ReplaceError {
_ = userdata;
_ = dir;
_ = options;
@panic("Not implemented yet");
}
fn processSpawn(userdata: ?*anyopaque, options: std.process.SpawnOptions) std.process.SpawnError!std.process.Child {
_ = userdata;
_ = options;
@panic("Not implemented yet");
}
fn processSpawnPath(userdata: ?*anyopaque, dir: Dir, options: std.process.SpawnOptions) std.process.SpawnError!std.process.Child {
_ = userdata;
_ = dir;
_ = options;
@panic("Not implemented yet");
}
fn childWait(userdata: ?*anyopaque, child: *std.process.Child) std.process.Child.WaitError!std.process.Child.Term {
_ = userdata;
_ = child;
@panic("Not implemented yet");
}
fn childKill(userdata: ?*anyopaque, child: *std.process.Child) void {
_ = userdata;
_ = child;
@panic("Not implemented yet");
}
fn progressParentFile(userdata: ?*anyopaque) std.Progress.ParentFileError!File {
_ = userdata;
@panic("Not implemented yet");
}
fn now(userdata: ?*anyopaque, clock: Clock) Timestamp {
const node: *Node = @ptrCast(@alignCast(userdata.?));
// TODO: Return an appropriate time for each clock
_ = clock;
return .{ .nanoseconds = node.local_time * 1000 };
}
fn clockResolution(userdata: ?*anyopaque, clock: Clock) Clock.ResolutionError!Duration {
_ = userdata;
_ = clock;
@panic("Not implemented yet");
}
fn sleep(userdata: ?*anyopaque, timeout: Timeout) Cancelable!void {
const node: *Node = @ptrCast(@alignCast(userdata.?));
const duration = switch (timeout) {
.none => return,
.duration => |d| d.raw,
.deadline => |d| now(userdata, d.clock).durationTo(d.raw),
};
if (duration.nanoseconds <= 0)
return;
const delta_us = std.math.cast(u64, @divTrunc(duration.nanoseconds + std.time.ns_per_us - 1, std.time.ns_per_us)) orelse std.math.maxInt(u64);
if (delta_us == 0)
return;
try node.sleep(delta_us);
}
fn random(userdata: ?*anyopaque, buffer: []u8) void {
_ = userdata;
_ = buffer;
@panic("Not implemented yet");
}
fn randomSecure(userdata: ?*anyopaque, buffer: []u8) RandomSecureError!void {
_ = userdata;
_ = buffer;
@panic("Not implemented yet");
}
fn nodeFromUserdata(userdata: ?*anyopaque) *Node {
return @ptrCast(@alignCast(userdata.?));
}
fn ipAddressToNodeAddress(address: *const net.IpAddress) ?Node.Address {
return switch (address.*) {
.ip4 => |ip4| .{
.ipv4 = std.mem.readInt(u32, &ip4.bytes, .big),
.port = ip4.port,
},
.ip6 => null,
};
}
fn nodeAddressToIpAddress(address: Node.Address) net.IpAddress {
var bytes: [4]u8 = undefined;
std.mem.writeInt(u32, &bytes, address.ipv4, .big);
return .{ .ip4 = .{
.bytes = bytes,
.port = address.port,
} };
}
fn netListenIp(userdata: ?*anyopaque, address: *const net.IpAddress, options: net.IpAddress.ListenOptions) net.IpAddress.ListenError!net.Socket {
const node = nodeFromUserdata(userdata);
if (options.mode != .stream)
return error.SocketModeUnsupported;
if (options.protocol != .tcp)
return error.ProtocolUnsupportedBySystem;
const node_address = ipAddressToNodeAddress(address) orelse return error.AddressFamilyUnsupported;
const handle = node.listen(node_address) catch |err| switch (err) {
error.DescriptorLimit => return error.ProcessFdQuotaExceeded,
error.AddressNotAvailable => return error.AddressUnavailable,
error.AddressAlreadyUsed => return error.AddressInUse,
error.OutOfMemory => return error.SystemResources,
error.Canceled => return error.Canceled,
};
return .{
.handle = handle,
.address = nodeAddressToIpAddress(node_address),
};
}
fn netAccept(userdata: ?*anyopaque, listen_handle: net.Socket.Handle, options: net.Server.AcceptOptions) net.Server.AcceptError!net.Socket {
const node = nodeFromUserdata(userdata);
_ = options;
const handle = node.accept(listen_handle) catch |err| switch (err) {
error.DescriptorLimit => return error.ProcessFdQuotaExceeded,
error.InvalidHandle => return error.SocketNotListening,
error.AcceptQueueEmpty => return error.WouldBlock,
error.OutOfMemory => return error.SystemResources,
error.Canceled => return error.Canceled,
};
return .{
.handle = handle,
.address = .{ .ip4 = .unspecified(0) },
};
}
fn netBindIp(userdata: ?*anyopaque, address: *const net.IpAddress, options: net.IpAddress.BindOptions) net.IpAddress.BindError!net.Socket {
_ = userdata;
_ = address;
_ = options;
return error.OptionUnsupported;
}
fn netConnectIp(userdata: ?*anyopaque, address: *const net.IpAddress, options: net.IpAddress.ConnectOptions) net.IpAddress.ConnectError!net.Socket {
const node = nodeFromUserdata(userdata);
if (options.mode != .stream)
return error.SocketModeUnsupported;
if (options.protocol) |protocol| {
if (protocol != .tcp)
return error.ProtocolUnsupportedBySystem;
}
const node_address = ipAddressToNodeAddress(address) orelse return error.AddressFamilyUnsupported;
const handle = node.connect(node_address) catch |err| switch (err) {
error.DescriptorLimit => return error.ProcessFdQuotaExceeded,
error.UnavailableHost => return error.HostUnreachable,
error.PeerNotListeningOnAddress => return error.ConnectionRefused,
error.OutOfMemory => return error.SystemResources,
error.Canceled => return error.Canceled,
};
return .{
.handle = handle,
.address = nodeAddressToIpAddress(node_address),
};
}
fn netListenUnix(userdata: ?*anyopaque, address: *const net.UnixAddress, options: net.UnixAddress.ListenOptions) net.UnixAddress.ListenError!net.Socket.Handle {
_ = userdata;
_ = address;
_ = options;
@panic("Not implemented yet");
}
fn netConnectUnix(userdata: ?*anyopaque, address: *const net.UnixAddress) net.UnixAddress.ConnectError!net.Socket.Handle {
_ = userdata;
_ = address;
@panic("Not implemented yet");
}
fn netSocketCreatePair(userdata: ?*anyopaque, options: net.Socket.CreatePairOptions) net.Socket.CreatePairError![2]net.Socket {
_ = userdata;
_ = options;
@panic("Not implemented yet");
}
fn netSend(userdata: ?*anyopaque, handle: net.Socket.Handle, messages: []net.OutgoingMessage, flags: net.SendFlags) struct { ?net.Socket.SendError, usize } {
const node = nodeFromUserdata(userdata);
_ = flags;
for (messages, 0..) |*message, index| {
const source = message.data_ptr[0..message.data_len];
const written = node.writeSocket(handle, source) catch |err| switch (err) {
error.InvalidHandle => return .{ error.SocketUnconnected, index },
error.NotConnected => return .{ error.SocketUnconnected, index },
error.OutOfMemory => return .{ error.SystemResources, index },
error.Canceled => return .{ error.Canceled, index },
};
message.data_len = written;
}
return .{ null, messages.len };
}
/// Returns 0 on end of stream.
fn netRead(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usize {
const node = nodeFromUserdata(userdata);
var copied: usize = 0;
for (data) |buffer| {
if (buffer.len == 0)
continue;
const block = (copied == 0);
const n = node.readSocket(fd, buffer, block) catch |e| switch (e) {
error.InvalidHandle => return error.SocketUnconnected,
error.Canceled => return error.Canceled,
};
if (n == 0)
break;
copied += n;
if (n < buffer.len)
break;
}
return copied;
}
fn netWrite(userdata: ?*anyopaque, handle: net.Socket.Handle, header: []const u8, data: []const []const u8, splat: usize) net.Stream.Writer.Error!usize {
const node = nodeFromUserdata(userdata);
var copied: usize = 0;
if (header.len != 0)
copied += node.writeSocket(handle, header) catch |err| switch (err) {
error.InvalidHandle => return error.SocketUnconnected,
error.NotConnected => return error.SocketUnconnected,
error.OutOfMemory => return error.SystemResources,
error.Canceled => return error.Canceled,
};
if (data.len != 0) {
for (data[0 .. data.len - 1]) |bytes| {
if (bytes.len == 0)
continue;
copied += node.writeSocket(handle, bytes) catch |err| switch (err) {
error.InvalidHandle => return error.SocketUnconnected,
error.NotConnected => return error.SocketUnconnected,
error.OutOfMemory => return error.SystemResources,
error.Canceled => return error.Canceled,
};
}
const pattern = data[data.len - 1];
for (0..splat) |_| {
if (pattern.len == 0)
break;
copied += node.writeSocket(handle, pattern) catch |err| switch (err) {
error.InvalidHandle => return error.SocketUnconnected,
error.NotConnected => return error.SocketUnconnected,
error.OutOfMemory => return error.SystemResources,
error.Canceled => return error.Canceled,
};
}
}
return copied;
}
fn netWriteFile(userdata: ?*anyopaque, socket_handle: net.Socket.Handle, header: []const u8, file_reader: *Io.File.Reader, limit: Io.Limit) net.Stream.Writer.WriteFileError!usize {
_ = userdata;
_ = socket_handle;
_ = header;
_ = file_reader;
_ = limit;
@panic("Not implemented yet");
}
fn netClose(userdata: ?*anyopaque, handles: []const net.Socket.Handle) void {
const node = nodeFromUserdata(userdata);
for (handles) |handle| {
node.closeSocket(handle) catch {};
}
}
fn netShutdown(userdata: ?*anyopaque, handle: net.Socket.Handle, how: net.ShutdownHow) net.ShutdownError!void {
const node = nodeFromUserdata(userdata);
_ = how;
node.closeSocket(handle) catch |err| switch (err) {
error.InvalidHandle => return error.SocketUnconnected,
error.Canceled => return error.Canceled,
};
}
fn netInterfaceNameResolve(userdata: ?*anyopaque, name: *const net.Interface.Name) net.Interface.Name.ResolveError!net.Interface {
_ = userdata;
_ = name;
@panic("Not implemented yet");
}
fn netInterfaceName(userdata: ?*anyopaque, interface: net.Interface) net.Interface.NameError!net.Interface.Name {
_ = userdata;
_ = interface;
@panic("Not implemented yet");
}
fn netLookup(userdata: ?*anyopaque, host_name: net.HostName, resolved: *Queue(net.HostName.LookupResult), options: net.HostName.LookupOptions) net.HostName.LookupError!void {
_ = userdata;
_ = host_name;
_ = resolved;
_ = options;
@panic("Not implemented yet");
}