1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
|
// SPDX-License-Identifier: MPL-2.0
// (c) Hare authors <https://harelang.org>
// TODO:
// - Set name field
// - Figure out the portability mess that is this interface
use rt;
export type msghdr = struct {
native: rt::msghdr,
vectors: []rt::iovec,
control: []u8,
};
// Creates a new message header for advanced socket usage, with configurable I/O
// vectors, control messages, and other details, for use with [[sendmsg]] and
// [[recvmsg]].
//
// The user must call [[finish]] when they are done using this message for
// sending or receiving. The same message may be used for multiple operations
// before calling [[finish]]. [[reset]] may be used to "reset" a [[msghdr]] to
// an empty list of I/O vectors and control messages without freeing the
// underlying memory, which may be useful if future messages are expected to
// have similar characteristics.
export fn newmsg() msghdr = msghdr { ... };
// Frees resources associated with a [[msghdr]].
export fn finish(msg: *msghdr) void = {
free(msg.control);
free(msg.vectors);
};
// Resets a message header, clearing out any I/O vectors or control messages,
// without freeing the underlying memory. This allows the user to configure new
// vectors or control messages without a re-allocation, which improves
// performance if the new configuration fits into the same amount of memory.
export fn reset(msg: *msghdr) void = {
msg.control = msg.control[..0];
msg.vectors = msg.vectors[..0];
};
// Adds an I/O vector to the message.
export fn addvector(msg: *msghdr, vec: []u8) (void | nomem) = {
append(msg.vectors, rt::iovec {
iov_base = vec: *[*]u8,
iov_len = len(vec),
})?;
};
// Sets flags for this message.
export fn setflags(msg: *msghdr, flags: int) void = {
msg.native.msg_flags = flags;
};
// Get flags for this message.
export fn getflags(msg: *msghdr) int = {
return msg.native.msg_flags;
};
// Sets name for this message.
export fn setname(msg: *msghdr, name: *opaque, length: size) void = {
msg.native.msg_name = name;
msg.native.msg_namelen = length: u32;
};
// Adds a control message of the desired length to a [[msghdr]], returning a
// buffer in which the ancillary data may be written in a domain-specific
// format.
//
// This is a low-level interface, and is not generally used by users. More
// often, users will call functions like [[net::unix::addfiles]] or
// [[net::unix::allocfiles]], which provide a high-level interface to this
// function for domain-specific use-cases.
export fn addcontrol(
msg: *msghdr,
length: size,
level: int,
ctype: int,
) ([]u8 | nomem) = {
const prev = len(msg.control);
const space = cmsg_space(length);
append(msg.control, [0...], space)?;
let newbuf = msg.control[prev..prev + space]: *[*]rt::cmsghdr;
newbuf[0].cmsg_len = cmsg_len(length): uint;
newbuf[0].cmsg_level = level;
newbuf[0].cmsg_type = ctype;
let user = &newbuf[1]: *[*]u8;
return user[..length];
};
// Retrieves a control header from a message, returning a slice of
// domain-specific data.
//
// This is a low-level interface, and is not generally used by users. More
// often, users will call functions like [[net::unix::addfiles]] or
// [[net::unix::allocfiles]], which provide a high-level interface to this
// function for domain-specific use-cases.
export fn getcontrol(
msg: *msghdr,
length: size,
level: int,
ctype: int,
) ([]u8 | void) = {
let native = &msg.native;
let cbuf = native.msg_control: *[*]u8;
for (let i = 0z; i < native.msg_controllen) {
let next = &cbuf[i]: *rt::cmsg;
if (next.hdr.cmsg_len >= length
&& next.hdr.cmsg_level == level
&& next.hdr.cmsg_type == ctype) {
return next.cmsg_data[..length];
};
i += next.hdr.cmsg_len;
};
};
fn cmsg_align(z: size) size = (z + size(size) - 1) & ~(size(size) - 1);
fn cmsg_len(z: size) size = cmsg_align(size(rt::cmsghdr)) + z;
fn cmsg_space(z: size) size = cmsg_align(size(rt::cmsghdr)) + cmsg_align(z);
|