Move Bindable to ContainerizationExtras, fix binding bugs. (#481)

- Bindable is generally useful, so it makes sense to move it out of the
netlink library. Removed Equatable conformance from Bindable as this
isn't generally necessary (it's only used for unit tests in netlink).
- Fix some offset checks in a couple data binding functions and improve
the unit tests.
This commit is contained in:
J Logan
2026-01-21 15:34:10 -08:00
committed by GitHub
parent c4f1263594
commit 8da5f2306d
10 changed files with 701 additions and 164 deletions
@@ -18,11 +18,31 @@
public struct IPv4Address: Sendable, Hashable, CustomStringConvertible, Equatable, Comparable {
public let value: UInt32
/// Creates an IPv4Address from an unsigned integer.
///
/// - Parameter string: The integer representation of the address.
@inlinable
public init(_ value: UInt32) {
self.value = value
}
/// Creates an IPv4Address from 4 bytes.
///
/// - Parameters:
/// - bytes: 4-byte array in network byte order representing the IPv4 address
/// - Throws: `AddressError.unableToParse` if the byte array length is not 4
@inlinable
public init(_ bytes: [UInt8]) throws {
guard bytes.count == 4 else {
throw AddressError.unableToParse
}
self.value =
(UInt32(bytes[0]) << 24)
| (UInt32(bytes[1]) << 16)
| (UInt32(bytes[2]) << 16)
| UInt32(bytes[3])
}
/// Creates an IPv4Address from a string representation.
///
/// - Parameter string: The IPv4 address string in dotted decimal notation (e.g., "192.168.1.1")
@@ -71,12 +71,12 @@ extension IPv6Address {
if remainingAddress.isEmpty {
// If we have IPv4 suffix, ipBytes already has the IPv4 data, just return
if hasIPv4Suffix {
return Self(ipBytes, zone: zone)
return try Self(ipBytes, zone: zone)
}
// Pure "::" - Return the unspecified address, handling zone identifiers
if let zone = zone, !zone.isEmpty {
return Self(ipBytes, zone: zone)
return try Self(ipBytes, zone: zone)
}
return .unspecified
}
@@ -33,11 +33,14 @@ public struct IPv6Address: Sendable, Hashable, CustomStringConvertible, Equatabl
/// Creates an IPv6Address from 16 bytes.
///
/// - Parameters:
/// - bytes: 16-byte array representing the IPv6 address
/// - bytes: 16-byte array in network byte order representing the IPv6 address
/// - zone: Optional zone identifier (e.g., "eth0")
/// - Throws: `AddressError.unableToParse` if the byte array length is not 16
@inlinable
public init(_ bytes: [UInt8], zone: String? = nil) {
precondition(bytes.count == 16, "IPv6 address must be exactly 16 bytes")
public init(_ bytes: [UInt8], zone: String? = nil) throws {
guard bytes.count == 16 else {
throw AddressError.unableToParse
}
// Build UInt128 value in chunks to avoid compiler complexity
let hh =
@@ -30,6 +30,25 @@ public struct MACAddress: Sendable, Hashable, CustomStringConvertible, Equatable
self.value = value & 0x0000_ffff_ffff_ffff
}
/// Creates an IPv4Address from 6 bytes.
///
/// - Parameters:
/// - bytes: 6-byte array in network byte order representing the IPv4 address
/// - Throws: `AddressError.unableToParse` if the byte array length is not 6
@inlinable
public init(_ bytes: [UInt8]) throws {
guard bytes.count == 6 else {
throw AddressError.unableToParse
}
self.value =
(UInt64(bytes[0]) << 40)
| (UInt64(bytes[1]) << 32)
| (UInt64(bytes[2]) << 24)
| (UInt64(bytes[3]) << 16)
| (UInt64(bytes[4]) << 8)
| UInt64(bytes[5])
}
/// Creates an MACAddress from a string representation.
///
/// - Parameter string: The MAC address string with colon or dash delimiters.
@@ -225,9 +244,9 @@ public struct MACAddress: Sendable, Hashable, CustomStringConvertible, Equatable
/// - Parameter network: The IPv6 address to use for the network prefix
/// - Returns: The link local IP address for the MAC address
@inlinable
public func ipv6Address(network: IPv6Address) -> IPv6Address {
public func ipv6Address(network: IPv6Address) throws -> IPv6Address {
let prefixBytes = network.bytes
return IPv6Address([
return try IPv6Address([
prefixBytes[0], prefixBytes[1], prefixBytes[2], prefixBytes[3],
prefixBytes[4], prefixBytes[5], prefixBytes[6], prefixBytes[7],
bytes[0] ^ 0x02, bytes[1], bytes[2], 0xff,
@@ -14,6 +14,28 @@
// limitations under the License.
//===----------------------------------------------------------------------===//
import Foundation
package enum BindError: Error, CustomStringConvertible {
case recvMarshalFailure(type: String, field: String)
case sendMarshalFailure(type: String, field: String)
package var description: String {
switch self {
case .recvMarshalFailure(let type, let field):
return "failed to unmarshal \(type).\(field)"
case .sendMarshalFailure(let type, let field):
return "failed to marshal \(type).\(field)"
}
}
}
package protocol Bindable: Sendable {
static var size: Int { get }
func appendBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int
mutating func bindBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int
}
extension ArraySlice<UInt8> {
package func hexEncodedString() -> String {
self.map { String(format: "%02hhx", $0) }.joined()
@@ -35,7 +57,7 @@ extension [UInt8] {
package mutating func copyIn<T>(as type: T.Type, value: T, offset: Int = 0, size: Int? = nil) -> Int? {
let size = size ?? MemoryLayout<T>.size
guard self.count >= size - offset else {
guard self.count >= size + offset else {
return nil
}
@@ -45,12 +67,12 @@ extension [UInt8] {
}
}
package mutating func copyOut<T>(as type: T.Type, offset: Int = 0, size: Int? = nil) -> (Int, T)? {
guard self.count >= (size ?? MemoryLayout<T>.size) - offset else {
package func copyOut<T>(as type: T.Type, offset: Int = 0, size: Int? = nil) -> (Int, T)? {
guard self.count >= (size ?? MemoryLayout<T>.size) + offset else {
return nil
}
return self.withUnsafeMutableBytes {
return self.withUnsafeBytes {
guard let value = $0.baseAddress?.advanced(by: offset).assumingMemoryBound(to: T.self).pointee else {
return nil
}
@@ -67,7 +89,7 @@ extension [UInt8] {
return offset + buffer.count
}
package mutating func copyOut(buffer: inout [UInt8], offset: Int = 0) -> Int? {
package func copyOut(buffer: inout [UInt8], offset: Int = 0) -> Int? {
guard offset + buffer.count <= self.count else {
return nil
}
@@ -102,7 +102,7 @@ public struct NetlinkSession {
let newRequestOffset =
requestBuffer.copyIn(as: UInt32.self, value: m, offset: requestOffset)
else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RTAttribute", field: "IFLA_MTU")
}
requestOffset = newRequestOffset
}
@@ -162,7 +162,7 @@ public struct NetlinkSession {
value: filters,
offset: requestOffset)
else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RTAttribute", field: "IFLA_EXT_MASK")
}
if let interfaceNameAttr {
@@ -170,7 +170,7 @@ public struct NetlinkSession {
requestOffset = try interfaceNameAttr.appendBuffer(&requestBuffer, offset: requestOffset)
guard let updatedRequestOffset = requestBuffer.copyIn(buffer: interfaceName, offset: requestOffset)
else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RTAttribute", field: "IFLA_IFNAME")
}
requestOffset = updatedRequestOffset
}
@@ -239,13 +239,13 @@ public struct NetlinkSession {
let ipLocalAttr = RTAttribute(len: UInt16(addressAttrSize), type: AddressAttributeType.IFA_LOCAL)
requestOffset = try ipLocalAttr.appendBuffer(&requestBuffer, offset: requestOffset)
guard var requestOffset = requestBuffer.copyIn(buffer: ipAddressBytes, offset: requestOffset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RTAttribute", field: "IFA_LOCAL")
}
let ipAddressAttr = RTAttribute(len: UInt16(addressAttrSize), type: AddressAttributeType.IFA_ADDRESS)
requestOffset = try ipAddressAttr.appendBuffer(&requestBuffer, offset: requestOffset)
guard let requestOffset = requestBuffer.copyIn(buffer: ipAddressBytes, offset: requestOffset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RTAttribute", field: "IFA_ADDRESS")
}
guard requestOffset == requestSize else {
@@ -305,13 +305,13 @@ public struct NetlinkSession {
let dstAddrAttr = RTAttribute(len: UInt16(dstAddrAttrSize), type: RouteAttributeType.DST)
requestOffset = try dstAddrAttr.appendBuffer(&requestBuffer, offset: requestOffset)
guard var requestOffset = requestBuffer.copyIn(buffer: dstAddrBytes, offset: requestOffset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RTAttribute", field: "RTA_DST")
}
let srcAddrAttr = RTAttribute(len: UInt16(dstAddrAttrSize), type: RouteAttributeType.PREFSRC)
requestOffset = try srcAddrAttr.appendBuffer(&requestBuffer, offset: requestOffset)
guard var requestOffset = requestBuffer.copyIn(buffer: srcAddrBytes, offset: requestOffset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RTAttribute", field: "RTA_PREFSRC")
}
let interfaceAttr = RTAttribute(len: UInt16(interfaceAttrSize), type: RouteAttributeType.OIF)
@@ -322,7 +322,7 @@ public struct NetlinkSession {
value: UInt32(interfaceIndex),
offset: requestOffset)
else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RTAttribute", field: "RTA_OIF")
}
guard requestOffset == requestSize else {
@@ -378,7 +378,7 @@ public struct NetlinkSession {
let dstAddrAttr = RTAttribute(len: UInt16(dstAddrAttrSize), type: RouteAttributeType.GATEWAY)
requestOffset = try dstAddrAttr.appendBuffer(&requestBuffer, offset: requestOffset)
guard var requestOffset = requestBuffer.copyIn(buffer: dstAddrBytes, offset: requestOffset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RTAttribute", field: "RTA_GATEWAY")
}
let interfaceAttr = RTAttribute(len: UInt16(interfaceAttrSize), type: RouteAttributeType.OIF)
requestOffset = try interfaceAttr.appendBuffer(&requestBuffer, offset: requestOffset)
@@ -388,7 +388,7 @@ public struct NetlinkSession {
value: UInt32(interfaceIndex),
offset: requestOffset)
else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RTAttribute", field: "RTA_OIF")
}
guard requestOffset == requestSize else {
@@ -404,7 +404,7 @@ public struct NetlinkSession {
private func getInterfaceName(_ interface: String) throws -> [UInt8] {
guard let interfaceNameData = interface.data(using: .utf8) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "String", field: "interface")
}
var interfaceName = [UInt8](interfaceNameData)
@@ -503,7 +503,7 @@ public struct NetlinkSession {
private func parseErrorCode(buffer: inout [UInt8], offset: Int) throws -> (Int32, Int) {
guard let errorPtr = buffer.bind(as: Int32.self, offset: offset) else {
throw NetlinkDataError.recvUnmarshalFailure
throw BindError.recvMarshalFailure(type: "NetlinkErrorMessage", field: "error")
}
let rc = errorPtr.pointee
+129 -133
View File
@@ -135,17 +135,11 @@ struct RouteAttributeType {
static let PREFSRC: UInt16 = 7
}
protocol Bindable: Equatable {
static var size: Int { get }
func appendBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int
mutating func bindBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int
}
struct SockaddrNetlink: Bindable {
struct SockaddrNetlink: Bindable, Equatable {
static let size = 12
var family: UInt16
var pad: UInt16 = 0
var _pad: UInt16 = 0
var pid: UInt32
var groups: UInt32
@@ -157,13 +151,16 @@ struct SockaddrNetlink: Bindable {
func appendBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {
guard let offset = buffer.copyIn(as: UInt16.self, value: family, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "SockaddrNetlink", field: "family")
}
guard let offset = buffer.copyIn(as: UInt16.self, value: 0, offset: offset) else {
throw BindError.sendMarshalFailure(type: "SockaddrNetlink", field: "_pad")
}
guard let offset = buffer.copyIn(as: UInt32.self, value: pid, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "SockaddrNetlink", field: "pid")
}
guard let offset = buffer.copyIn(as: UInt32.self, value: groups, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "SockaddrNetlink", field: "groups")
}
return offset
@@ -171,25 +168,30 @@ struct SockaddrNetlink: Bindable {
mutating func bindBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {
guard let (offset, value) = buffer.copyOut(as: UInt16.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "SockaddrNetlink", field: "family")
}
family = value
guard let (offset, value) = buffer.copyOut(as: UInt16.self, offset: offset) else {
throw BindError.recvMarshalFailure(type: "SockaddrNetlink", field: "_pad")
}
_pad = value
guard let (offset, value) = buffer.copyOut(as: UInt32.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "SockaddrNetlink", field: "pid")
}
pid = value
guard let (offset, value) = buffer.copyOut(as: UInt32.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "SockaddrNetlink", field: "groups")
}
groups = value
return offset + Self.size
return offset
}
}
struct NetlinkMessageHeader: Bindable {
struct NetlinkMessageHeader: Bindable, Equatable {
static let size = 16
var len: UInt32
@@ -208,19 +210,19 @@ struct NetlinkMessageHeader: Bindable {
func appendBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {
guard let offset = buffer.copyIn(as: UInt32.self, value: len, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "NetlinkMessageHeader", field: "len")
}
guard let offset = buffer.copyIn(as: UInt16.self, value: type, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "NetlinkMessageHeader", field: "type")
}
guard let offset = buffer.copyIn(as: UInt16.self, value: flags, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "NetlinkMessageHeader", field: "flags")
}
guard let offset = buffer.copyIn(as: UInt32.self, value: seq, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "NetlinkMessageHeader", field: "seq")
}
guard let offset = buffer.copyIn(as: UInt32.self, value: pid, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "NetlinkMessageHeader", field: "pid")
}
return offset
@@ -228,27 +230,27 @@ struct NetlinkMessageHeader: Bindable {
mutating func bindBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {
guard let (offset, value) = buffer.copyOut(as: UInt32.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "NetlinkMessageHeader", field: "len")
}
len = value
guard let (offset, value) = buffer.copyOut(as: UInt16.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "NetlinkMessageHeader", field: "type")
}
type = value
guard let (offset, value) = buffer.copyOut(as: UInt16.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "NetlinkMessageHeader", field: "flags")
}
flags = value
guard let (offset, value) = buffer.copyOut(as: UInt32.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "NetlinkMessageHeader", field: "seq")
}
seq = value
guard let (offset, value) = buffer.copyOut(as: UInt32.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "NetlinkMessageHeader", field: "pid")
}
pid = value
@@ -262,7 +264,7 @@ struct NetlinkMessageHeader: Bindable {
}
}
struct InterfaceInfo: Bindable {
struct InterfaceInfo: Bindable, Equatable {
static let size = 16
var family: UInt8
@@ -285,22 +287,22 @@ struct InterfaceInfo: Bindable {
func appendBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {
guard let offset = buffer.copyIn(as: UInt8.self, value: family, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "InterfaceInfo", field: "family")
}
guard let offset = buffer.copyIn(as: UInt8.self, value: _pad, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "InterfaceInfo", field: "_pad")
}
guard let offset = buffer.copyIn(as: UInt16.self, value: type, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "InterfaceInfo", field: "type")
}
guard let offset = buffer.copyIn(as: Int32.self, value: index, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "InterfaceInfo", field: "index")
}
guard let offset = buffer.copyIn(as: UInt32.self, value: flags, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "InterfaceInfo", field: "flags")
}
guard let offset = buffer.copyIn(as: UInt32.self, value: change, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "InterfaceInfo", field: "change")
}
return offset
@@ -308,32 +310,32 @@ struct InterfaceInfo: Bindable {
mutating func bindBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {
guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "InterfaceInfo", field: "family")
}
family = value
guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "InterfaceInfo", field: "_pad")
}
_pad = value
guard let (offset, value) = buffer.copyOut(as: UInt16.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "InterfaceInfo", field: "type")
}
type = value
guard let (offset, value) = buffer.copyOut(as: Int32.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "InterfaceInfo", field: "index")
}
index = value
guard let (offset, value) = buffer.copyOut(as: UInt32.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "InterfaceInfo", field: "flags")
}
flags = value
guard let (offset, value) = buffer.copyOut(as: UInt32.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "InterfaceInfo", field: "change")
}
change = value
@@ -341,7 +343,7 @@ struct InterfaceInfo: Bindable {
}
}
struct AddressInfo: Bindable {
struct AddressInfo: Bindable, Equatable {
static let size = 8
var family: UInt8
@@ -363,19 +365,19 @@ struct AddressInfo: Bindable {
func appendBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {
guard let offset = buffer.copyIn(as: UInt8.self, value: family, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "AddressInfo", field: "family")
}
guard let offset = buffer.copyIn(as: UInt8.self, value: prefixLength, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "AddressInfo", field: "prefixLength")
}
guard let offset = buffer.copyIn(as: UInt8.self, value: flags, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "AddressInfo", field: "flags")
}
guard let offset = buffer.copyIn(as: UInt8.self, value: scope, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "AddressInfo", field: "scope")
}
guard let offset = buffer.copyIn(as: UInt32.self, value: index, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "AddressInfo", field: "index")
}
return offset
@@ -383,27 +385,27 @@ struct AddressInfo: Bindable {
mutating func bindBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {
guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "AddressInfo", field: "family")
}
family = value
guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "AddressInfo", field: "prefixLength")
}
prefixLength = value
guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "AddressInfo", field: "flags")
}
flags = value
guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "AddressInfo", field: "scope")
}
scope = value
guard let (offset, value) = buffer.copyOut(as: UInt32.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "AddressInfo", field: "index")
}
index = value
@@ -411,7 +413,7 @@ struct AddressInfo: Bindable {
}
}
struct RouteInfo: Bindable {
struct RouteInfo: Bindable, Equatable {
static let size = 12
var family: UInt8
@@ -448,31 +450,31 @@ struct RouteInfo: Bindable {
func appendBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {
guard let offset = buffer.copyIn(as: UInt8.self, value: family, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RouteInfo", field: "family")
}
guard let offset = buffer.copyIn(as: UInt8.self, value: dstLen, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RouteInfo", field: "dstLen")
}
guard let offset = buffer.copyIn(as: UInt8.self, value: srcLen, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RouteInfo", field: "srcLen")
}
guard let offset = buffer.copyIn(as: UInt8.self, value: tos, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RouteInfo", field: "tos")
}
guard let offset = buffer.copyIn(as: UInt8.self, value: table, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RouteInfo", field: "table")
}
guard let offset = buffer.copyIn(as: UInt8.self, value: proto, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RouteInfo", field: "proto")
}
guard let offset = buffer.copyIn(as: UInt8.self, value: scope, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RouteInfo", field: "scope")
}
guard let offset = buffer.copyIn(as: UInt8.self, value: type, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RouteInfo", field: "type")
}
guard let offset = buffer.copyIn(as: UInt32.self, value: flags, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RouteInfo", field: "flags")
}
return offset
@@ -480,47 +482,47 @@ struct RouteInfo: Bindable {
mutating func bindBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {
guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "RouteInfo", field: "family")
}
family = value
guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "RouteInfo", field: "dstLen")
}
dstLen = value
guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "RouteInfo", field: "srcLen")
}
srcLen = value
guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "RouteInfo", field: "tos")
}
tos = value
guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "RouteInfo", field: "table")
}
table = value
guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "RouteInfo", field: "proto")
}
proto = value
guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "RouteInfo", field: "scope")
}
scope = value
guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "RouteInfo", field: "type")
}
type = value
guard let (offset, value) = buffer.copyOut(as: UInt32.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "RouteInfo", field: "flags")
}
flags = value
@@ -529,8 +531,8 @@ struct RouteInfo: Bindable {
}
/// A route information.
public struct RTAttribute: Bindable {
static let size = 4
public struct RTAttribute: Bindable, Equatable {
package static let size = 4
public var len: UInt16
public var type: UInt16
@@ -541,25 +543,25 @@ public struct RTAttribute: Bindable {
self.type = type
}
func appendBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {
package func appendBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {
guard let offset = buffer.copyIn(as: UInt16.self, value: len, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RTAttribute", field: "len")
}
guard let offset = buffer.copyIn(as: UInt16.self, value: type, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "RTAttribute", field: "type")
}
return offset
}
mutating func bindBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {
package mutating func bindBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {
guard let (offset, value) = buffer.copyOut(as: UInt16.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "RTAttribute", field: "len")
}
len = value
guard let (offset, value) = buffer.copyOut(as: UInt16.self, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.recvMarshalFailure(type: "RTAttribute", field: "type")
}
type = value
@@ -612,8 +614,8 @@ public struct LinkResponse {
}
/// Network interface statistics (64-bit version)
public struct LinkStatistics64: Bindable {
static let size = 23 * 8
public struct LinkStatistics64: Bindable, Equatable {
package static let size = 23 * 8
public var rxPackets: UInt64
public var txPackets: UInt64
@@ -665,193 +667,193 @@ public struct LinkStatistics64: Bindable {
self.txCompressed = 0
}
func appendBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {
package func appendBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {
guard let offset = buffer.copyIn(as: UInt64.self, value: rxPackets, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "LinkStatistics64", field: "rxPackets")
}
guard let offset = buffer.copyIn(as: UInt64.self, value: txPackets, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "LinkStatistics64", field: "txPackets")
}
guard let offset = buffer.copyIn(as: UInt64.self, value: rxBytes, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "LinkStatistics64", field: "rxBytes")
}
guard let offset = buffer.copyIn(as: UInt64.self, value: txBytes, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "LinkStatistics64", field: "txBytes")
}
guard let offset = buffer.copyIn(as: UInt64.self, value: rxErrors, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "LinkStatistics64", field: "rxErrors")
}
guard let offset = buffer.copyIn(as: UInt64.self, value: txErrors, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "LinkStatistics64", field: "txErrors")
}
guard let offset = buffer.copyIn(as: UInt64.self, value: rxDropped, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "LinkStatistics64", field: "rxDropped")
}
guard let offset = buffer.copyIn(as: UInt64.self, value: txDropped, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "LinkStatistics64", field: "txDropped")
}
guard let offset = buffer.copyIn(as: UInt64.self, value: multicast, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "LinkStatistics64", field: "multicast")
}
guard let offset = buffer.copyIn(as: UInt64.self, value: collisions, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "LinkStatistics64", field: "collisions")
}
guard let offset = buffer.copyIn(as: UInt64.self, value: rxLengthErrors, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "LinkStatistics64", field: "rxLengthErrors")
}
guard let offset = buffer.copyIn(as: UInt64.self, value: rxOverErrors, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "LinkStatistics64", field: "rxOverErrors")
}
guard let offset = buffer.copyIn(as: UInt64.self, value: rxCrcErrors, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "LinkStatistics64", field: "rxCrcErrors")
}
guard let offset = buffer.copyIn(as: UInt64.self, value: rxFrameErrors, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "LinkStatistics64", field: "rxFrameErrors")
}
guard let offset = buffer.copyIn(as: UInt64.self, value: rxFifoErrors, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "LinkStatistics64", field: "rxFifoErrors")
}
guard let offset = buffer.copyIn(as: UInt64.self, value: rxMissedErrors, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "LinkStatistics64", field: "rxMissedErrors")
}
guard let offset = buffer.copyIn(as: UInt64.self, value: txAbortedErrors, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "LinkStatistics64", field: "txAbortedErrors")
}
guard let offset = buffer.copyIn(as: UInt64.self, value: txCarrierErrors, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "LinkStatistics64", field: "txCarrierErrors")
}
guard let offset = buffer.copyIn(as: UInt64.self, value: txFifoErrors, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "LinkStatistics64", field: "txFifoErrors")
}
guard let offset = buffer.copyIn(as: UInt64.self, value: txHeartbeatErrors, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "LinkStatistics64", field: "txHeartbeatErrors")
}
guard let offset = buffer.copyIn(as: UInt64.self, value: txWindowErrors, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "LinkStatistics64", field: "txWindowErrors")
}
guard let offset = buffer.copyIn(as: UInt64.self, value: rxCompressed, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "LinkStatistics64", field: "rxCompressed")
}
guard let offset = buffer.copyIn(as: UInt64.self, value: txCompressed, offset: offset) else {
throw NetlinkDataError.sendMarshalFailure
throw BindError.sendMarshalFailure(type: "LinkStatistics64", field: "txCompressed")
}
return offset
}
mutating func bindBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {
package mutating func bindBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {
guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {
throw NetlinkDataError.recvUnmarshalFailure
throw BindError.recvMarshalFailure(type: "LinkStatistics64", field: "rxPackets")
}
rxPackets = value
guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {
throw NetlinkDataError.recvUnmarshalFailure
throw BindError.recvMarshalFailure(type: "LinkStatistics64", field: "txPackets")
}
txPackets = value
guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {
throw NetlinkDataError.recvUnmarshalFailure
throw BindError.recvMarshalFailure(type: "LinkStatistics64", field: "rxBytes")
}
rxBytes = value
guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {
throw NetlinkDataError.recvUnmarshalFailure
throw BindError.recvMarshalFailure(type: "LinkStatistics64", field: "txBytes")
}
txBytes = value
guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {
throw NetlinkDataError.recvUnmarshalFailure
throw BindError.recvMarshalFailure(type: "LinkStatistics64", field: "rxErrors")
}
rxErrors = value
guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {
throw NetlinkDataError.recvUnmarshalFailure
throw BindError.recvMarshalFailure(type: "LinkStatistics64", field: "txErrors")
}
txErrors = value
guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {
throw NetlinkDataError.recvUnmarshalFailure
throw BindError.recvMarshalFailure(type: "LinkStatistics64", field: "rxDropped")
}
rxDropped = value
guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {
throw NetlinkDataError.recvUnmarshalFailure
throw BindError.recvMarshalFailure(type: "LinkStatistics64", field: "txDropped")
}
txDropped = value
guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {
throw NetlinkDataError.recvUnmarshalFailure
throw BindError.recvMarshalFailure(type: "LinkStatistics64", field: "multicast")
}
multicast = value
guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {
throw NetlinkDataError.recvUnmarshalFailure
throw BindError.recvMarshalFailure(type: "LinkStatistics64", field: "collisions")
}
collisions = value
guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {
throw NetlinkDataError.recvUnmarshalFailure
throw BindError.recvMarshalFailure(type: "LinkStatistics64", field: "rxLengthErrors")
}
rxLengthErrors = value
guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {
throw NetlinkDataError.recvUnmarshalFailure
throw BindError.recvMarshalFailure(type: "LinkStatistics64", field: "rxOverErrors")
}
rxOverErrors = value
guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {
throw NetlinkDataError.recvUnmarshalFailure
throw BindError.recvMarshalFailure(type: "LinkStatistics64", field: "rxCrcErrors")
}
rxCrcErrors = value
guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {
throw NetlinkDataError.recvUnmarshalFailure
throw BindError.recvMarshalFailure(type: "LinkStatistics64", field: "rxFrameErrors")
}
rxFrameErrors = value
guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {
throw NetlinkDataError.recvUnmarshalFailure
throw BindError.recvMarshalFailure(type: "LinkStatistics64", field: "rxFifoErrors")
}
rxFifoErrors = value
guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {
throw NetlinkDataError.recvUnmarshalFailure
throw BindError.recvMarshalFailure(type: "LinkStatistics64", field: "rxMissedErrors")
}
rxMissedErrors = value
guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {
throw NetlinkDataError.recvUnmarshalFailure
throw BindError.recvMarshalFailure(type: "LinkStatistics64", field: "txAbortedErrors")
}
txAbortedErrors = value
guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {
throw NetlinkDataError.recvUnmarshalFailure
throw BindError.recvMarshalFailure(type: "LinkStatistics64", field: "txCarrierErrors")
}
txCarrierErrors = value
guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {
throw NetlinkDataError.recvUnmarshalFailure
throw BindError.recvMarshalFailure(type: "LinkStatistics64", field: "txFifoErrors")
}
txFifoErrors = value
guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {
throw NetlinkDataError.recvUnmarshalFailure
throw BindError.recvMarshalFailure(type: "LinkStatistics64", field: "txHeartbeatErrors")
}
txHeartbeatErrors = value
guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {
throw NetlinkDataError.recvUnmarshalFailure
throw BindError.recvMarshalFailure(type: "LinkStatistics64", field: "txWindowErrors")
}
txWindowErrors = value
guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {
throw NetlinkDataError.recvUnmarshalFailure
throw BindError.recvMarshalFailure(type: "LinkStatistics64", field: "rxCompressed")
}
rxCompressed = value
guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {
throw NetlinkDataError.recvUnmarshalFailure
throw BindError.recvMarshalFailure(type: "LinkStatistics64", field: "txCompressed")
}
txCompressed = value
@@ -861,18 +863,12 @@ public struct LinkStatistics64: Bindable {
/// Errors thrown when parsing netlink data.
public enum NetlinkDataError: Swift.Error, CustomStringConvertible, Equatable {
case sendMarshalFailure
case recvUnmarshalFailure
case responseError(rc: Int32)
case unsupportedPlatform
/// The description of the errors.
public var description: String {
switch self {
case .sendMarshalFailure:
return "could not marshal netlink packet"
case .recvUnmarshalFailure:
return "could not unmarshal netlink packet"
case .responseError(let rc):
return "netlink response indicates error, rc = \(rc)"
case .unsupportedPlatform:
@@ -182,10 +182,11 @@ struct MACAddressTests {
(0x5e3b_68d7_e510, 0xfd97_7b15_d62e_75ac_5c3b_68ff_fed7_e510),
]
)
func testLinkLocalAddress(mac: UInt64, ipv6: UInt128) {
func testLinkLocalAddress(mac: UInt64, ipv6: UInt128) throws {
let mac = MACAddress(mac)
let ipv6Prefix = IPv6Address(ipv6 & 0xffff_ffff_ffff_ffff_0000_0000_0000_0000)
#expect(mac.ipv6Address(network: ipv6Prefix) == IPv6Address(ipv6))
let ipv6Address = try mac.ipv6Address(network: ipv6Prefix)
#expect(ipv6Address == IPv6Address(ipv6))
}
}
@@ -16,7 +16,51 @@
import Testing
@testable import ContainerizationExtras
struct BufferTest {
// MARK: - hexEncodedString Tests
@Test func testArrayHexEncodedStringEmpty() {
let buffer: [UInt8] = []
#expect(buffer.hexEncodedString() == "")
}
@Test func testArrayHexEncodedStringSingleByte() {
let buffer: [UInt8] = [0xFF]
#expect(buffer.hexEncodedString() == "ff")
}
@Test func testArrayHexEncodedStringMultipleBytes() {
let buffer: [UInt8] = [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF]
#expect(buffer.hexEncodedString() == "0123456789abcdef")
}
@Test func testArrayHexEncodedStringZeroes() {
let buffer: [UInt8] = [0x00, 0x00, 0x00]
#expect(buffer.hexEncodedString() == "000000")
}
@Test func testArraySliceHexEncodedStringEmpty() {
let buffer: [UInt8] = [0x01, 0x02, 0x03]
let slice = buffer[0..<0]
#expect(slice.hexEncodedString() == "")
}
@Test func testArraySliceHexEncodedStringSingleByte() {
let buffer: [UInt8] = [0x01, 0x02, 0x03]
let slice = buffer[1..<2]
#expect(slice.hexEncodedString() == "02")
}
@Test func testArraySliceHexEncodedStringMultipleBytes() {
let buffer: [UInt8] = [0x00, 0xAA, 0xBB, 0xCC, 0x00]
let slice = buffer[1..<4]
#expect(slice.hexEncodedString() == "aabbcc")
}
// MARK: - bind<T> Tests
@Test func testBufferBind() throws {
let expectedValue: UInt64 = 0x0102_0304_0506_0708
let expectedBuffer: [UInt8] = [
@@ -26,11 +70,6 @@ struct BufferTest {
]
var buffer = [UInt8](repeating: 0, count: 3 * MemoryLayout<UInt64>.size)
guard let ptr = buffer.bind(as: UInt64.self, offset: 2 * MemoryLayout<UInt64>.size) else {
// NOTE: This does not work:
// let ptr: UnsafeMutablePointer<UInt64> = #require(buffer.bind(as: UInt64.self, offset: MemoryLayout<UInt64>.size), "could not bind value to buffer")
// it fails with the error:
// cannot use mutating member on immutable value: '$0' is immutable
// $0.bind(as: $1, offset: $2)
#expect(Bool(false), "could not bind value to buffer")
return
}
@@ -39,11 +78,257 @@ struct BufferTest {
#expect(buffer == expectedBuffer)
}
@Test func testBufferBindZeroOffset() {
let expectedValue: UInt32 = 0x1234_5678
var buffer = [UInt8](repeating: 0, count: 8)
guard let ptr = buffer.bind(as: UInt32.self, offset: 0) else {
#expect(Bool(false), "could not bind value to buffer at offset 0")
return
}
ptr.pointee = expectedValue
#expect(buffer[0] == 0x78)
#expect(buffer[1] == 0x56)
#expect(buffer[2] == 0x34)
#expect(buffer[3] == 0x12)
}
@Test func testBufferBindRangeError() throws {
var buffer = [UInt8](repeating: 0, count: 3 * MemoryLayout<UInt64>.size)
#expect(buffer.bind(as: UInt64.self, offset: 2 * MemoryLayout<UInt64>.size + 1) == nil)
}
@Test func testBufferBindRangeErrorExactBoundary() {
var buffer = [UInt8](repeating: 0, count: 8)
// Trying to bind UInt64 at offset 1 requires 9 bytes total
#expect(buffer.bind(as: UInt64.self, offset: 1) == nil)
}
@Test func testBufferBindWithCustomSize() {
var buffer = [UInt8](repeating: 0, count: 16)
// Request a size larger than the type
guard let ptr = buffer.bind(as: UInt32.self, offset: 4, size: 8) else {
#expect(Bool(false), "could not bind with custom size")
return
}
ptr.pointee = 0xAABB_CCDD
#expect(buffer[4] == 0xDD)
#expect(buffer[5] == 0xCC)
#expect(buffer[6] == 0xBB)
#expect(buffer[7] == 0xAA)
}
@Test func testBufferBindWithCustomSizeRangeError() {
var buffer = [UInt8](repeating: 0, count: 10)
// Request size 8 at offset 4 would require 12 bytes total
#expect(buffer.bind(as: UInt32.self, offset: 4, size: 8) == nil)
}
// MARK: - copyIn<T> Tests
@Test func testCopyInUInt8() {
var buffer = [UInt8](repeating: 0, count: 4)
let value: UInt8 = 0x42
guard let offset = buffer.copyIn(as: UInt8.self, value: value, offset: 2) else {
#expect(Bool(false), "could not copy UInt8 to buffer")
return
}
#expect(offset == 3)
#expect(buffer[2] == 0x42)
}
@Test func testCopyInUInt16() {
var buffer = [UInt8](repeating: 0, count: 8)
let value: UInt16 = 0x1234
guard let offset = buffer.copyIn(as: UInt16.self, value: value, offset: 3) else {
#expect(Bool(false), "could not copy UInt16 to buffer")
return
}
#expect(offset == 5)
#expect(buffer[3] == 0x34)
#expect(buffer[4] == 0x12)
}
@Test func testCopyInUInt32() {
var buffer = [UInt8](repeating: 0, count: 8)
let value: UInt32 = 0x1234_5678
guard let offset = buffer.copyIn(as: UInt32.self, value: value, offset: 0) else {
#expect(Bool(false), "could not copy UInt32 to buffer")
return
}
#expect(offset == 4)
#expect(buffer[0] == 0x78)
#expect(buffer[1] == 0x56)
#expect(buffer[2] == 0x34)
#expect(buffer[3] == 0x12)
}
@Test func testCopyInUInt64() {
var buffer = [UInt8](repeating: 0, count: 16)
let value: UInt64 = 0x0102_0304_0506_0708
guard let offset = buffer.copyIn(as: UInt64.self, value: value, offset: 4) else {
#expect(Bool(false), "could not copy UInt64 to buffer")
return
}
#expect(offset == 12)
#expect(buffer[4] == 0x08)
#expect(buffer[5] == 0x07)
#expect(buffer[6] == 0x06)
#expect(buffer[7] == 0x05)
#expect(buffer[8] == 0x04)
#expect(buffer[9] == 0x03)
#expect(buffer[10] == 0x02)
#expect(buffer[11] == 0x01)
}
@Test func testCopyInRangeError() {
var buffer = [UInt8](repeating: 0, count: 8)
let value: UInt64 = 0x1234_5678_90AB_CDEF
// Offset 4 + size 8 = 12, but buffer only has 8 bytes
#expect(buffer.copyIn(as: UInt64.self, value: value, offset: 4) == nil)
}
@Test func testCopyInExactBoundary() {
var buffer = [UInt8](repeating: 0, count: 8)
let value: UInt64 = 0xFEDC_BA98_7654_3210
guard let offset = buffer.copyIn(as: UInt64.self, value: value, offset: 0) else {
#expect(Bool(false), "could not copy UInt64 at exact boundary")
return
}
#expect(offset == 8)
}
@Test func testCopyInWithCustomSize() {
var buffer = [UInt8](repeating: 0, count: 16)
let value: UInt32 = 0xAABB_CCDD
// Copy with custom size of 8 (larger than UInt32's 4 bytes)
guard let offset = buffer.copyIn(as: UInt32.self, value: value, offset: 2, size: 8) else {
#expect(Bool(false), "could not copy with custom size")
return
}
#expect(offset == 6) // offset + MemoryLayout<UInt32>.size
#expect(buffer[2] == 0xDD)
#expect(buffer[3] == 0xCC)
#expect(buffer[4] == 0xBB)
#expect(buffer[5] == 0xAA)
}
@Test func testCopyInWithCustomSizeRangeError() {
var buffer = [UInt8](repeating: 0, count: 8)
let value: UInt32 = 0x1234_5678
// Request size 8 at offset 2 would require 10 bytes total
#expect(buffer.copyIn(as: UInt32.self, value: value, offset: 2, size: 8) == nil)
}
// MARK: - copyOut<T> Tests
@Test func testCopyOutUInt8() {
let buffer: [UInt8] = [0x00, 0x11, 0x22, 0x33]
guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: 2) else {
#expect(Bool(false), "could not copy out UInt8")
return
}
#expect(offset == 3)
#expect(value == 0x22)
}
@Test func testCopyOutUInt16() {
let buffer: [UInt8] = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]
guard let (offset, value) = buffer.copyOut(as: UInt16.self, offset: 2) else {
#expect(Bool(false), "could not copy out UInt16")
return
}
#expect(offset == 4)
#expect(value == 0x3322)
}
@Test func testCopyOutUInt32() {
let buffer: [UInt8] = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]
guard let (offset, value) = buffer.copyOut(as: UInt32.self, offset: 0) else {
#expect(Bool(false), "could not copy out UInt32")
return
}
#expect(offset == 4)
#expect(value == 0x7856_3412)
}
@Test func testCopyOutUInt64() {
let buffer: [UInt8] = [
0x00, 0x00, 0x00, 0x00,
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
0xFF, 0xFF,
]
guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: 4) else {
#expect(Bool(false), "could not copy out UInt64")
return
}
#expect(offset == 12)
#expect(value == 0x8877_6655_4433_2211)
}
@Test func testCopyOutRangeError() {
let buffer: [UInt8] = [0x00, 0x11, 0x22, 0x33]
// Trying to read UInt64 from offset 0 with only 4 bytes
#expect(buffer.copyOut(as: UInt64.self, offset: 0) == nil)
}
@Test func testCopyOutExactBoundary() {
let buffer: [UInt8] = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]
guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: 0) else {
#expect(Bool(false), "could not copy out at exact boundary")
return
}
#expect(offset == 8)
#expect(value == 0x0807_0605_0403_0201)
}
@Test func testCopyOutWithCustomSize() {
let buffer: [UInt8] = [0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0xFF, 0xFF, 0xFF, 0xFF]
guard let (offset, value) = buffer.copyOut(as: UInt32.self, offset: 2, size: 8) else {
#expect(Bool(false), "could not copy out with custom size")
return
}
#expect(offset == 6) // offset + MemoryLayout<UInt32>.size
#expect(value == 0x4433_2211)
}
@Test func testCopyOutWithCustomSizeRangeError() {
let buffer: [UInt8] = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]
// Request size 8 at offset 2 would require 10 bytes total, but buffer only has 6
#expect(buffer.copyOut(as: UInt32.self, offset: 2, size: 8) == nil)
}
// MARK: - copyIn(buffer:) and copyOut(buffer:) Tests
@Test func testBufferCopy() throws {
let inputBuffer: [UInt8] = [0x01, 0x02, 0x03]
var buffer = [UInt8](repeating: 0, count: 9)
@@ -78,6 +363,45 @@ struct BufferTest {
#expect(expectedOutputBuffer == outputBuffer)
}
@Test func testBufferCopyZeroOffset() {
let inputBuffer: [UInt8] = [0xAA, 0xBB, 0xCC]
var buffer = [UInt8](repeating: 0, count: 5)
guard let offset = buffer.copyIn(buffer: inputBuffer, offset: 0) else {
#expect(Bool(false), "could not copy to buffer at offset 0")
return
}
#expect(offset == 3)
#expect(buffer[0] == 0xAA)
#expect(buffer[1] == 0xBB)
#expect(buffer[2] == 0xCC)
}
@Test func testBufferCopyEmptyBuffer() {
let inputBuffer: [UInt8] = []
var buffer = [UInt8](repeating: 0, count: 5)
guard let offset = buffer.copyIn(buffer: inputBuffer, offset: 2) else {
#expect(Bool(false), "could not copy empty buffer")
return
}
#expect(offset == 2)
}
@Test func testBufferCopyExactFit() {
let inputBuffer: [UInt8] = [0x01, 0x02, 0x03]
var buffer = [UInt8](repeating: 0, count: 6)
guard let offset = buffer.copyIn(buffer: inputBuffer, offset: 3) else {
#expect(Bool(false), "could not copy to exact fit")
return
}
#expect(offset == 6)
}
@Test func testBufferCopyRangeError() throws {
let inputBuffer: [UInt8] = [0x01, 0x02, 0x03]
var buffer = [UInt8](repeating: 0, count: 9)
@@ -87,4 +411,46 @@ struct BufferTest {
var outputBuffer = [UInt8](repeating: 0, count: 3)
#expect(buffer.copyOut(buffer: &outputBuffer, offset: 7) == nil)
}
@Test func testBufferCopyOutZeroOffset() {
let buffer: [UInt8] = [0x11, 0x22, 0x33, 0x44, 0x55]
var outputBuffer = [UInt8](repeating: 0, count: 3)
guard let offset = buffer.copyOut(buffer: &outputBuffer, offset: 0) else {
#expect(Bool(false), "could not copy out at offset 0")
return
}
#expect(offset == 3)
#expect(outputBuffer[0] == 0x11)
#expect(outputBuffer[1] == 0x22)
#expect(outputBuffer[2] == 0x33)
}
@Test func testBufferCopyOutEmptyBuffer() {
let buffer: [UInt8] = [0x11, 0x22, 0x33]
var outputBuffer: [UInt8] = []
guard let offset = buffer.copyOut(buffer: &outputBuffer, offset: 1) else {
#expect(Bool(false), "could not copy out to empty buffer")
return
}
#expect(offset == 1)
}
@Test func testBufferCopyOutExactFit() {
let buffer: [UInt8] = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]
var outputBuffer = [UInt8](repeating: 0, count: 3)
guard let offset = buffer.copyOut(buffer: &outputBuffer, offset: 3) else {
#expect(Bool(false), "could not copy out exact fit")
return
}
#expect(offset == 6)
#expect(outputBuffer[0] == 0xDD)
#expect(outputBuffer[1] == 0xEE)
#expect(outputBuffer[2] == 0xFF)
}
}
@@ -108,4 +108,114 @@ struct TypesTest {
#expect(offset == RTAttribute.size)
#expect(expectedValue == value)
}
@Test func testSockaddrNetlink() throws {
let expectedValue = SockaddrNetlink(family: 16, pid: 0x1234_5678, groups: 0x9abc_def0)
let expectedBuffer: [UInt8] = [
0x10, 0x00, 0x00, 0x00,
0x78, 0x56, 0x34, 0x12,
0xf0, 0xde, 0xbc, 0x9a,
]
var buffer = [UInt8](repeating: 0, count: SockaddrNetlink.size)
let offset = try expectedValue.appendBuffer(&buffer, offset: 0)
#expect(SockaddrNetlink.size == offset)
#expect(expectedBuffer == buffer)
var unmarshaledValue = SockaddrNetlink()
let bindOffset = try unmarshaledValue.bindBuffer(&buffer, offset: 0)
#expect(bindOffset == SockaddrNetlink.size)
#expect(expectedValue == unmarshaledValue)
}
@Test func testRouteInfo() throws {
let expectedValue = RouteInfo(
family: UInt8(AddressFamily.AF_INET),
dstLen: 24,
srcLen: 0,
tos: 0,
table: RouteTable.MAIN,
proto: RouteProtocol.KERNEL,
scope: RouteScope.LINK,
type: RouteType.UNICAST,
flags: 0xdead_beef
)
let expectedBuffer: [UInt8] = [
0x02, 0x18, 0x00, 0x00,
0xfe, 0x02, 0xfd, 0x01,
0xef, 0xbe, 0xad, 0xde,
]
var buffer = [UInt8](repeating: 0, count: RouteInfo.size)
let offset = try expectedValue.appendBuffer(&buffer, offset: 0)
#expect(RouteInfo.size == offset)
#expect(expectedBuffer == buffer)
var unmarshaledValue = RouteInfo(
dstLen: 0, srcLen: 0, tos: 0, table: 0, proto: 0, scope: 0, type: 0, flags: 0)
let bindOffset = try unmarshaledValue.bindBuffer(&buffer, offset: 0)
#expect(bindOffset == RouteInfo.size)
#expect(expectedValue == unmarshaledValue)
}
@Test func testLinkStatistics64() throws {
var expectedValue = LinkStatistics64()
expectedValue.rxPackets = 0x0102_0304_0506_0708
expectedValue.txPackets = 0x090a_0b0c_0d0e_0f10
expectedValue.rxBytes = 0x1112_1314_1516_1718
expectedValue.txBytes = 0x191a_1b1c_1d1e_1f20
expectedValue.rxErrors = 0x2122_2324_2526_2728
expectedValue.txErrors = 0x292a_2b2c_2d2e_2f30
expectedValue.rxDropped = 0x3132_3334_3536_3738
expectedValue.txDropped = 0x393a_3b3c_3d3e_3f40
expectedValue.multicast = 0x4142_4344_4546_4748
expectedValue.collisions = 0x494a_4b4c_4d4e_4f50
expectedValue.rxLengthErrors = 0x5152_5354_5556_5758
expectedValue.rxOverErrors = 0x595a_5b5c_5d5e_5f60
expectedValue.rxCrcErrors = 0x6162_6364_6566_6768
expectedValue.rxFrameErrors = 0x696a_6b6c_6d6e_6f70
expectedValue.rxFifoErrors = 0x7172_7374_7576_7778
expectedValue.rxMissedErrors = 0x797a_7b7c_7d7e_7f80
expectedValue.txAbortedErrors = 0x8182_8384_8586_8788
expectedValue.txCarrierErrors = 0x898a_8b8c_8d8e_8f90
expectedValue.txFifoErrors = 0x9192_9394_9596_9798
expectedValue.txHeartbeatErrors = 0x999a_9b9c_9d9e_9fa0
expectedValue.txWindowErrors = 0xa1a2_a3a4_a5a6_a7a8
expectedValue.rxCompressed = 0xa9aa_abac_adae_afb0
expectedValue.txCompressed = 0xb1b2_b3b4_b5b6_b7b8
let expectedBuffer: [UInt8] = [
0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09,
0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11,
0x20, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19,
0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21,
0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29,
0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31,
0x40, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39,
0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41,
0x50, 0x4f, 0x4e, 0x4d, 0x4c, 0x4b, 0x4a, 0x49,
0x58, 0x57, 0x56, 0x55, 0x54, 0x53, 0x52, 0x51,
0x60, 0x5f, 0x5e, 0x5d, 0x5c, 0x5b, 0x5a, 0x59,
0x68, 0x67, 0x66, 0x65, 0x64, 0x63, 0x62, 0x61,
0x70, 0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x69,
0x78, 0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71,
0x80, 0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79,
0x88, 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81,
0x90, 0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x89,
0x98, 0x97, 0x96, 0x95, 0x94, 0x93, 0x92, 0x91,
0xa0, 0x9f, 0x9e, 0x9d, 0x9c, 0x9b, 0x9a, 0x99,
0xa8, 0xa7, 0xa6, 0xa5, 0xa4, 0xa3, 0xa2, 0xa1,
0xb0, 0xaf, 0xae, 0xad, 0xac, 0xab, 0xaa, 0xa9,
0xb8, 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1,
]
var buffer = [UInt8](repeating: 0, count: LinkStatistics64.size)
let offset = try expectedValue.appendBuffer(&buffer, offset: 0)
#expect(LinkStatistics64.size == offset)
#expect(expectedBuffer == buffer)
var unmarshaledValue = LinkStatistics64()
let bindOffset = try unmarshaledValue.bindBuffer(&buffer, offset: 0)
#expect(bindOffset == LinkStatistics64.size)
#expect(expectedValue == unmarshaledValue)
}
}