Skip to content

Commit 2d2eb29

Browse files
committed
Eliminate the allocation of single element arrays
1 parent 99d066e commit 2d2eb29

File tree

1 file changed

+59
-34
lines changed

1 file changed

+59
-34
lines changed

Sources/HTTPTypes/HTTPFields.swift

Lines changed: 59 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,10 @@ public struct HTTPFields: Sendable, Hashable {
172172
/// semicolon.
173173
public subscript(name: HTTPField.Name) -> String? {
174174
get {
175-
let values = self[values: name]
176-
if !values.isEmpty {
175+
let values = self.fields(for: name)
176+
if values.first(where: { _ in true }) != nil {
177177
let separator = name == .cookie ? "; " : ", "
178-
return values.joined(separator: separator)
178+
return values.lazy.map(\.value).joined(separator: separator)
179179
} else {
180180
return nil
181181
}
@@ -184,62 +184,87 @@ public struct HTTPFields: Sendable, Hashable {
184184
if let newValue {
185185
if #available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *),
186186
name == .cookie {
187-
self[fields: name] = newValue.split(separator: "; ", omittingEmptySubsequences: false).map {
187+
self.setFields(newValue.split(separator: "; ", omittingEmptySubsequences: false).lazy.map {
188188
HTTPField(name: name, value: String($0))
189-
}
189+
}, for: name)
190190
} else {
191-
self[values: name] = [newValue]
191+
self.setFields(CollectionOfOne(HTTPField(name: name, value: newValue)), for: name)
192192
}
193193
} else {
194-
self[values: name] = []
194+
self.setFields(EmptyCollection(), for: name)
195195
}
196196
}
197197
}
198198

199199
/// Access the field values by name as an array of strings. The order of fields is preserved.
200200
public subscript(values name: HTTPField.Name) -> [String] {
201201
get {
202-
self[fields: name].map(\.value)
202+
self.fields(for: name).map(\.value)
203203
}
204204
set {
205-
self[fields: name] = newValue.map { HTTPField(name: name, value: $0) }
205+
self.setFields(newValue.lazy.map { HTTPField(name: name, value: $0) }, for: name)
206206
}
207207
}
208208

209209
/// Access the fields by name as an array. The order of fields is preserved.
210210
public subscript(fields name: HTTPField.Name) -> [HTTPField] {
211211
get {
212-
var fields = [HTTPField]()
213-
var index = self._storage.ensureIndex[name.canonicalName]?.first ?? .max
214-
while index != .max {
215-
let (field, next) = self._storage.fields[Int(index)]
216-
fields.append(field)
217-
index = next
218-
}
219-
return fields
212+
Array(self.fields(for: name))
220213
}
221214
set {
222-
if !isKnownUniquelyReferenced(&self._storage) {
223-
self._storage = self._storage.copy()
224-
}
225-
var existingIndex = self._storage.ensureIndex[name.canonicalName]?.first ?? .max
226-
var newFieldIterator = newValue.makeIterator()
227-
var toDelete = [Int]()
228-
while existingIndex != .max {
229-
if let field = newFieldIterator.next() {
230-
self._storage.fields[Int(existingIndex)].field = field
231-
} else {
232-
toDelete.append(Int(existingIndex))
215+
self.setFields(newValue, for: name)
216+
}
217+
}
218+
219+
private func fields(for name: HTTPField.Name) -> some Sequence<HTTPField> {
220+
struct HTTPFieldSequence: Sequence {
221+
let fields: [(field: HTTPField, next: UInt16)]
222+
let index: UInt16
223+
224+
struct Iterator: IteratorProtocol {
225+
let fields: [(field: HTTPField, next: UInt16)]
226+
var index: UInt16
227+
228+
mutating func next() -> HTTPField? {
229+
if self.index == .max {
230+
return nil
231+
}
232+
let (field, next) = self.fields[Int(self.index)]
233+
self.index = next
234+
return field
233235
}
234-
existingIndex = self._storage.fields[Int(existingIndex)].next
235236
}
236-
if !toDelete.isEmpty {
237-
self._storage.fields.remove(at: toDelete)
238-
self._storage.index = nil
237+
238+
func makeIterator() -> Iterator {
239+
Iterator(fields: self.fields, index: self.index)
239240
}
240-
while let field = newFieldIterator.next() {
241-
self._storage.append(field: field)
241+
}
242+
243+
let index = self._storage.ensureIndex[name.canonicalName]?.first ?? .max
244+
return HTTPFieldSequence(fields: self._storage.fields, index: index)
245+
}
246+
247+
private mutating func setFields(_ fieldSequence: some Sequence<HTTPField>, for name: HTTPField.Name) {
248+
if !isKnownUniquelyReferenced(&self._storage) {
249+
self._storage = self._storage.copy()
250+
}
251+
var existingIndex = self._storage.ensureIndex[name.canonicalName]?.first ?? .max
252+
var newFieldIterator = fieldSequence.makeIterator()
253+
var toDelete = [Int]()
254+
while existingIndex != .max {
255+
if let field = newFieldIterator.next() {
256+
self._storage.fields[Int(existingIndex)].field = field
257+
} else {
258+
toDelete.append(Int(existingIndex))
242259
}
260+
existingIndex = self._storage.fields[Int(existingIndex)].next
261+
}
262+
if !toDelete.isEmpty {
263+
self._storage.fields.remove(at: toDelete)
264+
self._storage.index = nil
265+
}
266+
while let field = newFieldIterator.next() {
267+
self._storage.append(field: field)
243268
}
244269
}
245270

0 commit comments

Comments
 (0)