mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 21:08:36 +00:00
some work in progress networking code
also, casting to or from a u8 slice makes a function impure
This commit is contained in:
parent
d92ae20f45
commit
22888ca524
@ -4260,6 +4260,7 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
|
||||
(wanted_type->data.structure.fields[0].type_entry->data.pointer.is_const ||
|
||||
!actual_type->data.structure.fields[0].type_entry->data.pointer.is_const))
|
||||
{
|
||||
mark_impure_fn(context);
|
||||
return resolve_cast(g, context, node, expr_node, wanted_type, CastOpResizeSlice, true);
|
||||
}
|
||||
|
||||
|
||||
@ -6,6 +6,12 @@ const Allocator = mem.Allocator;
|
||||
const want_modification_safety = !@compile_var("is_release");
|
||||
const debug_u32 = if (want_modification_safety) u32 else void;
|
||||
|
||||
/*
|
||||
pub fn HashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b: K)->bool) {
|
||||
SmallHashMap(K, V, hash, eql, 8);
|
||||
}
|
||||
*/
|
||||
|
||||
pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b: K)->bool, STATIC_SIZE: isize) {
|
||||
entries: []Entry,
|
||||
size: isize,
|
||||
|
||||
@ -12,4 +12,3 @@ pub const mem = @import("mem.zig");
|
||||
pub fn assert(b: bool) {
|
||||
if (!b) unreachable{}
|
||||
}
|
||||
|
||||
|
||||
@ -312,13 +312,10 @@ fn restore_signals(set: &sigset_t) {
|
||||
}
|
||||
|
||||
|
||||
pub type sa_family_t = u16;
|
||||
pub type socklen_t = u32;
|
||||
pub type in_addr_t = u32;
|
||||
|
||||
export struct in_addr {
|
||||
s_addr: in_addr_t,
|
||||
}
|
||||
pub const sa_family_t = u16;
|
||||
pub const socklen_t = u32;
|
||||
pub const in_addr = u32;
|
||||
pub const in6_addr = [16]u8;
|
||||
|
||||
export struct sockaddr {
|
||||
family: sa_family_t,
|
||||
@ -341,15 +338,33 @@ export struct sockaddr_in6 {
|
||||
scope_id: u32,
|
||||
}
|
||||
|
||||
export struct in6_addr {
|
||||
addr: [16]u8,
|
||||
}
|
||||
|
||||
export struct iovec {
|
||||
iov_base: &u8,
|
||||
iov_len: usize,
|
||||
}
|
||||
|
||||
/*
|
||||
const IF_NAMESIZE = 16;
|
||||
|
||||
export struct ifreq {
|
||||
ifrn_name: [IF_NAMESIZE]u8,
|
||||
union {
|
||||
ifru_addr: sockaddr,
|
||||
ifru_dstaddr: sockaddr,
|
||||
ifru_broadaddr: sockaddr,
|
||||
ifru_netmask: sockaddr,
|
||||
ifru_hwaddr: sockaddr,
|
||||
ifru_flags: i16,
|
||||
ifru_ivalue: i32,
|
||||
ifru_mtu: i32,
|
||||
ifru_map: ifmap,
|
||||
ifru_slave: [IF_NAMESIZE]u8,
|
||||
ifru_newname: [IF_NAMESIZE]u8,
|
||||
ifru_data: &u8,
|
||||
} ifr_ifru;
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn getsockname(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t) -> isize {
|
||||
arch.syscall3(arch.SYS_getsockname, fd, isize(addr), isize(len))
|
||||
}
|
||||
@ -415,3 +430,33 @@ pub fn socketpair(domain: i32, socket_type: i32, protocol: i32, fd: [2]i32) -> i
|
||||
pub fn accept4(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t, flags: i32) -> isize {
|
||||
arch.syscall4(arch.SYS_accept4, fd, isize(addr), isize(len), flags)
|
||||
}
|
||||
|
||||
/*
|
||||
pub error NameTooLong;
|
||||
pub error SystemResources;
|
||||
pub error Io;
|
||||
|
||||
pub fn if_nametoindex(name: []u8) -> %u32 {
|
||||
var ifr: ifreq = undefined;
|
||||
|
||||
if (name.len >= ifr.ifr_name.len) {
|
||||
return error.NameTooLong;
|
||||
}
|
||||
|
||||
const socket_ret = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
|
||||
const socket_err = get_errno(socket_ret);
|
||||
if (socket_err > 0) {
|
||||
return error.SystemResources;
|
||||
}
|
||||
const socket_fd = i32(socket_ret);
|
||||
@memcpy(&ifr.ifr_name[0], &name[0], name.len);
|
||||
ifr.ifr_name[name.len] = 0;
|
||||
const ioctl_ret = ioctl(socket_fd, SIOCGIFINDEX, &ifr);
|
||||
close(socket_fd);
|
||||
const ioctl_err = get_errno(ioctl_ret);
|
||||
if (ioctl_err > 0) {
|
||||
return error.Io;
|
||||
}
|
||||
return ifr.ifr_ifindex;
|
||||
}
|
||||
*/
|
||||
|
||||
@ -3,8 +3,8 @@ const mem = @import("mem.zig");
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
/*
|
||||
fn List(T: type) -> type {
|
||||
List(T, 8)
|
||||
pub fn List(T: type) -> type {
|
||||
SmallList(T, 8)
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
288
std/net.zig
288
std/net.zig
@ -1,9 +1,11 @@
|
||||
const linux = @import("linux.zig");
|
||||
const errno = @import("errno.zig");
|
||||
const assert = @import("index.zig").assert;
|
||||
|
||||
pub error SigInterrupt;
|
||||
pub error Unexpected;
|
||||
pub error Io;
|
||||
pub error TimedOut;
|
||||
|
||||
struct Connection {
|
||||
socket_fd: i32,
|
||||
@ -20,17 +22,37 @@ struct Connection {
|
||||
}
|
||||
|
||||
struct Address {
|
||||
addr: linux.sockaddr,
|
||||
family: u16,
|
||||
scope_id: u32,
|
||||
addr: [16]u8,
|
||||
sort_key: i32,
|
||||
}
|
||||
|
||||
pub fn lookup(hostname: []const u8, out_addrs: []Address) -> %[]Address {
|
||||
if (hostname.len == 0) {
|
||||
|
||||
/*
|
||||
if (family != AF_INET6)
|
||||
buf[cnt++] = (struct address){ .family = AF_INET, .addr = { 127,0,0,1 } };
|
||||
if (family != AF_INET)
|
||||
buf[cnt++] = (struct address){ .family = AF_INET6, .addr = { [15] = 1 } };
|
||||
*/
|
||||
unreachable{} // TODO
|
||||
}
|
||||
|
||||
switch (parse_ip_literal(hostname)) {
|
||||
Ok => |addr| {
|
||||
out_addrs[0] = addr;
|
||||
return out_addrs[0...1];
|
||||
},
|
||||
else => {},
|
||||
};
|
||||
|
||||
unreachable{} // TODO
|
||||
}
|
||||
|
||||
pub fn connect_addr(addr: &Address, port: u16) -> %Connection {
|
||||
addr.addr.port = port;
|
||||
|
||||
const socket_ret = linux.socket(linux.PF_INET, linux.SOCK_STREAM, linux.PROTO_tcp);
|
||||
const socket_ret = linux.socket(addr.family, linux.SOCK_STREAM, linux.PROTO_tcp);
|
||||
const socket_err = linux.get_errno(socket_ret);
|
||||
if (socket_err > 0) {
|
||||
// TODO figure out possible errors from socket()
|
||||
@ -38,10 +60,33 @@ pub fn connect_addr(addr: &Address, port: u16) -> %Connection {
|
||||
}
|
||||
const socket_fd = i32(socket_ret);
|
||||
|
||||
const connect_err = linux.get_errno(linux.connect(socket_fd, &addr.addr, @sizeof(linux.sockaddr)));
|
||||
const connect_ret = if (addr.family == linux.AF_INET) {
|
||||
var os_addr: linux.sockaddr_in = undefined;
|
||||
os_addr.family = addr.family;
|
||||
os_addr.port = host_to_be(u16)(port);
|
||||
@memcpy((&u8)(&os_addr.addr), &addr.addr[0], 4);
|
||||
@memset(&os_addr.zero, 0, @sizeof(@typeof(os_addr.zero)));
|
||||
linux.connect(socket_fd, (&linux.sockaddr)(&os_addr), @sizeof(linux.sockaddr_in))
|
||||
} else if (addr.family == linux.AF_INET6) {
|
||||
var os_addr: linux.sockaddr_in6 = undefined;
|
||||
os_addr.family = addr.family;
|
||||
os_addr.port = host_to_be(u16)(port);
|
||||
os_addr.flowinfo = 0;
|
||||
os_addr.scope_id = addr.scope_id;
|
||||
@memcpy(&os_addr.addr[0], &addr.addr[0], 16);
|
||||
linux.connect(socket_fd, (&linux.sockaddr)(&os_addr), @sizeof(linux.sockaddr_in6))
|
||||
} else {
|
||||
unreachable{}
|
||||
};
|
||||
const connect_err = linux.get_errno(connect_ret);
|
||||
if (connect_err > 0) {
|
||||
// TODO figure out possible errors from connect()
|
||||
return error.Unexpected;
|
||||
switch (connect_err) {
|
||||
errno.ETIMEDOUT => return error.TimedOut,
|
||||
else => {
|
||||
// TODO figure out possible errors from connect()
|
||||
return error.Unexpected;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return Connection {
|
||||
@ -56,3 +101,232 @@ pub fn connect(hostname: []const u8, port: u16) -> %Connection {
|
||||
|
||||
return connect_addr(main_addr, port);
|
||||
}
|
||||
|
||||
pub error InvalidIpLiteral;
|
||||
|
||||
pub fn parse_ip_literal(buf: []const u8) -> %Address {
|
||||
switch (parse_ip4(buf)) {
|
||||
Ok => |ip4| {
|
||||
var result: Address = undefined;
|
||||
@memcpy(&result.addr[0], (&u8)(&ip4), @sizeof(u32));
|
||||
result.family = linux.AF_INET;
|
||||
result.scope_id = 0;
|
||||
return result;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
switch (parse_ip6(buf)) {
|
||||
Ok => |addr| {
|
||||
return addr;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
return error.InvalidIpLiteral;
|
||||
}
|
||||
|
||||
fn hex_digit(c: u8) -> u8 {
|
||||
// TODO use switch with range
|
||||
if ('0' <= c && c <= '9') {
|
||||
c - '0'
|
||||
} else if ('A' <= c && c <= 'Z') {
|
||||
c - 'A' + 10
|
||||
} else if ('a' <= c && c <= 'z') {
|
||||
c - 'a' + 10
|
||||
} else {
|
||||
@max_value(u8)
|
||||
}
|
||||
}
|
||||
|
||||
error InvalidChar;
|
||||
error Overflow;
|
||||
error JunkAtEnd;
|
||||
error Incomplete;
|
||||
|
||||
#static_eval_enable(false)
|
||||
fn parse_ip6(buf: []const u8) -> %Address {
|
||||
var result: Address = undefined;
|
||||
result.family = linux.AF_INET6;
|
||||
result.scope_id = 0;
|
||||
const ip_slice = result.addr[0...];
|
||||
|
||||
var x: u16 = 0;
|
||||
var saw_any_digits = false;
|
||||
var index: u8 = 0;
|
||||
var scope_id = false;
|
||||
for (buf) |c| {
|
||||
if (scope_id) {
|
||||
if (c >= '0' && c <= '9') {
|
||||
const digit = c - '0';
|
||||
if (@mul_with_overflow(u32, result.scope_id, 10, &result.scope_id)) {
|
||||
return error.Overflow;
|
||||
}
|
||||
if (@add_with_overflow(u32, result.scope_id, digit, &result.scope_id)) {
|
||||
return error.Overflow;
|
||||
}
|
||||
} else {
|
||||
return error.InvalidChar;
|
||||
}
|
||||
} else if (c == ':') {
|
||||
if (!saw_any_digits) {
|
||||
return error.InvalidChar;
|
||||
}
|
||||
if (index == 14) {
|
||||
return error.JunkAtEnd;
|
||||
}
|
||||
ip_slice[index] = @truncate(u8, x >> 8);
|
||||
index += 1;
|
||||
ip_slice[index] = @truncate(u8, x);
|
||||
index += 1;
|
||||
|
||||
x = 0;
|
||||
saw_any_digits = false;
|
||||
} else if (c == '%') {
|
||||
if (!saw_any_digits) {
|
||||
return error.InvalidChar;
|
||||
}
|
||||
if (index == 14) {
|
||||
ip_slice[index] = @truncate(u8, x >> 8);
|
||||
index += 1;
|
||||
ip_slice[index] = @truncate(u8, x);
|
||||
index += 1;
|
||||
}
|
||||
scope_id = true;
|
||||
saw_any_digits = false;
|
||||
} else {
|
||||
const digit = hex_digit(c);
|
||||
if (digit == @max_value(u8)) {
|
||||
return error.InvalidChar;
|
||||
}
|
||||
if (@mul_with_overflow(u16, x, 16, &x)) {
|
||||
return error.Overflow;
|
||||
}
|
||||
if (@add_with_overflow(u16, x, digit, &x)) {
|
||||
return error.Overflow;
|
||||
}
|
||||
saw_any_digits = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!saw_any_digits) {
|
||||
return error.Incomplete;
|
||||
}
|
||||
|
||||
/*
|
||||
if (p) {
|
||||
if (isdigit(*++p)) scopeid = strtoull(p, &z, 10);
|
||||
else z = p-1;
|
||||
if (*z) {
|
||||
if (!IN6_IS_ADDR_LINKLOCAL(&a6) &&
|
||||
!IN6_IS_ADDR_MC_LINKLOCAL(&a6))
|
||||
return EAI_NONAME;
|
||||
scopeid = if_nametoindex(p);
|
||||
if (!scopeid) return EAI_NONAME;
|
||||
}
|
||||
if (scopeid > UINT_MAX) return EAI_NONAME;
|
||||
}
|
||||
*/
|
||||
|
||||
if (scope_id) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (index == 14) {
|
||||
ip_slice[14] = @truncate(u8, x >> 8);
|
||||
ip_slice[15] = @truncate(u8, x);
|
||||
return result;
|
||||
}
|
||||
|
||||
return error.Incomplete;
|
||||
}
|
||||
|
||||
fn parse_ip4(buf: []const u8) -> %u32 {
|
||||
var result: u32 = undefined;
|
||||
const out_ptr = ([]u8)((&result)[0...1]);
|
||||
|
||||
var x: u8 = 0;
|
||||
var index: u8 = 0;
|
||||
var saw_any_digits = false;
|
||||
for (buf) |c| {
|
||||
if (c == '.') {
|
||||
if (!saw_any_digits) {
|
||||
return error.InvalidChar;
|
||||
}
|
||||
if (index == 3) {
|
||||
return error.JunkAtEnd;
|
||||
}
|
||||
out_ptr[index] = x;
|
||||
index += 1;
|
||||
x = 0;
|
||||
saw_any_digits = false;
|
||||
} else if (c >= '0' && c <= '9') {
|
||||
saw_any_digits = true;
|
||||
const digit = c - '0';
|
||||
if (@mul_with_overflow(u8, x, 10, &x)) {
|
||||
return error.Overflow;
|
||||
}
|
||||
if (@add_with_overflow(u8, x, digit, &x)) {
|
||||
return error.Overflow;
|
||||
}
|
||||
} else {
|
||||
return error.InvalidChar;
|
||||
}
|
||||
}
|
||||
if (index == 3 && saw_any_digits) {
|
||||
out_ptr[index] = x;
|
||||
return result;
|
||||
}
|
||||
|
||||
return error.Incomplete;
|
||||
}
|
||||
|
||||
|
||||
#attribute("test")
|
||||
fn test_parse_ip4() {
|
||||
assert(%%parse_ip4("127.0.0.1") == be_to_host(u32)(0x7f000001));
|
||||
switch (parse_ip4("256.0.0.1")) { Overflow => {}, else => unreachable {}, }
|
||||
switch (parse_ip4("x.0.0.1")) { InvalidChar => {}, else => unreachable {}, }
|
||||
switch (parse_ip4("127.0.0.1.1")) { JunkAtEnd => {}, else => unreachable {}, }
|
||||
switch (parse_ip4("127.0.0.")) { Incomplete => {}, else => unreachable {}, }
|
||||
switch (parse_ip4("100..0.1")) { InvalidChar => {}, else => unreachable {}, }
|
||||
}
|
||||
|
||||
#attribute("test")
|
||||
fn test_parse_ip6() {
|
||||
{
|
||||
const addr = %%parse_ip6("FF01:0:0:0:0:0:0:FB");
|
||||
assert(addr.addr[0] == 0xff);
|
||||
assert(addr.addr[1] == 0x01);
|
||||
assert(addr.addr[2] == 0x00);
|
||||
}
|
||||
}
|
||||
|
||||
#attribute("test")
|
||||
fn test_lookup_simple_ip() {
|
||||
{
|
||||
var addrs_buf: [5]Address = undefined;
|
||||
const addrs = %%lookup("192.168.1.1", addrs_buf);
|
||||
assert(addrs.len == 1);
|
||||
const addr = addrs[0];
|
||||
assert(addr.family == linux.AF_INET);
|
||||
assert(addr.addr[0] == 192);
|
||||
assert(addr.addr[1] == 168);
|
||||
assert(addr.addr[2] == 1);
|
||||
assert(addr.addr[3] == 1);
|
||||
}
|
||||
}
|
||||
|
||||
const be_to_host = host_to_be;
|
||||
fn host_to_be(T: type)(x: T) -> T {
|
||||
if (@compile_var("is_big_endian")) x else endian_swap(T)(x)
|
||||
}
|
||||
|
||||
fn endian_swap(T: type)(x: T) -> T {
|
||||
const x_slice = ([]u8)((&const x)[0...1]);
|
||||
var result: T = undefined;
|
||||
const result_slice = ([]u8)((&result)[0...1]);
|
||||
for (result_slice) |*b, i| {
|
||||
*b = x_slice[@sizeof(T) - i - 1];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user