Merge branch 'std.net'

This commit is contained in:
Andrew Kelley 2019-10-30 21:30:16 -04:00
commit 61d5a0bf48
No known key found for this signature in database
GPG Key ID: 7C5F548F728501A9
30 changed files with 2869 additions and 885 deletions

View File

@ -72,11 +72,11 @@ pub const Buffer = struct {
self.list.deinit();
}
pub fn toSlice(self: *const Buffer) []u8 {
pub fn toSlice(self: Buffer) []u8 {
return self.list.toSlice()[0..self.len()];
}
pub fn toSliceConst(self: *const Buffer) []const u8 {
pub fn toSliceConst(self: Buffer) []const u8 {
return self.list.toSliceConst()[0..self.len()];
}
@ -91,11 +91,11 @@ pub const Buffer = struct {
self.list.items[self.len()] = 0;
}
pub fn isNull(self: *const Buffer) bool {
pub fn isNull(self: Buffer) bool {
return self.list.len == 0;
}
pub fn len(self: *const Buffer) usize {
pub fn len(self: Buffer) usize {
return self.list.len - 1;
}
@ -111,16 +111,16 @@ pub const Buffer = struct {
self.list.toSlice()[old_len] = byte;
}
pub fn eql(self: *const Buffer, m: []const u8) bool {
pub fn eql(self: Buffer, m: []const u8) bool {
return mem.eql(u8, self.toSliceConst(), m);
}
pub fn startsWith(self: *const Buffer, m: []const u8) bool {
pub fn startsWith(self: Buffer, m: []const u8) bool {
if (self.len() < m.len) return false;
return mem.eql(u8, self.list.items[0..m.len], m);
}
pub fn endsWith(self: *const Buffer, m: []const u8) bool {
pub fn endsWith(self: Buffer, m: []const u8) bool {
const l = self.len();
if (l < m.len) return false;
const start = l - m.len;
@ -133,7 +133,7 @@ pub const Buffer = struct {
}
/// For passing to C functions.
pub fn ptr(self: *const Buffer) [*]u8 {
pub fn ptr(self: Buffer) [*]u8 {
return self.list.items.ptr;
}
};

View File

@ -117,6 +117,26 @@ pub extern "c" fn getsockname(sockfd: fd_t, noalias addr: *sockaddr, noalias add
pub extern "c" fn connect(sockfd: fd_t, sock_addr: *const sockaddr, addrlen: socklen_t) c_int;
pub extern "c" fn accept4(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t, flags: c_uint) c_int;
pub extern "c" fn getsockopt(sockfd: fd_t, level: c_int, optname: c_int, optval: *c_void, optlen: *socklen_t) c_int;
pub extern "c" fn send(sockfd: fd_t, buf: *const c_void, len: usize, flags: u32) isize;
pub extern "c" fn sendto(
sockfd: fd_t,
buf: *const c_void,
len: usize,
flags: u32,
dest_addr: *const sockaddr,
addrlen: socklen_t,
) isize;
pub extern fn recv(sockfd: fd_t, arg1: ?*c_void, arg2: usize, arg3: c_int) isize;
pub extern fn recvfrom(
sockfd: fd_t,
noalias buf: *c_void,
len: usize,
flags: u32,
noalias src_addr: ?*sockaddr,
noalias addrlen: ?*socklen_t,
) isize;
pub extern "c" fn kill(pid: pid_t, sig: c_int) c_int;
pub extern "c" fn getdirentries(fd: fd_t, buf_ptr: [*]u8, nbytes: usize, basep: *i64) isize;
pub extern "c" fn setgid(ruid: c_uint, euid: c_uint) c_int;
@ -149,3 +169,34 @@ pub extern "c" fn kevent(
nevents: c_int,
timeout: ?*const timespec,
) c_int;
pub extern "c" fn getaddrinfo(
noalias node: [*]const u8,
noalias service: [*]const u8,
noalias hints: *const addrinfo,
noalias res: **addrinfo,
) c_int;
pub extern "c" fn freeaddrinfo(res: *addrinfo) void;
pub extern "c" fn getnameinfo(
noalias addr: *const sockaddr,
addrlen: socklen_t,
noalias host: [*]u8,
hostlen: socklen_t,
noalias serv: [*]u8,
servlen: socklen_t,
flags: u32,
) c_int;
pub extern "c" fn gai_strerror(errcode: c_int) [*]const u8;
pub extern "c" fn poll(fds: [*]pollfd, nfds: nfds_t, timeout: c_int) c_int;
pub extern "c" fn dn_expand(
msg: [*]const u8,
eomorig: [*]const u8,
comp_dn: [*]const u8,
exp_dn: [*]u8,
length: c_int,
) c_int;

View File

@ -56,3 +56,58 @@ pub fn sigaddset(set: *sigset_t, signo: u5) void {
}
pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int;
/// get address to use bind()
pub const AI_PASSIVE = 0x00000001;
/// fill ai_canonname
pub const AI_CANONNAME = 0x00000002;
/// prevent host name resolution
pub const AI_NUMERICHOST = 0x00000004;
/// prevent service name resolution
pub const AI_NUMERICSERV = 0x00001000;
/// address family for hostname not supported
pub const EAI_ADDRFAMILY = 1;
/// temporary failure in name resolution
pub const EAI_AGAIN = 2;
/// invalid value for ai_flags
pub const EAI_BADFLAGS = 3;
/// non-recoverable failure in name resolution
pub const EAI_FAIL = 4;
/// ai_family not supported
pub const EAI_FAMILY = 5;
/// memory allocation failure
pub const EAI_MEMORY = 6;
/// no address associated with hostname
pub const EAI_NODATA = 7;
/// hostname nor servname provided, or not known
pub const EAI_NONAME = 8;
/// servname not supported for ai_socktype
pub const EAI_SERVICE = 9;
/// ai_socktype not supported
pub const EAI_SOCKTYPE = 10;
/// system error returned in errno
pub const EAI_SYSTEM = 11;
/// invalid value for hints
pub const EAI_BADHINTS = 12;
/// resolved protocol is unknown
pub const EAI_PROTOCOL = 13;
/// argument buffer overflow
pub const EAI_OVERFLOW = 14;
pub const EAI_MAX = 15;

View File

@ -17,6 +17,41 @@ pub const _errno = switch (builtin.abi) {
pub const MAP_FAILED = @intToPtr(*c_void, maxInt(usize));
pub const AI_PASSIVE = 0x01;
pub const AI_CANONNAME = 0x02;
pub const AI_NUMERICHOST = 0x04;
pub const AI_V4MAPPED = 0x08;
pub const AI_ALL = 0x10;
pub const AI_ADDRCONFIG = 0x20;
pub const AI_NUMERICSERV = 0x400;
pub const NI_NUMERICHOST = 0x01;
pub const NI_NUMERICSERV = 0x02;
pub const NI_NOFQDN = 0x04;
pub const NI_NAMEREQD = 0x08;
pub const NI_DGRAM = 0x10;
pub const NI_NUMERICSCOPE = 0x100;
pub const EAI_BADFLAGS = -1;
pub const EAI_NONAME = -2;
pub const EAI_AGAIN = -3;
pub const EAI_FAIL = -4;
pub const EAI_FAMILY = -6;
pub const EAI_SOCKTYPE = -7;
pub const EAI_SERVICE = -8;
pub const EAI_MEMORY = -10;
pub const EAI_SYSTEM = -11;
pub const EAI_OVERFLOW = -12;
pub const EAI_NODATA = -5;
pub const EAI_ADDRFAMILY = -9;
pub const EAI_INPROGRESS = -100;
pub const EAI_CANCELED = -101;
pub const EAI_NOTCANCELED = -102;
pub const EAI_ALLDONE = -103;
pub const EAI_INTR = -104;
pub const EAI_IDN_ENCODE = -105;
pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize;
pub extern "c" fn sched_getaffinity(pid: c_int, size: usize, set: *cpu_set_t) c_int;
pub extern "c" fn eventfd(initval: c_uint, flags: c_uint) c_int;

View File

@ -7,7 +7,6 @@ pub const RwLock = @import("event/rwlock.zig").RwLock;
pub const RwLocked = @import("event/rwlocked.zig").RwLocked;
pub const Loop = @import("event/loop.zig").Loop;
pub const fs = @import("event/fs.zig");
pub const net = @import("event/net.zig");
test "import event tests" {
_ = @import("event/channel.zig");
@ -19,5 +18,4 @@ test "import event tests" {
_ = @import("event/rwlock.zig");
_ = @import("event/rwlocked.zig");
_ = @import("event/loop.zig");
_ = @import("event/net.zig");
}

View File

@ -4,9 +4,11 @@ const assert = std.debug.assert;
const testing = std.testing;
const Loop = std.event.Loop;
/// many producer, many consumer, thread-safe, runtime configurable buffer size
/// when buffer is empty, consumers suspend and are resumed by producers
/// when buffer is full, producers suspend and are resumed by consumers
/// Many producer, many consumer, thread-safe, runtime configurable buffer size.
/// When buffer is empty, consumers suspend and are resumed by producers.
/// When buffer is full, producers suspend and are resumed by consumers.
/// TODO now that async function rewrite has landed, this API should be adjusted
/// to not use the event loop's allocator, and to not require allocation.
pub fn Channel(comptime T: type) type {
return struct {
loop: *Loop,
@ -48,7 +50,7 @@ pub fn Channel(comptime T: type) type {
tick_node: *Loop.NextTickNode,
};
/// call destroy when done
/// Call `destroy` when done.
pub fn create(loop: *Loop, capacity: usize) !*SelfChannel {
const buffer_nodes = try loop.allocator.alloc(T, capacity);
errdefer loop.allocator.free(buffer_nodes);

View File

@ -448,22 +448,67 @@ pub const Loop = struct {
self.finishOneEvent();
}
pub fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) !void {
defer self.linuxRemoveFd(fd);
pub fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) void {
assert(flags & os.EPOLLET == os.EPOLLET);
assert(flags & os.EPOLLONESHOT == os.EPOLLONESHOT);
var resume_node = ResumeNode.Basic{
.base = ResumeNode{
.id = .Basic,
.handle = @frame(),
.overlapped = ResumeNode.overlapped_init,
},
};
var need_to_delete = false;
defer if (need_to_delete) self.linuxRemoveFd(fd);
suspend {
var resume_node = ResumeNode.Basic{
.base = ResumeNode{
.id = .Basic,
.handle = @frame(),
.overlapped = ResumeNode.overlapped_init,
if (self.linuxAddFd(fd, &resume_node.base, flags)) |_| {
need_to_delete = true;
} else |err| switch (err) {
error.FileDescriptorNotRegistered => unreachable,
error.OperationCausesCircularLoop => unreachable,
error.FileDescriptorIncompatibleWithEpoll => unreachable,
error.FileDescriptorAlreadyPresentInSet => unreachable, // evented writes to the same fd is not thread-safe
error.SystemResources,
error.UserResourceLimitReached,
error.Unexpected,
=> {
// Fall back to a blocking poll(). Ideally this codepath is never hit, since
// epoll should be just fine. But this is better than incorrect behavior.
var poll_flags: i16 = 0;
if ((flags & os.EPOLLIN) != 0) poll_flags |= os.POLLIN;
if ((flags & os.EPOLLOUT) != 0) poll_flags |= os.POLLOUT;
var pfd = [1]os.pollfd{os.pollfd{
.fd = fd,
.events = poll_flags,
.revents = undefined,
}};
_ = os.poll(&pfd, -1) catch |poll_err| switch (poll_err) {
error.SystemResources,
error.Unexpected,
=> {
// Even poll() didn't work. The best we can do now is sleep for a
// small duration and then hope that something changed.
std.time.sleep(1 * std.time.millisecond);
},
};
resume @frame();
},
};
try self.linuxAddFd(fd, &resume_node.base, flags);
}
}
}
pub fn waitUntilFdReadable(self: *Loop, fd: os.fd_t) !void {
return self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLIN);
pub fn waitUntilFdReadable(self: *Loop, fd: os.fd_t) void {
return self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLONESHOT | os.EPOLLIN);
}
pub fn waitUntilFdWritable(self: *Loop, fd: os.fd_t) void {
return self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLONESHOT | os.EPOLLOUT);
}
pub fn waitUntilFdWritableOrReadable(self: *Loop, fd: os.fd_t) void {
return self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLONESHOT | os.EPOLLOUT | os.EPOLLIN);
}
pub async fn bsdWaitKev(self: *Loop, ident: usize, filter: i16, fflags: u32) !os.Kevent {
@ -642,7 +687,7 @@ pub const Loop = struct {
.linux => {
self.posixFsRequest(&self.os_data.fs_end_request);
// writing 8 bytes to an eventfd cannot fail
os.write(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;
noasync os.write(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;
return;
},
.macosx, .freebsd, .netbsd, .dragonfly => {
@ -790,6 +835,8 @@ pub const Loop = struct {
}
}
// TODO make this whole function noasync
// https://github.com/ziglang/zig/issues/3157
fn posixFsRun(self: *Loop) void {
while (true) {
if (builtin.os == .linux) {
@ -799,27 +846,27 @@ pub const Loop = struct {
switch (node.data.msg) {
.End => return,
.WriteV => |*msg| {
msg.result = os.writev(msg.fd, msg.iov);
msg.result = noasync os.writev(msg.fd, msg.iov);
},
.PWriteV => |*msg| {
msg.result = os.pwritev(msg.fd, msg.iov, msg.offset);
msg.result = noasync os.pwritev(msg.fd, msg.iov, msg.offset);
},
.PReadV => |*msg| {
msg.result = os.preadv(msg.fd, msg.iov, msg.offset);
msg.result = noasync os.preadv(msg.fd, msg.iov, msg.offset);
},
.Open => |*msg| {
msg.result = os.openC(msg.path.ptr, msg.flags, msg.mode);
msg.result = noasync os.openC(msg.path.ptr, msg.flags, msg.mode);
},
.Close => |*msg| os.close(msg.fd),
.Close => |*msg| noasync os.close(msg.fd),
.WriteFile => |*msg| blk: {
const flags = os.O_LARGEFILE | os.O_WRONLY | os.O_CREAT |
os.O_CLOEXEC | os.O_TRUNC;
const fd = os.openC(msg.path.ptr, flags, msg.mode) catch |err| {
const fd = noasync os.openC(msg.path.ptr, flags, msg.mode) catch |err| {
msg.result = err;
break :blk;
};
defer os.close(fd);
msg.result = os.write(fd, msg.contents);
defer noasync os.close(fd);
msg.result = noasync os.write(fd, msg.contents);
},
}
switch (node.data.finish) {

View File

@ -1,358 +0,0 @@
const std = @import("../std.zig");
const builtin = @import("builtin");
const testing = std.testing;
const event = std.event;
const mem = std.mem;
const os = std.os;
const Loop = std.event.Loop;
const File = std.fs.File;
const fd_t = os.fd_t;
pub const Server = struct {
handleRequestFn: async fn (*Server, *const std.net.Address, File) void,
loop: *Loop,
sockfd: ?i32,
accept_frame: ?anyframe,
listen_address: std.net.Address,
waiting_for_emfile_node: PromiseNode,
listen_resume_node: event.Loop.ResumeNode,
const PromiseNode = std.TailQueue(anyframe).Node;
pub fn init(loop: *Loop) Server {
// TODO can't initialize handler here because we need well defined copy elision
return Server{
.loop = loop,
.sockfd = null,
.accept_frame = null,
.handleRequestFn = undefined,
.waiting_for_emfile_node = undefined,
.listen_address = undefined,
.listen_resume_node = event.Loop.ResumeNode{
.id = event.Loop.ResumeNode.Id.Basic,
.handle = undefined,
.overlapped = event.Loop.ResumeNode.overlapped_init,
},
};
}
pub fn listen(
self: *Server,
address: *const std.net.Address,
handleRequestFn: async fn (*Server, *const std.net.Address, File) void,
) !void {
self.handleRequestFn = handleRequestFn;
const sockfd = try os.socket(os.AF_INET, os.SOCK_STREAM | os.SOCK_CLOEXEC | os.SOCK_NONBLOCK, os.PROTO_tcp);
errdefer os.close(sockfd);
self.sockfd = sockfd;
try os.bind(sockfd, &address.os_addr);
try os.listen(sockfd, os.SOMAXCONN);
self.listen_address = std.net.Address.initPosix(try os.getsockname(sockfd));
self.accept_frame = async Server.handler(self);
errdefer await self.accept_frame.?;
self.listen_resume_node.handle = self.accept_frame.?;
try self.loop.linuxAddFd(sockfd, &self.listen_resume_node, os.EPOLLIN | os.EPOLLOUT | os.EPOLLET);
errdefer self.loop.removeFd(sockfd);
}
/// Stop listening
pub fn close(self: *Server) void {
self.loop.linuxRemoveFd(self.sockfd.?);
if (self.sockfd) |fd| {
os.close(fd);
self.sockfd = null;
}
}
pub fn deinit(self: *Server) void {
if (self.accept_frame) |accept_frame| await accept_frame;
if (self.sockfd) |sockfd| os.close(sockfd);
}
pub async fn handler(self: *Server) void {
while (true) {
var accepted_addr: std.net.Address = undefined;
// TODO just inline the following function here and don't expose it as posixAsyncAccept
if (os.accept4_async(self.sockfd.?, &accepted_addr.os_addr, os.SOCK_NONBLOCK | os.SOCK_CLOEXEC)) |accepted_fd| {
if (accepted_fd == -1) {
// would block
suspend; // we will get resumed by epoll_wait in the event loop
continue;
}
var socket = File.openHandle(accepted_fd);
self.handleRequestFn(self, &accepted_addr, socket);
} else |err| switch (err) {
error.ProcessFdQuotaExceeded => @panic("TODO handle this error"),
error.ConnectionAborted => continue,
error.FileDescriptorNotASocket => unreachable,
error.OperationNotSupported => unreachable,
error.SystemFdQuotaExceeded, error.SystemResources, error.ProtocolFailure, error.BlockedByFirewall, error.Unexpected => {
@panic("TODO handle this error");
},
}
}
}
};
pub async fn connectUnixSocket(loop: *Loop, path: []const u8) !i32 {
const sockfd = try os.socket(
os.AF_UNIX,
os.SOCK_STREAM | os.SOCK_CLOEXEC | os.SOCK_NONBLOCK,
0,
);
errdefer os.close(sockfd);
var sock_addr = os.sockaddr_un{
.family = os.AF_UNIX,
.path = undefined,
};
if (path.len > @typeOf(sock_addr.path).len) return error.NameTooLong;
mem.copy(u8, sock_addr.path[0..], path);
const size = @intCast(u32, @sizeOf(os.sa_family_t) + path.len);
try os.connect_async(sockfd, &sock_addr, size);
try loop.linuxWaitFd(sockfd, os.EPOLLIN | os.EPOLLOUT | os.EPOLLET);
try os.getsockoptError(sockfd);
return sockfd;
}
pub const ReadError = error{
SystemResources,
Unexpected,
UserResourceLimitReached,
InputOutput,
FileDescriptorNotRegistered, // TODO remove this possibility
OperationCausesCircularLoop, // TODO remove this possibility
FileDescriptorAlreadyPresentInSet, // TODO remove this possibility
FileDescriptorIncompatibleWithEpoll, // TODO remove this possibility
};
/// returns number of bytes read. 0 means EOF.
pub async fn read(loop: *std.event.Loop, fd: fd_t, buffer: []u8) ReadError!usize {
const iov = os.iovec{
.iov_base = buffer.ptr,
.iov_len = buffer.len,
};
const iovs: *const [1]os.iovec = &iov;
return readvPosix(loop, fd, iovs, 1);
}
pub const WriteError = error{};
pub async fn write(loop: *std.event.Loop, fd: fd_t, buffer: []const u8) WriteError!void {
const iov = os.iovec_const{
.iov_base = buffer.ptr,
.iov_len = buffer.len,
};
const iovs: *const [1]os.iovec_const = &iov;
return writevPosix(loop, fd, iovs, 1);
}
pub async fn writevPosix(loop: *Loop, fd: i32, iov: [*]const os.iovec_const, count: usize) !void {
while (true) {
switch (builtin.os) {
.macosx, .linux => {
switch (os.errno(os.system.writev(fd, iov, count))) {
0 => return,
os.EINTR => continue,
os.ESPIPE => unreachable,
os.EINVAL => unreachable,
os.EFAULT => unreachable,
os.EAGAIN => {
try loop.linuxWaitFd(fd, os.EPOLLET | os.EPOLLOUT);
continue;
},
os.EBADF => unreachable, // always a race condition
os.EDESTADDRREQ => unreachable, // connect was never called
os.EDQUOT => unreachable,
os.EFBIG => unreachable,
os.EIO => return error.InputOutput,
os.ENOSPC => unreachable,
os.EPERM => return error.AccessDenied,
os.EPIPE => unreachable,
else => |err| return os.unexpectedErrno(err),
}
},
else => @compileError("Unsupported OS"),
}
}
}
/// returns number of bytes read. 0 means EOF.
pub async fn readvPosix(loop: *std.event.Loop, fd: i32, iov: [*]os.iovec, count: usize) !usize {
while (true) {
switch (builtin.os) {
builtin.Os.linux, builtin.Os.freebsd, builtin.Os.macosx => {
const rc = os.system.readv(fd, iov, count);
switch (os.errno(rc)) {
0 => return rc,
os.EINTR => continue,
os.EINVAL => unreachable,
os.EFAULT => unreachable,
os.EAGAIN => {
try loop.linuxWaitFd(fd, os.EPOLLET | os.EPOLLIN);
continue;
},
os.EBADF => unreachable, // always a race condition
os.EIO => return error.InputOutput,
os.EISDIR => unreachable,
os.ENOBUFS => return error.SystemResources,
os.ENOMEM => return error.SystemResources,
else => |err| return os.unexpectedErrno(err),
}
},
else => @compileError("Unsupported OS"),
}
}
}
pub async fn writev(loop: *Loop, fd: fd_t, data: []const []const u8) !void {
const iovecs = try loop.allocator.alloc(os.iovec_const, data.len);
defer loop.allocator.free(iovecs);
for (data) |buf, i| {
iovecs[i] = os.iovec_const{
.iov_base = buf.ptr,
.iov_len = buf.len,
};
}
return writevPosix(loop, fd, iovecs.ptr, data.len);
}
pub async fn readv(loop: *Loop, fd: fd_t, data: []const []u8) !usize {
const iovecs = try loop.allocator.alloc(os.iovec, data.len);
defer loop.allocator.free(iovecs);
for (data) |buf, i| {
iovecs[i] = os.iovec{
.iov_base = buf.ptr,
.iov_len = buf.len,
};
}
return readvPosix(loop, fd, iovecs.ptr, data.len);
}
pub async fn connect(loop: *Loop, _address: *const std.net.Address) !File {
var address = _address.*; // TODO https://github.com/ziglang/zig/issues/1592
const sockfd = try os.socket(os.AF_INET, os.SOCK_STREAM | os.SOCK_CLOEXEC | os.SOCK_NONBLOCK, os.PROTO_tcp);
errdefer os.close(sockfd);
try os.connect_async(sockfd, &address.os_addr, @sizeOf(os.sockaddr_in));
try loop.linuxWaitFd(sockfd, os.EPOLLIN | os.EPOLLOUT | os.EPOLLET);
try os.getsockoptError(sockfd);
return File.openHandle(sockfd);
}
test "listen on a port, send bytes, receive bytes" {
// https://github.com/ziglang/zig/issues/2377
if (true) return error.SkipZigTest;
if (builtin.os != builtin.Os.linux) {
// TODO build abstractions for other operating systems
return error.SkipZigTest;
}
const MyServer = struct {
tcp_server: Server,
const Self = @This();
async fn handler(tcp_server: *Server, _addr: *const std.net.Address, _socket: File) void {
const self = @fieldParentPtr(Self, "tcp_server", tcp_server);
var socket = _socket; // TODO https://github.com/ziglang/zig/issues/1592
defer socket.close();
const next_handler = errorableHandler(self, _addr, socket) catch |err| {
std.debug.panic("unable to handle connection: {}\n", err);
};
}
async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: File) !void {
const addr = _addr.*; // TODO https://github.com/ziglang/zig/issues/1592
var socket = _socket; // TODO https://github.com/ziglang/zig/issues/1592
const stream = &socket.outStream().stream;
try stream.print("hello from server\n");
}
};
const ip4addr = std.net.parseIp4("127.0.0.1") catch unreachable;
const addr = std.net.Address.initIp4(ip4addr, 0);
var loop: Loop = undefined;
try loop.initSingleThreaded(std.debug.global_allocator);
var server = MyServer{ .tcp_server = Server.init(&loop) };
defer server.tcp_server.deinit();
try server.tcp_server.listen(&addr, MyServer.handler);
_ = async doAsyncTest(&loop, &server.tcp_server.listen_address, &server.tcp_server);
loop.run();
}
async fn doAsyncTest(loop: *Loop, address: *const std.net.Address, server: *Server) void {
errdefer @panic("test failure");
var socket_file = try connect(loop, address);
defer socket_file.close();
var buf: [512]u8 = undefined;
const amt_read = try socket_file.read(buf[0..]);
const msg = buf[0..amt_read];
testing.expect(mem.eql(u8, msg, "hello from server\n"));
server.close();
}
pub const OutStream = struct {
fd: fd_t,
stream: Stream,
loop: *Loop,
pub const Error = WriteError;
pub const Stream = event.io.OutStream(Error);
pub fn init(loop: *Loop, fd: fd_t) OutStream {
return OutStream{
.fd = fd,
.loop = loop,
.stream = Stream{ .writeFn = writeFn },
};
}
async fn writeFn(out_stream: *Stream, bytes: []const u8) Error!void {
const self = @fieldParentPtr(OutStream, "stream", out_stream);
return write(self.loop, self.fd, bytes);
}
};
pub const InStream = struct {
fd: fd_t,
stream: Stream,
loop: *Loop,
pub const Error = ReadError;
pub const Stream = event.io.InStream(Error);
pub fn init(loop: *Loop, fd: fd_t) InStream {
return InStream{
.fd = fd,
.loop = loop,
.stream = Stream{ .readFn = readFn },
};
}
async fn readFn(in_stream: *Stream, bytes: []u8) Error!usize {
const self = @fieldParentPtr(InStream, "stream", in_stream);
return read(self.loop, self.fd, bytes);
}
};

View File

@ -53,7 +53,7 @@ fn peekIsAlign(comptime fmt: []const u8) bool {
/// The format string must be comptime known and may contain placeholders following
/// this format:
/// `{[position][specifier]:[fill][alignment][width].[precision]}`
///
///
/// Each word between `[` and `]` is a parameter you have to replace with something:
///
/// - *position* is the index of the argument that should be inserted
@ -78,7 +78,7 @@ fn peekIsAlign(comptime fmt: []const u8) bool {
/// - `d`: output numeric value in decimal notation
/// - `b`: output integer value in binary notation
/// - `c`: output integer as an ASCII character. Integer type must have 8 bits at max.
/// - `*`: output the address of the value instead of the value itself.
/// - `*`: output the address of the value instead of the value itself.
///
/// If a formatted user type contains a function of the type
/// ```

View File

@ -704,7 +704,7 @@ pub const Dir = struct {
/// Call `File.close` on the result when done.
pub fn openReadC(self: Dir, sub_path: [*]const u8) File.OpenError!File {
const flags = os.O_LARGEFILE | os.O_RDONLY;
const flags = os.O_LARGEFILE | os.O_RDONLY | os.O_CLOEXEC;
const fd = try os.openatC(self.fd, sub_path, flags, 0);
return File.openHandle(fd);
}

View File

@ -41,7 +41,7 @@ pub const File = struct {
const path_w = try windows.cStrToPrefixedFileW(path);
return openReadW(&path_w);
}
const flags = os.O_LARGEFILE | os.O_RDONLY;
const flags = os.O_LARGEFILE | os.O_RDONLY | os.O_CLOEXEC;
const fd = try os.openC(path, flags, 0);
return openHandle(fd);
}

View File

@ -64,68 +64,7 @@ pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream;
pub const SliceSeekableInStream = @import("io/seekable_stream.zig").SliceSeekableInStream;
pub const COutStream = @import("io/c_out_stream.zig").COutStream;
pub const InStream = @import("io/in_stream.zig").InStream;
pub fn OutStream(comptime WriteError: type) type {
return struct {
const Self = @This();
pub const Error = WriteError;
writeFn: fn (self: *Self, bytes: []const u8) Error!void,
pub fn print(self: *Self, comptime format: []const u8, args: ...) Error!void {
return std.fmt.format(self, Error, self.writeFn, format, args);
}
pub fn write(self: *Self, bytes: []const u8) Error!void {
return self.writeFn(self, bytes);
}
pub fn writeByte(self: *Self, byte: u8) Error!void {
const slice = (*const [1]u8)(&byte)[0..];
return self.writeFn(self, slice);
}
pub fn writeByteNTimes(self: *Self, byte: u8, n: usize) Error!void {
const slice = (*const [1]u8)(&byte)[0..];
var i: usize = 0;
while (i < n) : (i += 1) {
try self.writeFn(self, slice);
}
}
/// Write a native-endian integer.
pub fn writeIntNative(self: *Self, comptime T: type, value: T) Error!void {
var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
mem.writeIntNative(T, &bytes, value);
return self.writeFn(self, bytes);
}
/// Write a foreign-endian integer.
pub fn writeIntForeign(self: *Self, comptime T: type, value: T) Error!void {
var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
mem.writeIntForeign(T, &bytes, value);
return self.writeFn(self, bytes);
}
pub fn writeIntLittle(self: *Self, comptime T: type, value: T) Error!void {
var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
mem.writeIntLittle(T, &bytes, value);
return self.writeFn(self, bytes);
}
pub fn writeIntBig(self: *Self, comptime T: type, value: T) Error!void {
var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
mem.writeIntBig(T, &bytes, value);
return self.writeFn(self, bytes);
}
pub fn writeInt(self: *Self, comptime T: type, value: T, endian: builtin.Endian) Error!void {
var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
mem.writeInt(T, &bytes, value, endian);
return self.writeFn(self, bytes);
}
};
}
pub const OutStream = @import("io/out_stream.zig").OutStream;
/// TODO move this to `std.fs` and add a version to `std.fs.Dir`.
pub fn writeFile(path: []const u8, data: []const u8) !void {

View File

@ -11,7 +11,6 @@ pub const stack_size: usize = if (@hasDecl(root, "stack_size_std_io_InStream"))
root.stack_size_std_io_InStream
else
default_stack_size;
pub const stack_align = 16;
pub fn InStream(comptime ReadError: type) type {
return struct {
@ -34,7 +33,7 @@ pub fn InStream(comptime ReadError: type) type {
if (std.io.is_async) {
// Let's not be writing 0xaa in safe modes for upwards of 4 MiB for every stream read.
@setRuntimeSafety(false);
var stack_frame: [stack_size]u8 align(stack_align) = undefined;
var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined;
return await @asyncCall(&stack_frame, {}, self.readFn, self, buffer);
} else {
return self.readFn(self, buffer);
@ -130,6 +129,47 @@ pub fn InStream(comptime ReadError: type) type {
return buf.toOwnedSlice();
}
/// Reads from the stream until specified byte is found. If the buffer is not
/// large enough to hold the entire contents, `error.StreamTooLong` is returned.
/// If end-of-stream is found, returns the rest of the stream. If this
/// function is called again after that, returns null.
/// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The
/// delimiter byte is not included in the returned slice.
pub fn readUntilDelimiterOrEof(self: *Self, buf: []u8, delimiter: u8) !?[]u8 {
var index: usize = 0;
while (true) {
const byte = self.readByte() catch |err| switch (err) {
error.EndOfStream => {
if (index == 0) {
return null;
} else {
return buf[0..index];
}
},
else => |e| return e,
};
if (byte == delimiter) return buf[0..index];
if (index >= buf.len) return error.StreamTooLong;
buf[index] = byte;
index += 1;
}
}
/// Reads from the stream until specified byte is found, discarding all data,
/// including the delimiter.
/// If end-of-stream is found, this function succeeds.
pub fn skipUntilDelimiterOrEof(self: *Self, delimiter: u8) !void {
while (true) {
const byte = self.readByte() catch |err| switch (err) {
error.EndOfStream => return,
else => |e| return e,
};
if (byte == delimiter) return;
}
}
/// Reads 1 byte from the stream or returns `error.EndOfStream`.
pub fn readByte(self: *Self) !u8 {
var result: [1]u8 = undefined;

87
lib/std/io/out_stream.zig Normal file
View File

@ -0,0 +1,87 @@
const std = @import("../std.zig");
const builtin = @import("builtin");
const root = @import("root");
const mem = std.mem;
pub const default_stack_size = 1 * 1024 * 1024;
pub const stack_size: usize = if (@hasDecl(root, "stack_size_std_io_OutStream"))
root.stack_size_std_io_OutStream
else
default_stack_size;
/// TODO this is not integrated with evented I/O yet.
/// https://github.com/ziglang/zig/issues/3557
pub fn OutStream(comptime WriteError: type) type {
return struct {
const Self = @This();
pub const Error = WriteError;
// TODO https://github.com/ziglang/zig/issues/3557
pub const WriteFn = if (std.io.is_async and false)
async fn (self: *Self, bytes: []const u8) Error!void
else
fn (self: *Self, bytes: []const u8) Error!void;
writeFn: WriteFn,
pub fn write(self: *Self, bytes: []const u8) Error!void {
// TODO https://github.com/ziglang/zig/issues/3557
if (std.io.is_async and false) {
// Let's not be writing 0xaa in safe modes for upwards of 4 MiB for every stream write.
@setRuntimeSafety(false);
var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined;
return await @asyncCall(&stack_frame, {}, self.writeFn, self, bytes);
} else {
return self.writeFn(self, bytes);
}
}
pub fn print(self: *Self, comptime format: []const u8, args: ...) Error!void {
return std.fmt.format(self, Error, self.writeFn, format, args);
}
pub fn writeByte(self: *Self, byte: u8) Error!void {
const slice = (*const [1]u8)(&byte)[0..];
return self.writeFn(self, slice);
}
pub fn writeByteNTimes(self: *Self, byte: u8, n: usize) Error!void {
const slice = (*const [1]u8)(&byte)[0..];
var i: usize = 0;
while (i < n) : (i += 1) {
try self.writeFn(self, slice);
}
}
/// Write a native-endian integer.
pub fn writeIntNative(self: *Self, comptime T: type, value: T) Error!void {
var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
mem.writeIntNative(T, &bytes, value);
return self.writeFn(self, bytes);
}
/// Write a foreign-endian integer.
pub fn writeIntForeign(self: *Self, comptime T: type, value: T) Error!void {
var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
mem.writeIntForeign(T, &bytes, value);
return self.writeFn(self, bytes);
}
pub fn writeIntLittle(self: *Self, comptime T: type, value: T) Error!void {
var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
mem.writeIntLittle(T, &bytes, value);
return self.writeFn(self, bytes);
}
pub fn writeIntBig(self: *Self, comptime T: type, value: T) Error!void {
var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
mem.writeIntBig(T, &bytes, value);
return self.writeFn(self, bytes);
}
pub fn writeInt(self: *Self, comptime T: type, value: T, endian: builtin.Endian) Error!void {
var bytes: [(T.bit_count + 7) / 8]u8 = undefined;
mem.writeInt(T, &bytes, value, endian);
return self.writeFn(self, bytes);
}
};
}

View File

@ -99,7 +99,7 @@ pub const Allocator = struct {
/// memory is no longer needed, to avoid a resource leak. If the
/// `Allocator` implementation is unknown, then correct code will
/// call `free` when done.
///
///
/// For allocating a single item, see `create`.
pub fn alloc(self: *Allocator, comptime T: type, n: usize) Error![]T {
return self.alignedAlloc(T, null, n);

File diff suppressed because it is too large Load Diff

91
lib/std/net/test.zig Normal file
View File

@ -0,0 +1,91 @@
const std = @import("../std.zig");
const net = std.net;
const mem = std.mem;
const testing = std.testing;
test "parse and render IPv6 addresses" {
const addr = try net.IpAddress.parseIp6("FF01:0:0:0:0:0:0:FB", 80);
var buf: [100]u8 = undefined;
const printed = try std.fmt.bufPrint(&buf, "{}", addr);
std.testing.expect(mem.eql(u8, "[ff01::fb]:80", printed));
}
test "parse and render IPv4 addresses" {
var buffer: [18]u8 = undefined;
for ([_][]const u8{
"0.0.0.0",
"255.255.255.255",
"1.2.3.4",
"123.255.0.91",
"127.0.0.1",
}) |ip| {
var addr = net.IpAddress.parseIp4(ip, 0);
var newIp = std.fmt.bufPrint(buffer[0..], "{}", addr) catch unreachable;
std.testing.expect(std.mem.eql(u8, ip, newIp[0 .. newIp.len - 2]));
}
testing.expectError(error.Overflow, net.IpAddress.parseIp4("256.0.0.1", 0));
testing.expectError(error.InvalidCharacter, net.IpAddress.parseIp4("x.0.0.1", 0));
testing.expectError(error.InvalidEnd, net.IpAddress.parseIp4("127.0.0.1.1", 0));
testing.expectError(error.Incomplete, net.IpAddress.parseIp4("127.0.0.", 0));
testing.expectError(error.InvalidCharacter, net.IpAddress.parseIp4("100..0.1", 0));
}
test "resolve DNS" {
if (std.builtin.os == .windows) {
// DNS resolution not implemented on Windows yet.
return error.SkipZigTest;
}
var buf: [1000 * 10]u8 = undefined;
const a = &std.heap.FixedBufferAllocator.init(&buf).allocator;
const address_list = net.getAddressList(a, "example.com", 80) catch |err| switch (err) {
// The tests are required to work even when there is no Internet connection,
// so some of these errors we must accept and skip the test.
error.UnknownHostName => return error.SkipZigTest,
error.TemporaryNameServerFailure => return error.SkipZigTest,
else => return err,
};
address_list.deinit();
}
test "listen on a port, send bytes, receive bytes" {
if (std.builtin.os != .linux) {
// TODO build abstractions for other operating systems
return error.SkipZigTest;
}
if (std.io.mode != .evented) {
// TODO add ability to run tests in non-blocking I/O mode
return error.SkipZigTest;
}
// TODO doing this at comptime crashed the compiler
const localhost = net.IpAddress.parse("127.0.0.1", 0);
var server = net.TcpServer.init(net.TcpServer.Options{});
defer server.deinit();
try server.listen(localhost);
var server_frame = async testServer(&server);
var client_frame = async testClient(server.listen_address);
try await server_frame;
try await client_frame;
}
fn testClient(addr: net.IpAddress) anyerror!void {
const socket_file = try net.tcpConnectToAddress(addr);
defer socket_file.close();
var buf: [100]u8 = undefined;
const len = try socket_file.read(&buf);
const msg = buf[0..len];
testing.expect(mem.eql(u8, msg, "hello from server\n"));
}
fn testServer(server: *net.TcpServer) anyerror!void {
var client_file = try server.accept();
const stream = &client_file.outStream().stream;
try stream.print("hello from server\n");
}

View File

@ -310,7 +310,7 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
EINVAL => unreachable,
EFAULT => unreachable,
EAGAIN => if (std.event.Loop.instance) |loop| {
loop.waitUntilFdReadable(fd) catch return error.WouldBlock;
loop.waitUntilFdReadable(fd);
continue;
} else {
return error.WouldBlock;
@ -327,7 +327,36 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
}
/// Number of bytes read is returned. Upon reading end-of-file, zero is returned.
/// This function is for blocking file descriptors only.
/// If the application has a global event loop enabled, EAGAIN is handled
/// via the event loop. Otherwise EAGAIN results in error.WouldBlock.
pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize {
while (true) {
// TODO handle the case when iov_len is too large and get rid of this @intCast
const rc = system.readv(fd, iov.ptr, @intCast(u32, iov.len));
switch (errno(rc)) {
0 => return @bitCast(usize, rc),
EINTR => continue,
EINVAL => unreachable,
EFAULT => unreachable,
EAGAIN => if (std.event.Loop.instance) |loop| {
loop.waitUntilFdReadable(fd);
continue;
} else {
return error.WouldBlock;
},
EBADF => unreachable, // always a race condition
EIO => return error.InputOutput,
EISDIR => return error.IsDir,
ENOBUFS => return error.SystemResources,
ENOMEM => return error.SystemResources,
else => |err| return unexpectedErrno(err),
}
}
}
/// Number of bytes read is returned. Upon reading end-of-file, zero is returned.
/// If the application has a global event loop enabled, EAGAIN is handled
/// via the event loop. Otherwise EAGAIN results in error.WouldBlock.
pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) ReadError!usize {
if (comptime std.Target.current.isDarwin()) {
// Darwin does not have preadv but it does have pread.
@ -357,7 +386,12 @@ pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) ReadError!usize {
EINVAL => unreachable,
EFAULT => unreachable,
ESPIPE => unreachable, // fd is not seekable
EAGAIN => unreachable, // This function is for blocking reads.
EAGAIN => if (std.event.Loop.instance) |loop| {
loop.waitUntilFdReadable(fd);
continue;
} else {
return error.WouldBlock;
},
EBADF => unreachable, // always a race condition
EIO => return error.InputOutput,
EISDIR => return error.IsDir,
@ -375,7 +409,12 @@ pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) ReadError!usize {
EINTR => continue,
EINVAL => unreachable,
EFAULT => unreachable,
EAGAIN => unreachable, // This function is for blocking reads.
EAGAIN => if (std.event.Loop.instance) |loop| {
loop.waitUntilFdReadable(fd);
continue;
} else {
return error.WouldBlock;
},
EBADF => unreachable, // always a race condition
EIO => return error.InputOutput,
EISDIR => return error.IsDir,
@ -395,10 +434,17 @@ pub const WriteError = error{
BrokenPipe,
SystemResources,
OperationAborted,
/// This error occurs when no global event loop is configured,
/// and reading from the file descriptor would block.
WouldBlock,
} || UnexpectedError;
/// Write to a file descriptor. Keeps trying if it gets interrupted.
/// This function is for blocking file descriptors only.
/// If the application has a global event loop enabled, EAGAIN is handled
/// via the event loop. Otherwise EAGAIN results in error.WouldBlock.
/// TODO evented I/O integration is disabled until
/// https://github.com/ziglang/zig/issues/3557 is solved.
pub fn write(fd: fd_t, bytes: []const u8) WriteError!void {
if (builtin.os == .windows) {
return windows.WriteFile(fd, bytes);
@ -434,7 +480,14 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!void {
EINTR => continue,
EINVAL => unreachable,
EFAULT => unreachable,
EAGAIN => unreachable, // This function is for blocking writes.
// TODO https://github.com/ziglang/zig/issues/3557
EAGAIN => return error.WouldBlock,
//EAGAIN => if (std.event.Loop.instance) |loop| {
// loop.waitUntilFdWritable(fd);
// continue;
//} else {
// return error.WouldBlock;
//},
EBADF => unreachable, // Always a race condition.
EDESTADDRREQ => unreachable, // `connect` was never called.
EDQUOT => return error.DiskQuota,
@ -448,9 +501,9 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!void {
}
}
/// Write multiple buffers to a file descriptor. Keeps trying if it gets interrupted.
/// This function is for blocking file descriptors only. For non-blocking, see
/// `writevAsync`.
/// Write multiple buffers to a file descriptor.
/// If the application has a global event loop enabled, EAGAIN is handled
/// via the event loop. Otherwise EAGAIN results in error.WouldBlock.
pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!void {
while (true) {
// TODO handle the case when iov_len is too large and get rid of this @intCast
@ -460,7 +513,12 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!void {
EINTR => continue,
EINVAL => unreachable,
EFAULT => unreachable,
EAGAIN => unreachable, // This function is for blocking writes.
EAGAIN => if (std.event.Loop.instance) |loop| {
loop.waitUntilFdWritable(fd);
continue;
} else {
return error.WouldBlock;
},
EBADF => unreachable, // Always a race condition.
EDESTADDRREQ => unreachable, // `connect` was never called.
EDQUOT => return error.DiskQuota,
@ -476,8 +534,6 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!void {
/// Write multiple buffers to a file descriptor, with a position offset.
/// Keeps trying if it gets interrupted.
/// This function is for blocking file descriptors only. For non-blocking, see
/// `pwritevAsync`.
pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void {
if (comptime std.Target.current.isDarwin()) {
// Darwin does not have pwritev but it does have pwrite.
@ -506,7 +562,12 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void
ESPIPE => unreachable, // `fd` is not seekable.
EINVAL => unreachable,
EFAULT => unreachable,
EAGAIN => unreachable, // This function is for blocking writes.
EAGAIN => if (std.event.Loop.instance) |loop| {
loop.waitUntilFdWritable(fd);
continue;
} else {
return error.WouldBlock;
},
EBADF => unreachable, // Always a race condition.
EDESTADDRREQ => unreachable, // `connect` was never called.
EDQUOT => return error.DiskQuota,
@ -528,7 +589,12 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void
EINTR => continue,
EINVAL => unreachable,
EFAULT => unreachable,
EAGAIN => unreachable, // This function is for blocking writes.
EAGAIN => if (std.event.Loop.instance) |loop| {
loop.waitUntilFdWritable(fd);
continue;
} else {
return error.WouldBlock;
},
EBADF => unreachable, // Always a race condition.
EDESTADDRREQ => unreachable, // `connect` was never called.
EDQUOT => return error.DiskQuota,
@ -1510,16 +1576,17 @@ pub const SocketError = error{
ProtocolNotSupported,
} || UnexpectedError;
pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!i32 {
pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!fd_t {
const rc = system.socket(domain, socket_type, protocol);
switch (errno(rc)) {
0 => return @intCast(i32, rc),
0 => return @intCast(fd_t, rc),
EACCES => return error.PermissionDenied,
EAFNOSUPPORT => return error.AddressFamilyNotSupported,
EINVAL => return error.ProtocolFamilyNotAvailable,
EMFILE => return error.ProcessFdQuotaExceeded,
ENFILE => return error.SystemFdQuotaExceeded,
ENOBUFS, ENOMEM => return error.SystemResources,
ENOBUFS => return error.SystemResources,
ENOMEM => return error.SystemResources,
EPROTONOSUPPORT => return error.ProtocolNotSupported,
else => |err| return unexpectedErrno(err),
}
@ -1561,17 +1628,17 @@ pub const BindError = error{
} || UnexpectedError;
/// addr is `*const T` where T is one of the sockaddr
pub fn bind(fd: i32, addr: *const sockaddr) BindError!void {
const rc = system.bind(fd, addr, @sizeOf(sockaddr));
pub fn bind(sockfd: fd_t, addr: *const sockaddr, len: socklen_t) BindError!void {
const rc = system.bind(sockfd, addr, len);
switch (errno(rc)) {
0 => return,
EACCES => return error.AccessDenied,
EADDRINUSE => return error.AddressInUse,
EBADF => unreachable, // always a race condition if this error is returned
EINVAL => unreachable,
ENOTSOCK => unreachable,
EINVAL => unreachable, // invalid parameters
ENOTSOCK => unreachable, // invalid `sockfd`
EADDRNOTAVAIL => return error.AddressNotAvailable,
EFAULT => unreachable,
EFAULT => unreachable, // invalid `addr` pointer
ELOOP => return error.SymLinkLoop,
ENAMETOOLONG => return error.NameTooLong,
ENOENT => return error.FileNotFound,
@ -1622,12 +1689,6 @@ pub const AcceptError = error{
/// by the socket buffer limits, not by the system memory.
SystemResources,
/// The file descriptor sockfd does not refer to a socket.
FileDescriptorNotASocket,
/// The referenced socket is not of type SOCK_STREAM.
OperationNotSupported,
ProtocolFailure,
/// Firewall rules forbid connection.
@ -1644,7 +1705,7 @@ pub const AcceptError = error{
pub fn accept4(
/// This argument is a socket that has been created with `socket`, bound to a local address
/// with `bind`, and is listening for connections after a `listen`.
sockfd: i32,
sockfd: fd_t,
/// This argument is a pointer to a sockaddr structure. This structure is filled in with the
/// address of the peer socket, as known to the communications layer. The exact format of the
/// address returned addr is determined by the socket's address family (see `socket` and the
@ -1665,15 +1726,15 @@ pub fn accept4(
/// * `SOCK_CLOEXEC` - Set the close-on-exec (`FD_CLOEXEC`) flag on the new file descriptor. See the
/// description of the `O_CLOEXEC` flag in `open` for reasons why this may be useful.
flags: u32,
) AcceptError!i32 {
) AcceptError!fd_t {
while (true) {
const rc = system.accept4(sockfd, addr, addr_size, flags);
switch (errno(rc)) {
0 => return @intCast(i32, rc),
0 => return @intCast(fd_t, rc),
EINTR => continue,
EAGAIN => if (std.event.Loop.instance) |loop| {
loop.waitUntilFdReadable(sockfd) catch return error.WouldBlock;
loop.waitUntilFdReadable(sockfd);
continue;
} else {
return error.WouldBlock;
@ -1682,12 +1743,12 @@ pub fn accept4(
ECONNABORTED => return error.ConnectionAborted,
EFAULT => unreachable,
EINVAL => unreachable,
ENOTSOCK => unreachable,
EMFILE => return error.ProcessFdQuotaExceeded,
ENFILE => return error.SystemFdQuotaExceeded,
ENOBUFS => return error.SystemResources,
ENOMEM => return error.SystemResources,
ENOTSOCK => return error.FileDescriptorNotASocket,
EOPNOTSUPP => return error.OperationNotSupported,
EOPNOTSUPP => unreachable,
EPROTO => return error.ProtocolFailure,
EPERM => return error.BlockedByFirewall,
@ -1809,11 +1870,9 @@ pub const GetSockNameError = error{
SystemResources,
} || UnexpectedError;
pub fn getsockname(sockfd: i32) GetSockNameError!sockaddr {
var addr: sockaddr = undefined;
var addrlen: socklen_t = @sizeOf(sockaddr);
switch (errno(system.getsockname(sockfd, &addr, &addrlen))) {
0 => return addr,
pub fn getsockname(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void {
switch (errno(system.getsockname(sockfd, addr, addrlen))) {
0 => return,
else => |err| return unexpectedErrno(err),
EBADF => unreachable, // always a race condition
@ -1856,12 +1915,14 @@ pub const ConnectError = error{
/// Timeout while attempting connection. The server may be too busy to accept new connections. Note
/// that for IP sockets the timeout may be very long when syncookies are enabled on the server.
ConnectionTimedOut,
/// This error occurs when no global event loop is configured,
/// and connecting to the socket would block.
WouldBlock,
} || UnexpectedError;
/// Initiate a connection on a socket.
/// This is for blocking file descriptors only.
/// For non-blocking, see `connect_async`.
pub fn connect(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!void {
pub fn connect(sockfd: fd_t, sock_addr: *const sockaddr, len: socklen_t) ConnectError!void {
while (true) {
switch (errno(system.connect(sockfd, sock_addr, len))) {
0 => return,
@ -1870,12 +1931,15 @@ pub fn connect(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!v
EADDRINUSE => return error.AddressInUse,
EADDRNOTAVAIL => return error.AddressNotAvailable,
EAFNOSUPPORT => return error.AddressFamilyNotSupported,
EAGAIN => return error.SystemResources,
EAGAIN, EINPROGRESS => {
const loop = std.event.Loop.instance orelse return error.WouldBlock;
loop.waitUntilFdWritableOrReadable(sockfd);
return getsockoptError(sockfd);
},
EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
EBADF => unreachable, // sockfd is not a valid open file descriptor.
ECONNREFUSED => return error.ConnectionRefused,
EFAULT => unreachable, // The socket structure address is outside the user's address space.
EINPROGRESS => unreachable, // The socket is nonblocking and the connection cannot be completed immediately.
EINTR => continue,
EISCONN => unreachable, // The socket is already connected.
ENETUNREACH => return error.NetworkUnreachable,
@ -1887,34 +1951,6 @@ pub fn connect(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!v
}
}
/// Same as `connect` except it is for non-blocking socket file descriptors.
/// It expects to receive EINPROGRESS`.
pub fn connect_async(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!void {
while (true) {
switch (errno(system.connect(sockfd, sock_addr, len))) {
EINVAL => unreachable,
EINTR => continue,
0, EINPROGRESS => return,
EACCES => return error.PermissionDenied,
EPERM => return error.PermissionDenied,
EADDRINUSE => return error.AddressInUse,
EADDRNOTAVAIL => return error.AddressNotAvailable,
EAFNOSUPPORT => return error.AddressFamilyNotSupported,
EAGAIN => return error.SystemResources,
EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
EBADF => unreachable, // sockfd is not a valid open file descriptor.
ECONNREFUSED => return error.ConnectionRefused,
EFAULT => unreachable, // The socket structure address is outside the user's address space.
EISCONN => unreachable, // The socket is already connected.
ENETUNREACH => return error.NetworkUnreachable,
ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
ETIMEDOUT => return error.ConnectionTimedOut,
else => |err| return unexpectedErrno(err),
}
}
}
pub fn getsockoptError(sockfd: i32) ConnectError!void {
var err_code: u32 = undefined;
var size: u32 = @sizeOf(u32);
@ -2835,3 +2871,297 @@ pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 {
@compileError("TODO implement gethostname for this OS");
}
pub fn res_mkquery(
op: u4,
dname: []const u8,
class: u8,
ty: u8,
data: []const u8,
newrr: ?[*]const u8,
buf: []u8,
) usize {
// This implementation is ported from musl libc.
// A more idiomatic "ziggy" implementation would be welcome.
var name = dname;
if (mem.endsWith(u8, name, ".")) name.len -= 1;
assert(name.len <= 253);
const n = 17 + name.len + @boolToInt(name.len != 0);
// Construct query template - ID will be filled later
var q: [280]u8 = undefined;
@memset(&q, 0, n);
q[2] = u8(op) * 8 + 1;
q[5] = 1;
mem.copy(u8, q[13..], name);
var i: usize = 13;
var j: usize = undefined;
while (q[i] != 0) : (i = j + 1) {
j = i;
while (q[j] != 0 and q[j] != '.') : (j += 1) {}
// TODO determine the circumstances for this and whether or
// not this should be an error.
if (j - i - 1 > 62) unreachable;
q[i - 1] = @intCast(u8, j - i);
}
q[i + 1] = ty;
q[i + 3] = class;
// Make a reasonably unpredictable id
var ts: timespec = undefined;
clock_gettime(CLOCK_REALTIME, &ts) catch {};
const UInt = @IntType(false, @typeOf(ts.tv_nsec).bit_count);
const unsec = @bitCast(UInt, ts.tv_nsec);
const id = @truncate(u32, unsec + unsec / 65536);
q[0] = @truncate(u8, id / 256);
q[1] = @truncate(u8, id);
mem.copy(u8, buf, q[0..n]);
return n;
}
pub const SendError = error{
/// (For UNIX domain sockets, which are identified by pathname) Write permission is denied
/// on the destination socket file, or search permission is denied for one of the
/// directories the path prefix. (See path_resolution(7).)
/// (For UDP sockets) An attempt was made to send to a network/broadcast address as though
/// it was a unicast address.
AccessDenied,
/// The socket is marked nonblocking and the requested operation would block, and
/// there is no global event loop configured.
/// It's also possible to get this error under the following condition:
/// (Internet domain datagram sockets) The socket referred to by sockfd had not previously
/// been bound to an address and, upon attempting to bind it to an ephemeral port, it was
/// determined that all port numbers in the ephemeral port range are currently in use. See
/// the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7).
WouldBlock,
/// Another Fast Open is already in progress.
FastOpenAlreadyInProgress,
/// Connection reset by peer.
ConnectionResetByPeer,
/// The socket type requires that message be sent atomically, and the size of the message
/// to be sent made this impossible. The message is not transmitted.
///
MessageTooBig,
/// The output queue for a network interface was full. This generally indicates that the
/// interface has stopped sending, but may be caused by transient congestion. (Normally,
/// this does not occur in Linux. Packets are just silently dropped when a device queue
/// overflows.)
/// This is also caused when there is not enough kernel memory available.
SystemResources,
/// The local end has been shut down on a connection oriented socket. In this case, the
/// process will also receive a SIGPIPE unless MSG_NOSIGNAL is set.
BrokenPipe,
} || UnexpectedError;
/// Transmit a message to another socket.
///
/// The `sendto` call may be used only when the socket is in a connected state (so that the intended
/// recipient is known). The following call
///
/// send(sockfd, buf, len, flags);
///
/// is equivalent to
///
/// sendto(sockfd, buf, len, flags, NULL, 0);
///
/// If sendto() is used on a connection-mode (`SOCK_STREAM`, `SOCK_SEQPACKET`) socket, the arguments
/// `dest_addr` and `addrlen` are asserted to be `null` and `0` respectively, and asserted
/// that the socket was actually connected.
/// Otherwise, the address of the target is given by `dest_addr` with `addrlen` specifying its size.
///
/// If the message is too long to pass atomically through the underlying protocol,
/// `SendError.MessageTooBig` is returned, and the message is not transmitted.
///
/// There is no indication of failure to deliver.
///
/// When the message does not fit into the send buffer of the socket, `sendto` normally blocks,
/// unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail
/// with `SendError.WouldBlock`. The `select` call may be used to determine when it is
/// possible to send more data.
pub fn sendto(
/// The file descriptor of the sending socket.
sockfd: fd_t,
/// Message to send.
buf: []const u8,
flags: u32,
dest_addr: ?*const sockaddr,
addrlen: socklen_t,
) SendError!usize {
while (true) {
const rc = system.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen);
switch (errno(rc)) {
0 => return rc,
EACCES => return error.AccessDenied,
EAGAIN => if (std.event.Loop.instance) |loop| {
loop.waitUntilFdWritable(sockfd);
continue;
} else {
return error.WouldBlock;
},
EALREADY => return error.FastOpenAlreadyInProgress,
EBADF => unreachable, // always a race condition
ECONNRESET => return error.ConnectionResetByPeer,
EDESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set.
EFAULT => unreachable, // An invalid user space address was specified for an argument.
EINTR => continue,
EINVAL => unreachable, // Invalid argument passed.
EISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified
EMSGSIZE => return error.MessageTooBig,
ENOBUFS => return error.SystemResources,
ENOMEM => return error.SystemResources,
ENOTCONN => unreachable, // The socket is not connected, and no target has been given.
ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
EOPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type.
EPIPE => return error.BrokenPipe,
else => |err| return unexpectedErrno(err),
}
}
}
/// Transmit a message to another socket.
///
/// The `send` call may be used only when the socket is in a connected state (so that the intended
/// recipient is known). The only difference between `send` and `write` is the presence of
/// flags. With a zero flags argument, `send` is equivalent to `write`. Also, the following
/// call
///
/// send(sockfd, buf, len, flags);
///
/// is equivalent to
///
/// sendto(sockfd, buf, len, flags, NULL, 0);
///
/// There is no indication of failure to deliver.
///
/// When the message does not fit into the send buffer of the socket, `send` normally blocks,
/// unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail
/// with `SendError.WouldBlock`. The `select` call may be used to determine when it is
/// possible to send more data.
pub fn send(
/// The file descriptor of the sending socket.
sockfd: fd_t,
buf: []const u8,
flags: u32,
) SendError!usize {
return sendto(sockfd, buf, flags, null, 0);
}
pub const PollError = error{
/// The kernel had no space to allocate file descriptor tables.
SystemResources,
} || UnexpectedError;
pub fn poll(fds: []pollfd, timeout: i32) PollError!usize {
while (true) {
const rc = system.poll(fds.ptr, fds.len, timeout);
switch (errno(rc)) {
0 => return rc,
EFAULT => unreachable,
EINTR => continue,
EINVAL => unreachable,
ENOMEM => return error.SystemResources,
else => |err| return unexpectedErrno(err),
}
}
}
pub const RecvFromError = error{
/// The socket is marked nonblocking and the requested operation would block, and
/// there is no global event loop configured.
WouldBlock,
/// A remote host refused to allow the network connection, typically because it is not
/// running the requested service.
ConnectionRefused,
/// Could not allocate kernel memory.
SystemResources,
} || UnexpectedError;
pub fn recvfrom(
sockfd: fd_t,
buf: []u8,
flags: u32,
src_addr: ?*sockaddr,
addrlen: ?*socklen_t,
) RecvFromError!usize {
while (true) {
const rc = system.recvfrom(sockfd, buf.ptr, buf.len, flags, src_addr, addrlen);
switch (errno(rc)) {
0 => return rc,
EBADF => unreachable, // always a race condition
EFAULT => unreachable,
EINVAL => unreachable,
ENOTCONN => unreachable,
ENOTSOCK => unreachable,
EINTR => continue,
EAGAIN => if (std.event.Loop.instance) |loop| {
loop.waitUntilFdReadable(sockfd);
continue;
} else {
return error.WouldBlock;
},
ENOMEM => return error.SystemResources,
ECONNREFUSED => return error.ConnectionRefused,
else => |err| return unexpectedErrno(err),
}
}
}
pub const DnExpandError = error{InvalidDnsPacket};
pub fn dn_expand(
msg: []const u8,
comp_dn: []const u8,
exp_dn: []u8,
) DnExpandError!usize {
// This implementation is ported from musl libc.
// A more idiomatic "ziggy" implementation would be welcome.
var p = comp_dn.ptr;
var len: usize = std.math.maxInt(usize);
const end = msg.ptr + msg.len;
if (p == end or exp_dn.len == 0) return error.InvalidDnsPacket;
var dest = exp_dn.ptr;
const dend = dest + std.math.min(exp_dn.len, 254);
// detect reference loop using an iteration counter
var i: usize = 0;
while (i < msg.len) : (i += 2) {
// loop invariants: p<end, dest<dend
if ((p[0] & 0xc0) != 0) {
if (p + 1 == end) return error.InvalidDnsPacket;
var j = ((p[0] & usize(0x3f)) << 8) | p[1];
if (len == std.math.maxInt(usize)) len = @ptrToInt(p) + 2 - @ptrToInt(comp_dn.ptr);
if (j >= msg.len) return error.InvalidDnsPacket;
p = msg.ptr + j;
} else if (p[0] != 0) {
if (dest != exp_dn.ptr) {
dest.* = '.';
dest += 1;
}
var j = p[0];
p += 1;
if (j >= @ptrToInt(end) - @ptrToInt(p) or j >= @ptrToInt(dend) - @ptrToInt(dest)) {
return error.InvalidDnsPacket;
}
while (j != 0) {
j -= 1;
dest.* = p[0];
dest += 1;
p += 1;
}
} else {
dest.* = 0;
if (len == std.math.maxInt(usize)) len = @ptrToInt(p) + 1 - @ptrToInt(comp_dn.ptr);
return len;
}
}
return error.InvalidDnsPacket;
}

View File

@ -8,26 +8,34 @@ pub const pid_t = c_int;
pub const in_port_t = u16;
pub const sa_family_t = u8;
pub const socklen_t = u32;
pub const sockaddr = extern union {
in: sockaddr_in,
in6: sockaddr_in6,
pub const sockaddr = extern struct {
len: u8,
family: sa_family_t,
data: [14]u8,
};
pub const sockaddr_in = extern struct {
len: u8,
family: sa_family_t,
len: u8 = @sizeOf(sockaddr_in),
family: sa_family_t = AF_INET,
port: in_port_t,
addr: u32,
zero: [8]u8,
zero: [8]u8 = [8]u8{ 0, 0, 0, 0, 0, 0, 0, 0 },
};
pub const sockaddr_in6 = extern struct {
len: u8,
family: sa_family_t,
len: u8 = @sizeOf(sockaddr_in6),
family: sa_family_t = AF_INET6,
port: in_port_t,
flowinfo: u32,
addr: [16]u8,
scope_id: u32,
};
/// UNIX domain socket
pub const sockaddr_un = extern struct {
len: u8 = @sizeOf(sockaddr_un),
family: sa_family_t = AF_UNIX,
path: [104]u8,
};
pub const timeval = extern struct {
tv_sec: c_long,
tv_usec: i32,
@ -1196,3 +1204,14 @@ pub const AT_SYMLINK_FOLLOW = 0x0040;
/// Path refers to directory
pub const AT_REMOVEDIR = 0x0080;
pub const addrinfo = extern struct {
flags: i32,
family: i32,
socktype: i32,
protocol: i32,
addrlen: socklen_t,
canonname: ?[*]u8,
addr: ?*sockaddr,
next: ?*addrinfo,
};

View File

@ -141,28 +141,40 @@ pub const dirent = extern struct {
pub const in_port_t = u16;
pub const sa_family_t = u16;
pub const sockaddr = extern union {
in: sockaddr_in,
in6: sockaddr_in6,
pub const sockaddr = extern struct {
/// total length
len: u8,
/// address family
family: sa_family_t,
/// actually longer; address value
data: [14]u8,
};
pub const sockaddr_in = extern struct {
len: u8,
family: sa_family_t,
len: u8 = @sizeOf(sockaddr_in),
family: sa_family_t = AF_INET,
port: in_port_t,
addr: [16]u8,
zero: [8]u8,
addr: u32,
zero: [8]u8 = [8]u8{ 0, 0, 0, 0, 0, 0, 0, 0 },
};
pub const sockaddr_in6 = extern struct {
len: u8,
family: sa_family_t,
len: u8 = @sizeOf(sockaddr_in6),
family: sa_family_t = AF_INET6,
port: in_port_t,
flowinfo: u32,
addr: [16]u8,
scope_id: u32,
};
pub const sockaddr_un = extern struct {
len: u8 = @sizeOf(sockaddr_un),
family: sa_family_t = AF_UNIX,
path: [104]u8,
};
pub const CTL_KERN = 1;
pub const CTL_DEBUG = 5;
@ -336,43 +348,6 @@ pub const SOCK_SEQPACKET = 5;
pub const SOCK_CLOEXEC = 0x10000000;
pub const SOCK_NONBLOCK = 0x20000000;
pub const PROTO_ip = 0o000;
pub const PROTO_icmp = 0o001;
pub const PROTO_igmp = 0o002;
pub const PROTO_ggp = 0o003;
pub const PROTO_ipencap = 0o004;
pub const PROTO_st = 0o005;
pub const PROTO_tcp = 0o006;
pub const PROTO_egp = 0o010;
pub const PROTO_pup = 0o014;
pub const PROTO_udp = 0o021;
pub const PROTO_hmp = 0o024;
pub const PROTO_xns_idp = 0o026;
pub const PROTO_rdp = 0o033;
pub const PROTO_iso_tp4 = 0o035;
pub const PROTO_xtp = 0o044;
pub const PROTO_ddp = 0o045;
pub const PROTO_idpr_cmtp = 0o046;
pub const PROTO_ipv6 = 0o051;
pub const PROTO_ipv6_route = 0o053;
pub const PROTO_ipv6_frag = 0o054;
pub const PROTO_idrp = 0o055;
pub const PROTO_rsvp = 0o056;
pub const PROTO_gre = 0o057;
pub const PROTO_esp = 0o062;
pub const PROTO_ah = 0o063;
pub const PROTO_skip = 0o071;
pub const PROTO_ipv6_icmp = 0o072;
pub const PROTO_ipv6_nonxt = 0o073;
pub const PROTO_ipv6_opts = 0o074;
pub const PROTO_rspf = 0o111;
pub const PROTO_vmtp = 0o121;
pub const PROTO_ospf = 0o131;
pub const PROTO_ipip = 0o136;
pub const PROTO_encap = 0o142;
pub const PROTO_pim = 0o147;
pub const PROTO_raw = 0o377;
pub const PF_UNSPEC = 0;
pub const PF_LOCAL = 1;
pub const PF_UNIX = PF_LOCAL;
@ -963,3 +938,351 @@ pub const AT_REMOVEDIR = 0x0800;
/// Fail if not under dirfd
pub const AT_BENEATH = 0x1000;
/// dummy for IP
pub const IPPROTO_IP = 0;
/// control message protocol
pub const IPPROTO_ICMP = 1;
/// tcp
pub const IPPROTO_TCP = 6;
/// user datagram protocol
pub const IPPROTO_UDP = 17;
/// IP6 header
pub const IPPROTO_IPV6 = 41;
/// raw IP packet
pub const IPPROTO_RAW = 255;
/// IP6 hop-by-hop options
pub const IPPROTO_HOPOPTS = 0;
/// group mgmt protocol
pub const IPPROTO_IGMP = 2;
/// gateway^2 (deprecated)
pub const IPPROTO_GGP = 3;
/// IPv4 encapsulation
pub const IPPROTO_IPV4 = 4;
/// for compatibility
pub const IPPROTO_IPIP = IPPROTO_IPV4;
/// Stream protocol II
pub const IPPROTO_ST = 7;
/// exterior gateway protocol
pub const IPPROTO_EGP = 8;
/// private interior gateway
pub const IPPROTO_PIGP = 9;
/// BBN RCC Monitoring
pub const IPPROTO_RCCMON = 10;
/// network voice protocol
pub const IPPROTO_NVPII = 11;
/// pup
pub const IPPROTO_PUP = 12;
/// Argus
pub const IPPROTO_ARGUS = 13;
/// EMCON
pub const IPPROTO_EMCON = 14;
/// Cross Net Debugger
pub const IPPROTO_XNET = 15;
/// Chaos
pub const IPPROTO_CHAOS = 16;
/// Multiplexing
pub const IPPROTO_MUX = 18;
/// DCN Measurement Subsystems
pub const IPPROTO_MEAS = 19;
/// Host Monitoring
pub const IPPROTO_HMP = 20;
/// Packet Radio Measurement
pub const IPPROTO_PRM = 21;
/// xns idp
pub const IPPROTO_IDP = 22;
/// Trunk-1
pub const IPPROTO_TRUNK1 = 23;
/// Trunk-2
pub const IPPROTO_TRUNK2 = 24;
/// Leaf-1
pub const IPPROTO_LEAF1 = 25;
/// Leaf-2
pub const IPPROTO_LEAF2 = 26;
/// Reliable Data
pub const IPPROTO_RDP = 27;
/// Reliable Transaction
pub const IPPROTO_IRTP = 28;
/// tp-4 w/ class negotiation
pub const IPPROTO_TP = 29;
/// Bulk Data Transfer
pub const IPPROTO_BLT = 30;
/// Network Services
pub const IPPROTO_NSP = 31;
/// Merit Internodal
pub const IPPROTO_INP = 32;
/// Datagram Congestion Control Protocol
pub const IPPROTO_DCCP = 33;
/// Third Party Connect
pub const IPPROTO_3PC = 34;
/// InterDomain Policy Routing
pub const IPPROTO_IDPR = 35;
/// XTP
pub const IPPROTO_XTP = 36;
/// Datagram Delivery
pub const IPPROTO_DDP = 37;
/// Control Message Transport
pub const IPPROTO_CMTP = 38;
/// TP++ Transport
pub const IPPROTO_TPXX = 39;
/// IL transport protocol
pub const IPPROTO_IL = 40;
/// Source Demand Routing
pub const IPPROTO_SDRP = 42;
/// IP6 routing header
pub const IPPROTO_ROUTING = 43;
/// IP6 fragmentation header
pub const IPPROTO_FRAGMENT = 44;
/// InterDomain Routing
pub const IPPROTO_IDRP = 45;
/// resource reservation
pub const IPPROTO_RSVP = 46;
/// General Routing Encap.
pub const IPPROTO_GRE = 47;
/// Mobile Host Routing
pub const IPPROTO_MHRP = 48;
/// BHA
pub const IPPROTO_BHA = 49;
/// IP6 Encap Sec. Payload
pub const IPPROTO_ESP = 50;
/// IP6 Auth Header
pub const IPPROTO_AH = 51;
/// Integ. Net Layer Security
pub const IPPROTO_INLSP = 52;
/// IP with encryption
pub const IPPROTO_SWIPE = 53;
/// Next Hop Resolution
pub const IPPROTO_NHRP = 54;
/// IP Mobility
pub const IPPROTO_MOBILE = 55;
/// Transport Layer Security
pub const IPPROTO_TLSP = 56;
/// SKIP
pub const IPPROTO_SKIP = 57;
/// ICMP6
pub const IPPROTO_ICMPV6 = 58;
/// IP6 no next header
pub const IPPROTO_NONE = 59;
/// IP6 destination option
pub const IPPROTO_DSTOPTS = 60;
/// any host internal protocol
pub const IPPROTO_AHIP = 61;
/// CFTP
pub const IPPROTO_CFTP = 62;
/// "hello" routing protocol
pub const IPPROTO_HELLO = 63;
/// SATNET/Backroom EXPAK
pub const IPPROTO_SATEXPAK = 64;
/// Kryptolan
pub const IPPROTO_KRYPTOLAN = 65;
/// Remote Virtual Disk
pub const IPPROTO_RVD = 66;
/// Pluribus Packet Core
pub const IPPROTO_IPPC = 67;
/// Any distributed FS
pub const IPPROTO_ADFS = 68;
/// Satnet Monitoring
pub const IPPROTO_SATMON = 69;
/// VISA Protocol
pub const IPPROTO_VISA = 70;
/// Packet Core Utility
pub const IPPROTO_IPCV = 71;
/// Comp. Prot. Net. Executive
pub const IPPROTO_CPNX = 72;
/// Comp. Prot. HeartBeat
pub const IPPROTO_CPHB = 73;
/// Wang Span Network
pub const IPPROTO_WSN = 74;
/// Packet Video Protocol
pub const IPPROTO_PVP = 75;
/// BackRoom SATNET Monitoring
pub const IPPROTO_BRSATMON = 76;
/// Sun net disk proto (temp.)
pub const IPPROTO_ND = 77;
/// WIDEBAND Monitoring
pub const IPPROTO_WBMON = 78;
/// WIDEBAND EXPAK
pub const IPPROTO_WBEXPAK = 79;
/// ISO cnlp
pub const IPPROTO_EON = 80;
/// VMTP
pub const IPPROTO_VMTP = 81;
/// Secure VMTP
pub const IPPROTO_SVMTP = 82;
/// Banyon VINES
pub const IPPROTO_VINES = 83;
/// TTP
pub const IPPROTO_TTP = 84;
/// NSFNET-IGP
pub const IPPROTO_IGP = 85;
/// dissimilar gateway prot.
pub const IPPROTO_DGP = 86;
/// TCF
pub const IPPROTO_TCF = 87;
/// Cisco/GXS IGRP
pub const IPPROTO_IGRP = 88;
/// OSPFIGP
pub const IPPROTO_OSPFIGP = 89;
/// Strite RPC protocol
pub const IPPROTO_SRPC = 90;
/// Locus Address Resoloution
pub const IPPROTO_LARP = 91;
/// Multicast Transport
pub const IPPROTO_MTP = 92;
/// AX.25 Frames
pub const IPPROTO_AX25 = 93;
/// IP encapsulated in IP
pub const IPPROTO_IPEIP = 94;
/// Mobile Int.ing control
pub const IPPROTO_MICP = 95;
/// Semaphore Comm. security
pub const IPPROTO_SCCSP = 96;
/// Ethernet IP encapsulation
pub const IPPROTO_ETHERIP = 97;
/// encapsulation header
pub const IPPROTO_ENCAP = 98;
/// any private encr. scheme
pub const IPPROTO_APES = 99;
/// GMTP
pub const IPPROTO_GMTP = 100;
/// payload compression (IPComp)
pub const IPPROTO_IPCOMP = 108;
/// SCTP
pub const IPPROTO_SCTP = 132;
/// IPv6 Mobility Header
pub const IPPROTO_MH = 135;
/// UDP-Lite
pub const IPPROTO_UDPLITE = 136;
/// IP6 Host Identity Protocol
pub const IPPROTO_HIP = 139;
/// IP6 Shim6 Protocol
pub const IPPROTO_SHIM6 = 140;
/// Protocol Independent Mcast
pub const IPPROTO_PIM = 103;
/// CARP
pub const IPPROTO_CARP = 112;
/// PGM
pub const IPPROTO_PGM = 113;
/// MPLS-in-IP
pub const IPPROTO_MPLS = 137;
/// PFSYNC
pub const IPPROTO_PFSYNC = 240;
/// Reserved
pub const IPPROTO_RESERVED_253 = 253;
/// Reserved
pub const IPPROTO_RESERVED_254 = 254;

View File

@ -227,43 +227,6 @@ pub const SEEK_SET = 0;
pub const SEEK_CUR = 1;
pub const SEEK_END = 2;
pub const PROTO_ip = 0o000;
pub const PROTO_icmp = 0o001;
pub const PROTO_igmp = 0o002;
pub const PROTO_ggp = 0o003;
pub const PROTO_ipencap = 0o004;
pub const PROTO_st = 0o005;
pub const PROTO_tcp = 0o006;
pub const PROTO_egp = 0o010;
pub const PROTO_pup = 0o014;
pub const PROTO_udp = 0o021;
pub const PROTO_hmp = 0o024;
pub const PROTO_xns_idp = 0o026;
pub const PROTO_rdp = 0o033;
pub const PROTO_iso_tp4 = 0o035;
pub const PROTO_xtp = 0o044;
pub const PROTO_ddp = 0o045;
pub const PROTO_idpr_cmtp = 0o046;
pub const PROTO_ipv6 = 0o051;
pub const PROTO_ipv6_route = 0o053;
pub const PROTO_ipv6_frag = 0o054;
pub const PROTO_idrp = 0o055;
pub const PROTO_rsvp = 0o056;
pub const PROTO_gre = 0o057;
pub const PROTO_esp = 0o062;
pub const PROTO_ah = 0o063;
pub const PROTO_skip = 0o071;
pub const PROTO_ipv6_icmp = 0o072;
pub const PROTO_ipv6_nonxt = 0o073;
pub const PROTO_ipv6_opts = 0o074;
pub const PROTO_rspf = 0o111;
pub const PROTO_vmtp = 0o121;
pub const PROTO_ospf = 0o131;
pub const PROTO_ipip = 0o136;
pub const PROTO_encap = 0o142;
pub const PROTO_pim = 0o147;
pub const PROTO_raw = 0o377;
pub const SHUT_RD = 0;
pub const SHUT_WR = 1;
pub const SHUT_RDWR = 2;
@ -846,30 +809,31 @@ pub const in_port_t = u16;
pub const sa_family_t = u16;
pub const socklen_t = u32;
/// This intentionally only has ip4 and ip6
pub const sockaddr = extern union {
in: sockaddr_in,
in6: sockaddr_in6,
un: sockaddr_un,
pub const sockaddr = extern struct {
family: sa_family_t,
data: [14]u8,
};
/// IPv4 socket address
pub const sockaddr_in = extern struct {
family: sa_family_t,
family: sa_family_t = AF_INET,
port: in_port_t,
addr: u32,
zero: [8]u8,
zero: [8]u8 = [8]u8{ 0, 0, 0, 0, 0, 0, 0, 0 },
};
/// IPv6 socket address
pub const sockaddr_in6 = extern struct {
family: sa_family_t,
family: sa_family_t = AF_INET6,
port: in_port_t,
flowinfo: u32,
addr: [16]u8,
scope_id: u32,
};
/// UNIX domain socket address
pub const sockaddr_un = extern struct {
family: sa_family_t,
family: sa_family_t = AF_UNIX,
path: [108]u8,
};
@ -1388,3 +1352,70 @@ pub const Statx = extern struct {
__pad2: [14]u64,
};
pub const addrinfo = extern struct {
flags: i32,
family: i32,
socktype: i32,
protocol: i32,
addrlen: socklen_t,
addr: ?*sockaddr,
canonname: ?[*]u8,
next: ?*addrinfo,
};
pub const IPPORT_RESERVED = 1024;
pub const IPPROTO_IP = 0;
pub const IPPROTO_HOPOPTS = 0;
pub const IPPROTO_ICMP = 1;
pub const IPPROTO_IGMP = 2;
pub const IPPROTO_IPIP = 4;
pub const IPPROTO_TCP = 6;
pub const IPPROTO_EGP = 8;
pub const IPPROTO_PUP = 12;
pub const IPPROTO_UDP = 17;
pub const IPPROTO_IDP = 22;
pub const IPPROTO_TP = 29;
pub const IPPROTO_DCCP = 33;
pub const IPPROTO_IPV6 = 41;
pub const IPPROTO_ROUTING = 43;
pub const IPPROTO_FRAGMENT = 44;
pub const IPPROTO_RSVP = 46;
pub const IPPROTO_GRE = 47;
pub const IPPROTO_ESP = 50;
pub const IPPROTO_AH = 51;
pub const IPPROTO_ICMPV6 = 58;
pub const IPPROTO_NONE = 59;
pub const IPPROTO_DSTOPTS = 60;
pub const IPPROTO_MTP = 92;
pub const IPPROTO_BEETPH = 94;
pub const IPPROTO_ENCAP = 98;
pub const IPPROTO_PIM = 103;
pub const IPPROTO_COMP = 108;
pub const IPPROTO_SCTP = 132;
pub const IPPROTO_MH = 135;
pub const IPPROTO_UDPLITE = 136;
pub const IPPROTO_MPLS = 137;
pub const IPPROTO_RAW = 255;
pub const IPPROTO_MAX = 256;
pub const RR_A = 1;
pub const RR_CNAME = 5;
pub const RR_AAAA = 28;
pub const nfds_t = usize;
pub const pollfd = extern struct {
fd: fd_t,
events: i16,
revents: i16,
};
pub const POLLIN = 0x001;
pub const POLLPRI = 0x002;
pub const POLLOUT = 0x004;
pub const POLLERR = 0x008;
pub const POLLHUP = 0x010;
pub const POLLNVAL = 0x020;
pub const POLLRDNORM = 0x040;
pub const POLLRDBAND = 0x080;

View File

@ -137,21 +137,27 @@ pub const dirent = extern struct {
pub const in_port_t = u16;
pub const sa_family_t = u8;
pub const sockaddr = extern union {
in: sockaddr_in,
in6: sockaddr_in6,
pub const sockaddr = extern struct {
/// total length
len: u8,
/// address family
family: sa_family_t,
/// actually longer; address value
data: [14]u8,
};
pub const sockaddr_in = extern struct {
len: u8,
len: u8 = @sizeOf(sockaddr_in),
family: sa_family_t,
port: in_port_t,
addr: u32,
zero: [8]u8,
zero: [8]u8 = [8]u8{ 0, 0, 0, 0, 0, 0, 0, 0 },
};
pub const sockaddr_in6 = extern struct {
len: u8,
len: u8 = @sizeOf(sockaddr_in6),
family: sa_family_t,
port: in_port_t,
flowinfo: u32,
@ -159,6 +165,18 @@ pub const sockaddr_in6 = extern struct {
scope_id: u32,
};
/// Definitions for UNIX IPC domain.
pub const sockaddr_un = extern struct {
/// total sockaddr length
len: u8 = @sizeOf(sockaddr_un),
/// AF_LOCAL
family: sa_family_t,
/// path name
path: [104]u8,
};
pub const CTL_KERN = 1;
pub const CTL_DEBUG = 5;
@ -316,31 +334,6 @@ pub const SOCK_SEQPACKET = 5;
pub const SOCK_CLOEXEC = 0x10000000;
pub const SOCK_NONBLOCK = 0x20000000;
pub const PROTO_ip = 0;
pub const PROTO_icmp = 1;
pub const PROTO_igmp = 2;
pub const PROTO_ggp = 3;
pub const PROTO_ipencap = 4;
pub const PROTO_tcp = 6;
pub const PROTO_egp = 8;
pub const PROTO_pup = 12;
pub const PROTO_udp = 17;
pub const PROTO_xns_idp = 22;
pub const PROTO_iso_tp4 = 29;
pub const PROTO_ipv6 = 41;
pub const PROTO_ipv6_route = 43;
pub const PROTO_ipv6_frag = 44;
pub const PROTO_rsvp = 46;
pub const PROTO_gre = 47;
pub const PROTO_esp = 50;
pub const PROTO_ah = 51;
pub const PROTO_ipv6_icmp = 58;
pub const PROTO_ipv6_nonxt = 59;
pub const PROTO_ipv6_opts = 60;
pub const PROTO_encap = 98;
pub const PROTO_pim = 103;
pub const PROTO_raw = 255;
pub const PF_UNSPEC = 0;
pub const PF_LOCAL = 1;
pub const PF_UNIX = PF_LOCAL;
@ -827,3 +820,114 @@ pub fn S_IWHT(m: u32) bool {
}
pub const HOST_NAME_MAX = 255;
/// dummy for IP
pub const IPPROTO_IP = 0;
/// IP6 hop-by-hop options
pub const IPPROTO_HOPOPTS = 0;
/// control message protocol
pub const IPPROTO_ICMP = 1;
/// group mgmt protocol
pub const IPPROTO_IGMP = 2;
/// gateway^2 (deprecated)
pub const IPPROTO_GGP = 3;
/// IP header
pub const IPPROTO_IPV4 = 4;
/// IP inside IP
pub const IPPROTO_IPIP = 4;
/// tcp
pub const IPPROTO_TCP = 6;
/// exterior gateway protocol
pub const IPPROTO_EGP = 8;
/// pup
pub const IPPROTO_PUP = 12;
/// user datagram protocol
pub const IPPROTO_UDP = 17;
/// xns idp
pub const IPPROTO_IDP = 22;
/// tp-4 w/ class negotiation
pub const IPPROTO_TP = 29;
/// DCCP
pub const IPPROTO_DCCP = 33;
/// IP6 header
pub const IPPROTO_IPV6 = 41;
/// IP6 routing header
pub const IPPROTO_ROUTING = 43;
/// IP6 fragmentation header
pub const IPPROTO_FRAGMENT = 44;
/// resource reservation
pub const IPPROTO_RSVP = 46;
/// GRE encaps RFC 1701
pub const IPPROTO_GRE = 47;
/// encap. security payload
pub const IPPROTO_ESP = 50;
/// authentication header
pub const IPPROTO_AH = 51;
/// IP Mobility RFC 2004
pub const IPPROTO_MOBILE = 55;
/// IPv6 ICMP
pub const IPPROTO_IPV6_ICMP = 58;
/// ICMP6
pub const IPPROTO_ICMPV6 = 58;
/// IP6 no next header
pub const IPPROTO_NONE = 59;
/// IP6 destination option
pub const IPPROTO_DSTOPTS = 60;
/// ISO cnlp
pub const IPPROTO_EON = 80;
/// Ethernet-in-IP
pub const IPPROTO_ETHERIP = 97;
/// encapsulation header
pub const IPPROTO_ENCAP = 98;
/// Protocol indep. multicast
pub const IPPROTO_PIM = 103;
/// IP Payload Comp. Protocol
pub const IPPROTO_IPCOMP = 108;
/// VRRP RFC 2338
pub const IPPROTO_VRRP = 112;
/// Common Address Resolution Protocol
pub const IPPROTO_CARP = 112;
/// L2TPv3
pub const IPPROTO_L2TP = 115;
/// SCTP
pub const IPPROTO_SCTP = 132;
/// PFSYNC
pub const IPPROTO_PFSYNC = 240;
/// raw IP packet
pub const IPPROTO_RAW = 255;

View File

@ -161,3 +161,63 @@ pub const F_OK = 0;
/// Remove directory instead of unlinking file
pub const AT_REMOVEDIR = 0x200;
pub const in_port_t = u16;
pub const sa_family_t = u16;
pub const socklen_t = u32;
pub const sockaddr = extern struct {
family: sa_family_t,
data: [14]u8,
};
pub const sockaddr_in = extern struct {
family: sa_family_t = AF_INET,
port: in_port_t,
addr: in_addr,
zero: [8]u8 = [8]u8{ 0, 0, 0, 0, 0, 0, 0, 0 },
};
pub const sockaddr_in6 = extern struct {
family: sa_family_t = AF_INET6,
port: in_port_t,
flowinfo: u32,
addr: in6_addr,
scope_id: u32,
};
pub const in6_addr = [16]u8;
pub const in_addr = u32;
pub const AF_UNSPEC = 0;
pub const AF_UNIX = 1;
pub const AF_INET = 2;
pub const AF_IMPLINK = 3;
pub const AF_PUP = 4;
pub const AF_CHAOS = 5;
pub const AF_NS = 6;
pub const AF_IPX = AF_NS;
pub const AF_ISO = 7;
pub const AF_OSI = AF_ISO;
pub const AF_ECMA = 8;
pub const AF_DATAKIT = 9;
pub const AF_CCITT = 10;
pub const AF_SNA = 11;
pub const AF_DECnet = 12;
pub const AF_DLI = 13;
pub const AF_LAT = 14;
pub const AF_HYLINK = 15;
pub const AF_APPLETALK = 16;
pub const AF_NETBIOS = 17;
pub const AF_VOICEVIEW = 18;
pub const AF_FIREFOX = 19;
pub const AF_UNKNOWN1 = 20;
pub const AF_BAN = 21;
pub const AF_ATM = 22;
pub const AF_INET6 = 23;
pub const AF_CLUSTER = 24;
pub const AF_12844 = 25;
pub const AF_IRDA = 26;
pub const AF_NETDES = 28;
pub const AF_TCNPROCESS = 29;
pub const AF_TCNMESSAGE = 30;
pub const AF_ICLFXBM = 31;
pub const AF_BTH = 32;
pub const AF_MAX = 33;

View File

@ -226,6 +226,28 @@ pub fn munmap(address: [*]const u8, length: usize) usize {
return syscall2(SYS_munmap, @ptrToInt(address), length);
}
pub fn poll(fds: [*]pollfd, n: nfds_t, timeout: i32) usize {
if (@hasDecl(@This(), "SYS_poll")) {
return syscall3(SYS_poll, @ptrToInt(fds), n, @bitCast(u32, timeout));
} else {
return syscall6(
SYS_ppoll,
@ptrToInt(fds),
n,
@ptrToInt(if (timeout >= 0)
&timespec{
.tv_sec = @divTrunc(timeout, 1000),
.tv_nsec = @rem(timeout, 1000) * 1000000,
}
else
null),
0,
0,
NSIG / 8,
);
}
}
pub fn read(fd: i32, buf: [*]u8, count: usize) usize {
return syscall3(SYS_read, @bitCast(usize, isize(fd)), @ptrToInt(buf), count);
}

View File

@ -205,6 +205,8 @@ pub const Target = union(enum) {
},
};
pub const stack_align = 16;
pub fn zigTriple(self: Target, allocator: *mem.Allocator) ![]u8 {
return std.fmt.allocPrint(
allocator,

View File

@ -128,6 +128,7 @@ export fn stage2_free_clang_errors(errors_ptr: [*]translate_c.ClangErrMsg, error
export fn stage2_render_ast(tree: *ast.Tree, output_file: *FILE) Error {
const c_out_stream = &std.io.COutStream.init(output_file).stream;
_ = std.zig.render(std.heap.c_allocator, c_out_stream, tree) catch |e| switch (e) {
error.WouldBlock => unreachable, // stage1 opens stuff in exclusively blocking mode
error.SystemResources => return Error.SystemResources,
error.OperationAborted => return Error.OperationAborted,
error.BrokenPipe => return Error.BrokenPipe,

View File

@ -151,18 +151,22 @@ pub fn translate(
};
defer ZigClangASTUnit_delete(ast_unit);
var tree_arena = std.heap.ArenaAllocator.init(backing_allocator);
errdefer tree_arena.deinit();
const tree = blk: {
var tree_arena = std.heap.ArenaAllocator.init(backing_allocator);
errdefer tree_arena.deinit();
const tree = try tree_arena.allocator.create(ast.Tree);
tree.* = ast.Tree{
.source = undefined, // need to use Buffer.toOwnedSlice later
.root_node = undefined,
.arena_allocator = tree_arena,
.tokens = undefined, // can't reference the allocator yet
.errors = undefined, // can't reference the allocator yet
const tree = try tree_arena.allocator.create(ast.Tree);
tree.* = ast.Tree{
.source = undefined, // need to use Buffer.toOwnedSlice later
.root_node = undefined,
.arena_allocator = tree_arena,
.tokens = undefined, // can't reference the allocator yet
.errors = undefined, // can't reference the allocator yet
};
break :blk tree;
};
const arena = &tree.arena_allocator.allocator; // now we can reference the allocator
errdefer tree.arena_allocator.deinit();
tree.tokens = ast.Tree.TokenList.init(arena);
tree.errors = ast.Tree.ErrorList.init(arena);

View File

@ -4381,6 +4381,10 @@ static Error analyze_callee_async(CodeGen *g, ZigFn *fn, ZigFn *callee, AstNode
if (callee->anal_state == FnAnalStateComplete) {
analyze_fn_async(g, callee, true);
if (callee->anal_state == FnAnalStateInvalid) {
if (g->trace_err != nullptr) {
g->trace_err = add_error_note(g, g->trace_err, call_node,
buf_sprintf("while checking if '%s' is async", buf_ptr(&fn->symbol_name)));
}
return ErrorSemanticAnalyzeFail;
}
callee_is_async = fn_is_async(callee);
@ -6128,6 +6132,9 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {
param_name = buf_sprintf("@arg%" ZIG_PRI_usize, arg_i);
}
ZigType *param_type = param_info->type;
if ((err = type_resolve(g, param_type, ResolveStatusSizeKnown))) {
return err;
}
fields.append({buf_ptr(param_name), param_type, 0});
}
@ -7538,7 +7545,9 @@ bool type_is_c_abi_int(CodeGen *g, ZigType *ty) {
uint32_t get_host_int_bytes(CodeGen *g, ZigType *struct_type, TypeStructField *field) {
assert(struct_type->id == ZigTypeIdStruct);
assert(type_is_resolved(struct_type, ResolveStatusSizeKnown));
if (struct_type->data.structure.layout != ContainerLayoutAuto) {
assert(type_is_resolved(struct_type, ResolveStatusSizeKnown));
}
if (struct_type->data.structure.host_int_bytes == nullptr)
return 0;
return struct_type->data.structure.host_int_bytes[field->gen_index];

View File

@ -17692,7 +17692,8 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct
{
size_t offset = ptr_field->data.x_ptr.data.base_array.elem_index;
uint64_t new_index = offset + index;
assert(new_index < ptr_field->data.x_ptr.data.base_array.array_val->type->data.array.len);
ir_assert(new_index < ptr_field->data.x_ptr.data.base_array.array_val->type->data.array.len,
&elem_ptr_instruction->base);
out_val->data.x_ptr.special = ConstPtrSpecialBaseArray;
out_val->data.x_ptr.data.base_array.array_val =
ptr_field->data.x_ptr.data.base_array.array_val;
@ -17854,7 +17855,10 @@ static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction
case OnePossibleValueNo:
break;
}
if ((err = type_resolve(ira->codegen, struct_type, ResolveStatusAlignmentKnown)))
ResolveStatus needed_resolve_status =
(struct_type->data.structure.layout == ContainerLayoutAuto) ?
ResolveStatusZeroBitsKnown : ResolveStatusSizeKnown;
if ((err = type_resolve(ira->codegen, struct_type, needed_resolve_status)))
return ira->codegen->invalid_instruction;
assert(struct_ptr->value.type->id == ZigTypeIdPointer);
uint32_t ptr_bit_offset = struct_ptr->value.type->data.pointer.bit_offset_in_host;
@ -17873,6 +17877,9 @@ static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction
return ira->codegen->invalid_instruction;
if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) {
if ((err = type_resolve(ira->codegen, struct_type, ResolveStatusSizeKnown)))
return ira->codegen->invalid_instruction;
ConstExprValue *struct_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node);
if (struct_val == nullptr)
return ira->codegen->invalid_instruction;
@ -17919,7 +17926,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_
Error err;
ZigType *bare_type = container_ref_type(container_type);
if ((err = type_resolve(ira->codegen, bare_type, ResolveStatusSizeKnown)))
if ((err = type_resolve(ira->codegen, bare_type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
assert(container_ptr->value.type->id == ZigTypeIdPointer);

View File

@ -162,9 +162,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ const obj = AstObject{ .lhsExpr = lhsExpr };
\\}
,
"tmp.zig:1:17: error: struct 'LhsExpr' depends on itself",
"tmp.zig:5:5: note: while checking this field",
"tmp.zig:4:19: error: union 'AstObject' depends on itself",
"tmp.zig:2:5: note: while checking this field",
"tmp.zig:5:5: note: while checking this field",
);
cases.add(