Skip to content

Commit af3310d

Browse files
committed
Improve request building performance
1 parent d047364 commit af3310d

File tree

17 files changed

+333
-67
lines changed

17 files changed

+333
-67
lines changed

Sources/NetworkingCore/Configurations/Configurations.swift

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,14 @@ public struct ConfigurationValues: Sendable, CustomStringConvertible {
3535
/// - Parameter key: The type of the configuration key.
3636
public subscript<K: ConfigurationKey>(_ key: K.Type) -> K.Value {
3737
get {
38-
guard let value = box.values[String(describing: key)] else {
38+
guard let value = box.value(for: ObjectIdentifier(key)) else {
3939
return K.defaultValue
4040
}
4141
return unsafeBitCast(value, to: K.Value.self)
4242
}
4343
set {
4444
ensureUniqueBox()
45-
box.values[String(describing: key)] = newValue
45+
box.setValue(newValue, for: ObjectIdentifier(key))
4646
}
4747
}
4848

@@ -54,15 +54,16 @@ public struct ConfigurationValues: Sendable, CustomStringConvertible {
5454
/// - SeeAlso: `isKnownUniquelyReferenced(_:)`
5555
@inline(__always) private mutating func ensureUniqueBox() {
5656
guard !isKnownUniquelyReferenced(&box) else {return}
57-
box = ProperiesBox(values: box.values)
57+
box = ProperiesBox(values: box.allValues())
5858
}
5959

6060
/// A string that represents the contents of the environment values instance.
6161
public var description: String {
62-
guard !box.values.isEmpty else {
62+
let values = box.allValues()
63+
guard !values.isEmpty else {
6364
return "\(String(describing: Self.self)) = []"
6465
}
65-
let valuesString = box.values
66+
let valuesString = values
6667
.map({" \($0) : \($1)"})
6768
.joined(separator: ",\n")
6869
return """
@@ -75,17 +76,37 @@ public struct ConfigurationValues: Sendable, CustomStringConvertible {
7576

7677
extension ConfigurationValues {
7778
/// A type that stores configuration key-value pairs.
78-
internal final class ProperiesBox: @unchecked Sendable {
79+
fileprivate final class ProperiesBox: @unchecked Sendable {
80+
private var lock = os_unfair_lock_s()
81+
7982
/// The stored configuration values.
80-
internal var values: [String: any Sendable]
83+
private var values: [ObjectIdentifier: any Sendable]
8184

8285
/// Creates a new box with the specified initial values.
8386
///
8487
/// - Parameter values: A dictionary of configuration values.
8588
/// Defaults to an empty dictionary.
86-
internal init(values: [String: any Sendable] = [:]) {
89+
fileprivate init(values: [ObjectIdentifier: any Sendable] = [:]) {
8790
self.values = values
8891
}
92+
93+
fileprivate func setValue(_ value: any Sendable, for key: ObjectIdentifier) {
94+
os_unfair_lock_lock(&lock)
95+
defer { os_unfair_lock_unlock(&lock) }
96+
values[key] = value
97+
}
98+
99+
fileprivate func value(for key: ObjectIdentifier) -> (any Sendable)? {
100+
os_unfair_lock_lock(&lock)
101+
defer { os_unfair_lock_unlock(&lock) }
102+
return values[key]
103+
}
104+
105+
fileprivate func allValues() -> [ObjectIdentifier: any Sendable] {
106+
os_unfair_lock_lock(&lock)
107+
defer { os_unfair_lock_unlock(&lock) }
108+
return values
109+
}
89110
}
90111
}
91112

Sources/NetworkingCore/Configurations/DynamicConfigurable.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,21 +64,28 @@ public protocol _DynamicConfigurable {
6464
public func _accept(_ values: ConfigurationValues) {
6565
content.values = values
6666
}
67+
68+
package func setValue<V>(
69+
_ value: V,
70+
for keyPath: WritableKeyPath<ConfigurationValues, V>
71+
) {
72+
content.values[keyPath: keyPath] = value
73+
}
6774
}
6875

6976
extension Configurations {
7077
/// A type that stores the configuration values.
7178
///
7279
/// This internal type enables reference semantics, allowing configurations to be
7380
/// efficiently shared and updated across instances.
74-
internal final class Content {
81+
fileprivate final class Content {
7582
/// The current configuration values.
76-
internal var values: ConfigurationValues
83+
fileprivate var values: ConfigurationValues
7784

7885
/// Creates a new instance with the specified initial values.
7986
///
8087
/// - Parameter values: The initial configuration values. Defaults to a new instance.
81-
init(values: ConfigurationValues = ConfigurationValues()) {
88+
fileprivate init(values: ConfigurationValues = ConfigurationValues()) {
8289
self.values = values
8390
}
8491
}

Sources/NetworkingCore/Request/AnyRequest.swift

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import Foundation
99

1010
/// A type-erased request.
11-
@frozen public struct AnyRequest: Request {
11+
@frozen public struct AnyRequest: Request, Sendable {
1212
/// The contents of the request.
1313
public typealias Contents = Never
1414

@@ -17,12 +17,13 @@ import Foundation
1717
private let storage: AnyRequestStorageBase
1818

1919
/// The request's identifier.
20-
public let id: String
20+
public var id: String {
21+
return storage.id
22+
}
2123

2224
// MARK: - Initializer
2325
/// Create an instance that type-erases `request`.
2426
public init(_ request: some Request) {
25-
self.id = request.id
2627
self.storage = AnyRequestStorage(request: request)
2728
}
2829

@@ -52,27 +53,37 @@ import Foundation
5253

5354
extension AnyRequest {
5455
/// An abstract base class for type-erased request storage.
55-
@usableFromInline internal class AnyRequestStorageBase {
56+
@usableFromInline internal class AnyRequestStorageBase: @unchecked Sendable {
57+
/// The request's identifier.
58+
internal var id: String {
59+
fatalError("Subclasses must override this member.")
60+
}
61+
5662
/// Creates a ``URLRequest`` from the underlying request.
5763
///
5864
/// - Returns: A configured ``URLRequest``.
5965
internal func makeURLRequest() throws -> URLRequest {
60-
fatalError("Subclasses must override this method.")
66+
fatalError("Subclasses must override this member.")
6167
}
6268

6369
/// Applies the specified configuration values to the underlying request.
6470
///
6571
/// - Parameter values: The configuration values to apply.
6672
internal func accept(_ values: ConfigurationValues) {
67-
fatalError("Subclasses must override this method.")
73+
fatalError("Subclasses must override this member.")
6874
}
6975
}
7076

7177
/// A concrete storage type for a specific ``Request`` instance.
72-
fileprivate final class AnyRequestStorage<R: Request>: AnyRequestStorageBase {
78+
fileprivate final class AnyRequestStorage<R: Request>: AnyRequestStorageBase, @unchecked Sendable {
7379
/// The wrapped request.
7480
private let request: R
7581

82+
/// The request's identifier.
83+
fileprivate override var id: String {
84+
return request.id
85+
}
86+
7687
/// Creates a new instance that wraps the given request.
7788
///
7889
/// - Parameter request: The request to wrap.

Sources/NetworkingCore/Request/HTTPRequest.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ extension HTTPRequest: Request {
125125
.modifying(consume urlRequest)
126126
}
127127

128-
return try modifier.modifying(urlRequest)
128+
return try modifier.modifying(consume urlRequest)
129129
}
130130

131131
/// Applies the given configuration values to the request.

Sources/NetworkingCore/Request/ModifiableRequest.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import Foundation
3535
/// which is applied to the resulting ``URLRequest`` before sending.
3636
///
3737
/// - Note: Prefer using ``ModifiedRequest`` over creating custom ``ModifiableRequest``s.
38-
protocol ModifiableRequest: Request {
38+
public protocol ModifiableRequest: Request {
3939
/// The type of modifier applied to the request.
4040
associatedtype Modifier: RequestModifier
4141

@@ -54,9 +54,9 @@ extension ModifiableRequest {
5454
///
5555
/// - Returns: A modified ``URLRequest`` ready to be sent.
5656
/// - Note: This type is prefixed with `_` to indicate that it is not intended for public use.
57-
@inlinable public func _makeURLRequest() throws -> URLRequest {
57+
public func _makeURLRequest() throws -> URLRequest {
5858
let urlRequest = try request._makeURLRequest()
59-
return try modifier.modifying(urlRequest)
59+
return try modifier.modifying(consume urlRequest)
6060
}
6161

6262
/// Applies the given configuration values to the request and its modifier.
@@ -67,7 +67,7 @@ extension ModifiableRequest {
6767
///
6868
/// - Parameter values: The configuration values to apply.
6969
/// - Note: This type is prefixed with `_` to indicate that it is not intended for public use.
70-
@inlinable public func _accept(_ values: ConfigurationValues) {
70+
public func _accept(_ values: ConfigurationValues) {
7171
request._accept(values)
7272
modifier._accept(values)
7373
}

Sources/NetworkingCore/RequestModifiers/Headers/RequestHeader.swift

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,8 @@ extension RequestHeader {
2626
public func modifying(
2727
_ request: consuming URLRequest
2828
) throws -> URLRequest {
29-
if var headersFields = request.allHTTPHeaderFields {
30-
for header in headers {
31-
headersFields[header.key] = header.value
32-
}
33-
request.allHTTPHeaderFields = headersFields
34-
}else {
35-
request.allHTTPHeaderFields = headers
29+
for header in headers {
30+
request.setValue(header.value, forHTTPHeaderField: header.key)
3631
}
3732
return request
3833
}

Sources/NetworkingCore/ResultBuilders/Shared Modifiers/_ConditionalModifier.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,28 +14,28 @@ import Foundation
1414
}
1515
public let storage: Self.Storage
1616

17-
@inlinable public init(storage: Self.Storage) {
17+
public init(storage: Self.Storage) {
1818
self.storage = storage
1919
}
2020
}
2121

2222
// MARK: - RequestModifier
2323
extension _ConditionalModifier: RequestModifier where TrueContent: RequestModifier, FalseContent: RequestModifier {
24-
@inlinable public func modifying(
24+
public func modifying(
2525
_ request: consuming URLRequest
2626
) throws -> URLRequest {
2727
switch storage {
2828
case .trueContent(let mod):
29-
return try mod.modifying(request)
29+
return try mod.modifying(consume request)
3030
case .falseContent(let mod):
31-
return try mod.modifying(request)
31+
return try mod.modifying(consume request)
3232
}
3333
}
3434
}
3535

3636
// MARK: - _DynamicConfigurable
3737
extension _ConditionalModifier: _DynamicConfigurable where TrueContent: _DynamicConfigurable, FalseContent: _DynamicConfigurable {
38-
@inlinable public func _accept(_ values: ConfigurationValues) {
38+
public func _accept(_ values: ConfigurationValues) {
3939
switch storage {
4040
case .trueContent(let mod):
4141
mod._accept(values)
@@ -59,7 +59,7 @@ extension _ConditionalModifier: CustomStringConvertible where TrueContent: Custo
5959

6060
// MARK: - RequestHeader
6161
extension _ConditionalModifier: RequestHeader where TrueContent: RequestHeader, FalseContent: RequestHeader {
62-
@inlinable public var headers: [String : String] {
62+
public var headers: [String : String] {
6363
switch storage {
6464
case .trueContent(let header):
6565
return header.headers
@@ -71,7 +71,7 @@ extension _ConditionalModifier: RequestHeader where TrueContent: RequestHeader,
7171

7272
// MARK: - RequestParameter
7373
extension _ConditionalModifier: RequestParameter where TrueContent: RequestParameter, FalseContent: RequestParameter {
74-
@inlinable public var parameters: [URLQueryItem] {
74+
public var parameters: [URLQueryItem] {
7575
switch storage {
7676
case .trueContent(let parameter):
7777
return parameter.parameters

Sources/NetworkingCore/ResultBuilders/Shared Modifiers/_OptionalModifier.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,26 @@ import Foundation
1010
@frozen public struct _OptionalModifier<Modifier> {
1111
public let storage: Modifier?
1212

13-
@inlinable public init(storage: Modifier?) {
13+
public init(storage: Modifier?) {
1414
self.storage = storage
1515
}
1616
}
1717

1818
// MARK: - RequestModifier
1919
extension _OptionalModifier: RequestModifier where Modifier: RequestModifier {
20-
@inlinable public func modifying(
20+
public func modifying(
2121
_ request: consuming URLRequest
2222
) throws -> URLRequest {
2323
guard let storage else {
2424
return request
2525
}
26-
return try storage.modifying(request)
26+
return try storage.modifying(consume request)
2727
}
2828
}
2929

3030
// MARK: - _DynamicConfigurable
3131
extension _OptionalModifier: _DynamicConfigurable where Modifier: _DynamicConfigurable {
32-
@inlinable public func _accept(_ values: ConfigurationValues) {
32+
public func _accept(_ values: ConfigurationValues) {
3333
storage?._accept(values)
3434
}
3535
}
@@ -43,14 +43,14 @@ extension _OptionalModifier: CustomStringConvertible where Modifier: CustomStrin
4343

4444
// MARK: - RequestHeader
4545
extension _OptionalModifier: RequestHeader where Modifier: RequestHeader {
46-
@inlinable public var headers: [String : String] {
46+
public var headers: [String : String] {
4747
return storage?.headers ?? [:]
4848
}
4949
}
5050

5151
// MARK: - RequestParameter
5252
extension _OptionalModifier: RequestParameter where Modifier: RequestParameter {
53-
@inlinable public var parameters: [URLQueryItem] {
53+
public var parameters: [URLQueryItem] {
5454
return storage?.parameters ?? []
5555
}
5656
}

Sources/NetworkingCore/ResultBuilders/Shared Modifiers/_TupleModifier.swift

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import Foundation
1414
// MARK: - RequestModifier
1515
extension _TupleModifier: RequestModifier where M0: RequestModifier, M1: RequestModifier, M2: RequestModifier, M3: RequestModifier, M4: RequestModifier, M5: RequestModifier, M6: RequestModifier, M7: RequestModifier, M8: RequestModifier, M9: RequestModifier {
1616

17-
@inlinable public init(
17+
public init(
1818
_ m0: M0,
1919
_ m1: M1,
2020
_ m2: M2 = EmptyModifier(),
@@ -29,28 +29,27 @@ extension _TupleModifier: RequestModifier where M0: RequestModifier, M1: Request
2929
self.value = (m0, m1, m2, m3, m4, m5, m6, m7, m8, m9)
3030
}
3131

32-
@inlinable public func modifying(
32+
public func modifying(
3333
_ request: consuming URLRequest
3434
) throws -> URLRequest {
35-
var urlRequest = request
36-
try urlRequest = value.0.modifying(consume urlRequest)
37-
try urlRequest = value.1.modifying(consume urlRequest)
38-
try urlRequest = value.2.modifying(consume urlRequest)
39-
try urlRequest = value.3.modifying(consume urlRequest)
40-
try urlRequest = value.4.modifying(consume urlRequest)
41-
try urlRequest = value.5.modifying(consume urlRequest)
42-
try urlRequest = value.6.modifying(consume urlRequest)
43-
try urlRequest = value.7.modifying(consume urlRequest)
44-
try urlRequest = value.8.modifying(consume urlRequest)
45-
try urlRequest = value.9.modifying(consume urlRequest)
35+
var urlRequest = try value.0.modifying(consume request)
36+
urlRequest = try value.1.modifying(consume urlRequest)
37+
urlRequest = try value.2.modifying(consume urlRequest)
38+
urlRequest = try value.3.modifying(consume urlRequest)
39+
urlRequest = try value.4.modifying(consume urlRequest)
40+
urlRequest = try value.5.modifying(consume urlRequest)
41+
urlRequest = try value.6.modifying(consume urlRequest)
42+
urlRequest = try value.7.modifying(consume urlRequest)
43+
urlRequest = try value.8.modifying(consume urlRequest)
44+
urlRequest = try value.9.modifying(consume urlRequest)
4645
return urlRequest
4746
}
4847
}
4948

5049
// MARK: - _DynamicConfigurable
5150
extension _TupleModifier: _DynamicConfigurable where M0: _DynamicConfigurable, M1: _DynamicConfigurable, M2: _DynamicConfigurable, M3: _DynamicConfigurable, M4: _DynamicConfigurable, M5: _DynamicConfigurable, M6: _DynamicConfigurable, M7: _DynamicConfigurable, M8: _DynamicConfigurable, M9: _DynamicConfigurable {
5251

53-
@inlinable public func _accept(_ values: ConfigurationValues) {
52+
public func _accept(_ values: ConfigurationValues) {
5453
value.0._accept(values)
5554
value.1._accept(values)
5655
value.2._accept(values)
@@ -83,15 +82,15 @@ extension _TupleModifier: CustomStringConvertible where M0: CustomStringConverti
8382

8483
// MARK: - RequestParameter
8584
extension _TupleModifier: RequestParameter where M0: RequestParameter, M1: RequestParameter, M2: RequestParameter, M3: RequestParameter, M4: RequestParameter, M5: RequestParameter, M6: RequestParameter, M7: RequestParameter, M8: RequestParameter, M9: RequestParameter {
86-
@inlinable public var parameters: [URLQueryItem] {
85+
public var parameters: [URLQueryItem] {
8786
return value.0.parameters + value.1.parameters + value.2.parameters + value.3.parameters + value.4.parameters + value.5.parameters + value.6.parameters + value.7.parameters + value.8.parameters + value.9.parameters
8887
}
8988
}
9089

9190
// MARK: - RequestHeader
9291
extension _TupleModifier: RequestHeader where M0: RequestHeader, M1: RequestHeader, M2: RequestHeader, M3: RequestHeader, M4: RequestHeader, M5: RequestHeader, M6: RequestHeader, M7: RequestHeader, M8: RequestHeader, M9: RequestHeader {
9392

94-
@inlinable public var headers: [String : String] {
93+
public var headers: [String : String] {
9594
value.0.headers
9695
.merging(value.1.headers, uniquingKeysWith: { $1 })
9796
.merging(value.2.headers, uniquingKeysWith: { $1 })

0 commit comments

Comments
 (0)