Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions Sources/SwiftNetwork/Endpoint/AddressEndpoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ public struct AddressEndpoint: EndpointProtocol, EndpointCommonProtocol, Sendabl
public var type: AddressEndpointType
public var priority: UInt16 = 0
public var weight: UInt16 = 0
public var ethernetAddress: EthernetAddress?
var originalFd: Int32? = nil
var scope: UInt32 = 0

Expand Down Expand Up @@ -291,7 +290,7 @@ public struct AddressEndpoint: EndpointProtocol, EndpointCommonProtocol, Sendabl
if !matchInterface {
return true
}
guard let interface else {
guard let interface = self.interface else {
return false
}
return interface.index == interfaceIndex
Expand Down Expand Up @@ -327,7 +326,7 @@ public struct AddressEndpoint: EndpointProtocol, EndpointCommonProtocol, Sendabl
if !matchInterface {
return true
}
guard let interface else {
guard let interface = self.interface else {
return false
}
return interface.index == interfaceIndex
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftNetwork/Endpoint/BonjourEndpoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ public struct BonjourEndpoint: EndpointProtocol, EndpointCommonProtocol, Sendabl

public func descriptionInternal(redacted: Bool) -> String {
var suffix = ""
if let interface {
if let interface = self.interface {
suffix = "@" + interface.name
}
if redacted {
Expand Down
112 changes: 56 additions & 56 deletions Sources/SwiftNetwork/Endpoint/Endpoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ public class EndpointParent: Hashable, Equatable {

@_spi(Essentials)
@available(Network 0.1.0, *)
#if !NETWORK_EMBEDDED
@dynamicMemberLookup
#endif
public final class Endpoint: EndpointParent, EndpointProtocol {
public enum EndpointType: Sendable {
case address(AddressEndpoint)
Expand All @@ -43,14 +46,62 @@ public final class Endpoint: EndpointParent, EndpointProtocol {
}

public var type: EndpointType
var alternatePort: UInt16? = nil
var cnames: [Endpoint]? = nil
public var parentEndpoint: Endpoint? = nil

/// Mutates `type` in place. This takes exclusive access to `type` for the
/// duration of `body`, so `body` must not read this same endpoint.
func modifyType(_ body: (inout EndpointType) -> Void) {
body(&type)
}

#if NETWORK_PRIVATE || NETWORK_DRIVERKIT
var endpointPrivate = Endpoint.EndpointPrivate()
#endif

// MARK: -- Common state --

/// The common state shared by every endpoint type, forwarded to whichever
/// case `type` currently holds (unwrapped by `EndpointType.common`). All
/// individual common fields are reached through the `dynamicMember`
/// subscript below rather than through per-field switches, and writes go
/// through `modifyCommon` so they modify `type` in place.
var common: EndpointCommon {
get { type.common }
set { modifyType { $0.common = newValue } }
}

/// Mutates the common state in place. As with `modifyType`, `body` must not
/// read this same endpoint.
func modifyCommon(_ body: (inout EndpointCommon) -> Void) {
modifyType { body(&$0.common) }
}

#if !NETWORK_EMBEDDED
/// Reads and writes any ``EndpointCommon`` field directly on the endpoint,
/// e.g. `endpoint.alternatePort` or `endpoint.parentEndpoint`.
public subscript<Value>(dynamicMember keyPath: WritableKeyPath<EndpointCommon, Value>) -> Value {
get { common[keyPath: keyPath] }
set { modifyCommon { $0[keyPath: keyPath] = newValue } }
}
#else
// Embedded Swift has no key paths; forward the common fields explicitly.
var alternatePort: UInt16? {
get { common.alternatePort }
set { modifyCommon { $0.alternatePort = newValue } }
}
var cnames: [Endpoint]? {
get { common.cnames }
set { modifyCommon { $0.cnames = newValue } }
}
public var parentEndpoint: Endpoint? {
get { common.parentEndpoint }
set { modifyCommon { $0.parentEndpoint = newValue } }
}
var ethernetAddress: EthernetAddress? {
get { common.ethernetAddress }
set { modifyCommon { $0.ethernetAddress = newValue } }
}
#endif

// MARK: -- Initializers --
public init(_ address: AddressEndpoint) {
self.type = .address(address)
Expand Down Expand Up @@ -199,20 +250,6 @@ public final class Endpoint: EndpointParent, EndpointProtocol {

#if !NETWORK_PRIVATE
func isEqual(to other: Endpoint, flags: EndpointEqualityFlags) -> Bool {
if flags.contains(.alternatives) {
let alternatePort = alternatePort ?? 0
let otherAlternatePort = other.alternatePort ?? 0
if alternatePort != otherAlternatePort {
return false
}
}

if flags.contains(.parent) {
if parentEndpoint != other.parentEndpoint {
return false
}
}

switch self.type {
case .address(let endpoint):
if case let .address(otherEndpoint) = other.type {
Expand Down Expand Up @@ -297,45 +334,8 @@ public final class Endpoint: EndpointParent, EndpointProtocol {
}

var interface: Interface? {
get {
switch self.type {
case .address(let endpoint):
return endpoint.interface
case .applicationService(let endpoint):
return endpoint.interface
case .bonjour(let endpoint):
return endpoint.interface
case .host(let endpoint):
return endpoint.interface
case .srv(let endpoint):
return endpoint.interface
case .url(let endpoint):
return endpoint.interface
}
}

set {
switch self.type {
case .address(var endpoint):
endpoint.interface = newValue
self.type = .address(endpoint)
case .applicationService(var endpoint):
endpoint.interface = newValue
self.type = .applicationService(endpoint)
case .bonjour(var endpoint):
endpoint.interface = newValue
self.type = .bonjour(endpoint)
case .host(var endpoint):
endpoint.interface = newValue
self.type = .host(endpoint)
case .srv(var endpoint):
endpoint.interface = newValue
self.type = .srv(endpoint)
case .url(var endpoint):
endpoint.interface = newValue
self.type = .url(endpoint)
}
}
get { common.interface }
set { modifyCommon { $0.interface = newValue } }
}

// MARK: -- Hashing --
Expand Down
115 changes: 108 additions & 7 deletions Sources/SwiftNetwork/Endpoint/EndpointCommon.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,42 @@ extension Endpoint.EndpointType {
case applicationService = 6
}

/// The common state of whichever endpoint this case holds.
var common: EndpointCommon {
get {
switch self {
case .address(let endpoint): return endpoint.common
case .applicationService(let endpoint): return endpoint.common
case .bonjour(let endpoint): return endpoint.common
case .host(let endpoint): return endpoint.common
case .srv(let endpoint): return endpoint.common
case .url(let endpoint): return endpoint.common
}
}
set {
switch self {
case .address(var endpoint):
endpoint.common = newValue
self = .address(endpoint)
case .applicationService(var endpoint):
endpoint.common = newValue
self = .applicationService(endpoint)
case .bonjour(var endpoint):
endpoint.common = newValue
self = .bonjour(endpoint)
case .host(var endpoint):
endpoint.common = newValue
self = .host(endpoint)
case .srv(var endpoint):
endpoint.common = newValue
self = .srv(endpoint)
case .url(var endpoint):
endpoint.common = newValue
self = .url(endpoint)
}
}
}

func toRawValue() -> UInt32 {
switch self {
case .address(_):
Expand Down Expand Up @@ -84,17 +120,23 @@ extension Endpoint.EndpointType {

@available(Network 0.1.0, *)
protocol EndpointProtocol: CustomStringConvertible {
var interface: Interface? { get }
func isEqual(to other: Self, flags: EndpointEqualityFlags) -> Bool
func serialize() -> [UInt8]?
}

@_spi(Essentials)
@available(Network 0.1.0, *)
public struct EndpointCommon: Equatable, Hashable, Sendable {
let interface: Interface?
// `cnames` and `parentEndpoint` hold `Endpoint`, a mutable class, so the sendability of this
// struct is only as good as the confinement of those endpoints. The package builds in a language
// mode that checks this; libnetcore does not, which is why it carries the same fields unannotated.
public struct EndpointCommon: Equatable, Hashable, @unchecked Sendable {
var interface: Interface?
var alternatePort: UInt16?
var cnames: [Endpoint]?
var parentEndpoint: Endpoint?
var ethernetAddress: EthernetAddress?
#if NETWORK_PRIVATE
let commonPrivate: EndpointCommon_Private?
var commonPrivate: EndpointCommon_Private?
#endif

init(interface: Interface? = nil) {
Expand All @@ -111,8 +153,26 @@ public struct EndpointCommon: Equatable, Hashable, Sendable {
}
}

if flags.contains(.alternatives) {
if (self.alternatePort ?? 0) != (other.alternatePort ?? 0) {
return false
}
}

if flags.contains(.parent) {
if self.parentEndpoint != other.parentEndpoint {
return false
}
}

if flags.contains(.interface) {
if self.ethernetAddress != other.ethernetAddress {
return false
}
}

#if NETWORK_PRIVATE
if commonPrivate != other.commonPrivate {
if !self.isPrivateEqual(to: other, flags: flags) {
return false
}
#endif
Expand All @@ -125,23 +185,64 @@ public struct EndpointCommon: Equatable, Hashable, Sendable {
hasher.combine(interface.hashValue)
}
}

public static func == (lhs: EndpointCommon, rhs: EndpointCommon) -> Bool {
lhs.isEqual(to: rhs, flags: .all)
}
}

/// Exposes every ``EndpointCommon`` field on the conforming type as if it were
/// declared there directly, so that adding a field to `EndpointCommon` (or to
/// `EndpointCommon_Private`) needs no per-field accessor plumbing on each of the
/// endpoint types.
///
/// Embedded Swift does not support key paths, so that build gets explicit
/// forwarding accessors below instead of the `dynamicMember` subscript.
@_spi(Essentials)
@available(Network 0.1.0, *)
#if !NETWORK_EMBEDDED
@dynamicMemberLookup
#endif
public protocol EndpointCommonProtocol: Hashable, Equatable {
var common: EndpointCommon { get set }
}

#if !NETWORK_PRIVATE
#if !NETWORK_EMBEDDED
@_spi(Essentials)
@available(Network 0.1.0, *)
extension EndpointCommonProtocol {
public subscript<Value>(dynamicMember keyPath: WritableKeyPath<EndpointCommon, Value>) -> Value {
get { common[keyPath: keyPath] }
set { common[keyPath: keyPath] = newValue }
}
}
#else
@available(Network 0.1.0, *)
extension EndpointCommonProtocol {
var interface: Interface? {
get { common.interface }
set { common = EndpointCommon(interface: newValue) }
set { common.interface = newValue }
}
var alternatePort: UInt16? {
get { common.alternatePort }
set { common.alternatePort = newValue }
}
var cnames: [Endpoint]? {
get { common.cnames }
set { common.cnames = newValue }
}
var parentEndpoint: Endpoint? {
get { common.parentEndpoint }
set { common.parentEndpoint = newValue }
}
var ethernetAddress: EthernetAddress? {
get { common.ethernetAddress }
set { common.ethernetAddress = newValue }
}
}
#endif

#if !NETWORK_PRIVATE
@available(Network 0.1.0, *)
extension EndpointCommon {
init?(_ data: inout [UInt8]) {
Expand Down
Loading