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