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