Skip to content

Commit b2ea94c

Browse files
committed
Remove unnecessary genericism and generic constraints.
Motivation: Early on in the design of Structured Headers I made a conscious effort to try to avoid transforming types in the low-level API. This design was built on the assumption that strings, tokens, and some other data types could be built entirely as slices of the backing data. Unfortunately, this isn't true: because of the need to escape chars in Strings, it became necessary to start using more complex types in Item. Eventually, we ended up in a place where the only field in Item that actually stored backing data was `.undecodedByteSequence`. This field forced almost the entire set of types in the StructuredHeaders module to be generic, just for the sake of carrying this one slice type around. This prevented powerful optimisations, bloated code size, and forced additional unnecessary heap allocations. It also forced us to constrain the types used in the parser to those where the subsequence type was Hashable, which excluded some really important types (like `String.UTF8View`). Fundamentally, this genericism was hurting more than it helped at this point, so I bit the bullet and replaced the slice type with `String` for `undecodedByteSequence`. This led to a huge cascading removal of generic type parameters for a wide range of types, as well as the removal of the "must have hashable subsequence" constraints on the parser data type. This lifts an enormous amount of complexity out of the module at the extremely minor cost of forcing an allocation whenever we have a lot of undecoded binary data: not a huge cost, in my view, and well worth what we got back in return. Modifications: - Remove generic type parameter in BaseItem. - Remove generic types everywhere else. - Remove hashable constraint. Result: Better, faster, simpler code.
1 parent 246d886 commit b2ea94c

16 files changed

+131
-133
lines changed

Sources/CodableStructuredHeaders/Decoder/BareInnerListDecoder.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@
1414
import Foundation
1515
import StructuredHeaders
1616

17-
struct BareInnerListDecoder<BaseData: RandomAccessCollection> where BaseData.Element == UInt8, BaseData.SubSequence: Hashable {
18-
private var list: BareInnerList<BaseData.SubSequence>
17+
struct BareInnerListDecoder<BaseData: RandomAccessCollection> where BaseData.Element == UInt8 {
18+
private var list: BareInnerList
1919

2020
private var currentOffset: Int
2121

2222
private var decoder: _StructuredFieldDecoder<BaseData>
2323

24-
init(_ list: BareInnerList<BaseData.SubSequence>, decoder: _StructuredFieldDecoder<BaseData>) {
24+
init(_ list: BareInnerList, decoder: _StructuredFieldDecoder<BaseData>) {
2525
self.list = list
2626
self.currentOffset = 0
2727
self.decoder = decoder

Sources/CodableStructuredHeaders/Decoder/BareItemDecoder.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@
1414
import Foundation
1515
import StructuredHeaders
1616

17-
struct BareItemDecoder<BaseData: RandomAccessCollection> where BaseData.Element == UInt8, BaseData.SubSequence == BaseData, BaseData: Hashable {
18-
private var item: BareItem<BaseData>
17+
struct BareItemDecoder {
18+
private var item: BareItem
1919

2020
private var _codingPath: [_StructuredHeaderCodingKey]
2121

22-
init(_ item: BareItem<BaseData>, codingPath: [_StructuredHeaderCodingKey]) {
22+
init(_ item: BareItem, codingPath: [_StructuredHeaderCodingKey]) {
2323
self.item = item
2424
self._codingPath = codingPath
2525
}
@@ -102,7 +102,7 @@ extension BareItemDecoder: SingleValueDecodingContainer {
102102
throw StructuredHeaderError.invalidTypeForItem
103103
}
104104

105-
guard let decoded = Data(base64Encoded: Data(data)) else {
105+
guard let decoded = Data(base64Encoded: data) else {
106106
throw StructuredHeaderError.invalidByteSequence
107107
}
108108

Sources/CodableStructuredHeaders/Decoder/DictionaryKeyedContainer.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@
1414
import Foundation
1515
import StructuredHeaders
1616

17-
struct DictionaryKeyedContainer<Key: CodingKey, BaseData: RandomAccessCollection> where BaseData.Element == UInt8, BaseData.SubSequence: Hashable {
18-
private var dictionary: OrderedMap<String, ItemOrInnerList<BaseData.SubSequence>>
17+
struct DictionaryKeyedContainer<Key: CodingKey, BaseData: RandomAccessCollection> where BaseData.Element == UInt8 {
18+
private var dictionary: OrderedMap<String, ItemOrInnerList>
1919

2020
private var decoder: _StructuredFieldDecoder<BaseData>
2121

22-
init(_ dictionary: OrderedMap<String, ItemOrInnerList<BaseData.SubSequence>>, decoder: _StructuredFieldDecoder<BaseData>) {
22+
init(_ dictionary: OrderedMap<String, ItemOrInnerList>, decoder: _StructuredFieldDecoder<BaseData>) {
2323
self.dictionary = dictionary
2424
self.decoder = decoder
2525
}

Sources/CodableStructuredHeaders/Decoder/KeyedInnerListDecoder.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ private let keyedInnerListDecoderSupportedKeys = ["items", "parameters"]
1919
/// Used when someone has requested a keyed decoder for a property of inner list type.
2020
///
2121
/// There are only two valid keys for this: "items" and "parameters".
22-
struct KeyedInnerListDecoder<Key: CodingKey, BaseData: RandomAccessCollection> where BaseData.Element == UInt8, BaseData.SubSequence: Hashable {
23-
private var innerList: InnerList<BaseData.SubSequence>
22+
struct KeyedInnerListDecoder<Key: CodingKey, BaseData: RandomAccessCollection> where BaseData.Element == UInt8 {
23+
private var innerList: InnerList
2424

2525
private var decoder: _StructuredFieldDecoder<BaseData>
2626

27-
init(_ innerList: InnerList<BaseData.SubSequence>, decoder: _StructuredFieldDecoder<BaseData>) {
27+
init(_ innerList: InnerList, decoder: _StructuredFieldDecoder<BaseData>) {
2828
self.innerList = innerList
2929
self.decoder = decoder
3030
}

Sources/CodableStructuredHeaders/Decoder/KeyedItemDecoder.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ private let keyedItemDecoderSupportedKeys = ["item", "parameters"]
1919
/// Used when someone has requested a keyed decoder for a property of item type.
2020
///
2121
/// There are only two valid keys for this: "item" and "parameters".
22-
struct KeyedItemDecoder<Key: CodingKey, BaseData: RandomAccessCollection> where BaseData.Element == UInt8, BaseData.SubSequence: Hashable {
23-
private var item: Item<BaseData.SubSequence>
22+
struct KeyedItemDecoder<Key: CodingKey, BaseData: RandomAccessCollection> where BaseData.Element == UInt8 {
23+
private var item: Item
2424

2525
private var decoder: _StructuredFieldDecoder<BaseData>
2626

27-
init(_ item: Item<BaseData.SubSequence>, decoder: _StructuredFieldDecoder<BaseData>) {
27+
init(_ item: Item, decoder: _StructuredFieldDecoder<BaseData>) {
2828
self.item = item
2929
self.decoder = decoder
3030
}

Sources/CodableStructuredHeaders/Decoder/KeyedTopLevelListDecoder.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ private let keyedTopLevelListDecoderSupportedKeys = ["items"]
1919
/// Used when someone has requested a keyed decoder for a property of list type.
2020
///
2121
/// There is only one valid key for this: "items".
22-
struct KeyedTopLevelListDecoder<Key: CodingKey, BaseData: RandomAccessCollection> where BaseData.Element == UInt8, BaseData.SubSequence: Hashable {
23-
private var list: [ItemOrInnerList<BaseData.SubSequence>]
22+
struct KeyedTopLevelListDecoder<Key: CodingKey, BaseData: RandomAccessCollection> where BaseData.Element == UInt8 {
23+
private var list: [ItemOrInnerList]
2424

2525
private var decoder: _StructuredFieldDecoder<BaseData>
26-
27-
init(_ list: [ItemOrInnerList<BaseData.SubSequence>], decoder: _StructuredFieldDecoder<BaseData>) {
26+
27+
init(_ list: [ItemOrInnerList], decoder: _StructuredFieldDecoder<BaseData>) {
2828
self.list = list
2929
self.decoder = decoder
3030
}

Sources/CodableStructuredHeaders/Decoder/ParametersDecoder.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@
1414
import Foundation
1515
import StructuredHeaders
1616

17-
struct ParametersDecoder<Key: CodingKey, BaseData: RandomAccessCollection> where BaseData.Element == UInt8, BaseData.SubSequence: Hashable {
18-
private var parameters: OrderedMap<String, BareItem<BaseData.SubSequence>>
17+
struct ParametersDecoder<Key: CodingKey, BaseData: RandomAccessCollection> where BaseData.Element == UInt8 {
18+
private var parameters: OrderedMap<String, BareItem>
1919

2020
private var decoder: _StructuredFieldDecoder<BaseData>
2121

22-
init(_ parameters: OrderedMap<String, BareItem<BaseData.SubSequence>>, decoder: _StructuredFieldDecoder<BaseData>) {
22+
init(_ parameters: OrderedMap<String, BareItem>, decoder: _StructuredFieldDecoder<BaseData>) {
2323
self.parameters = parameters
2424
self.decoder = decoder
2525
}

Sources/CodableStructuredHeaders/Decoder/StructuredFieldDecoder.swift

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ extension StructuredFieldDecoder {
4646
/// - data: The bytes of the structured header field.
4747
/// - throws: If the header field could not be parsed, or could not be decoded.
4848
/// - returns: An object of type `StructuredField`.
49-
public func decode<StructuredField: StructuredHeaderField, BaseData: RandomAccessCollection>(_ type: StructuredField.Type = StructuredField.self, from data: BaseData) throws -> StructuredField where BaseData.Element == UInt8, BaseData.SubSequence: Hashable {
49+
public func decode<StructuredField: StructuredHeaderField, BaseData: RandomAccessCollection>(_ type: StructuredField.Type = StructuredField.self, from data: BaseData) throws -> StructuredField where BaseData.Element == UInt8 {
5050
switch StructuredField.structuredFieldType {
5151
case .item:
5252
return try self.decodeItemField(from: data)
@@ -64,7 +64,7 @@ extension StructuredFieldDecoder {
6464
/// - data: The bytes of the structured header field.
6565
/// - throws: If the header field could not be parsed, or could not be decoded.
6666
/// - returns: An object of type `StructuredField`.
67-
private func decodeDictionaryField<StructuredField: Decodable, BaseData: RandomAccessCollection>(_ type: StructuredField.Type = StructuredField.self, from data: BaseData) throws -> StructuredField where BaseData.Element == UInt8, BaseData.SubSequence: Hashable {
67+
private func decodeDictionaryField<StructuredField: Decodable, BaseData: RandomAccessCollection>(_ type: StructuredField.Type = StructuredField.self, from data: BaseData) throws -> StructuredField where BaseData.Element == UInt8 {
6868
let parser = StructuredFieldParser(data)
6969
let decoder = _StructuredFieldDecoder(parser, keyDecodingStrategy: self.keyDecodingStrategy)
7070
try decoder.parseDictionaryField()
@@ -78,7 +78,7 @@ extension StructuredFieldDecoder {
7878
/// - data: The bytes of the structured header field.
7979
/// - throws: If the header field could not be parsed, or could not be decoded.
8080
/// - returns: An object of type `StructuredField`.
81-
private func decodeListField<StructuredField: Decodable, BaseData: RandomAccessCollection>(_ type: StructuredField.Type = StructuredField.self, from data: BaseData) throws -> StructuredField where BaseData.Element == UInt8, BaseData.SubSequence: Hashable {
81+
private func decodeListField<StructuredField: Decodable, BaseData: RandomAccessCollection>(_ type: StructuredField.Type = StructuredField.self, from data: BaseData) throws -> StructuredField where BaseData.Element == UInt8 {
8282
let parser = StructuredFieldParser(data)
8383
let decoder = _StructuredFieldDecoder(parser, keyDecodingStrategy: self.keyDecodingStrategy)
8484
try decoder.parseListField()
@@ -92,7 +92,7 @@ extension StructuredFieldDecoder {
9292
/// - data: The bytes of the structured header field.
9393
/// - throws: If the header field could not be parsed, or could not be decoded.
9494
/// - returns: An object of type `StructuredField`.
95-
private func decodeItemField<StructuredField: Decodable, BaseData: RandomAccessCollection>(_ type: StructuredField.Type = StructuredField.self, from data: BaseData) throws -> StructuredField where BaseData.Element == UInt8, BaseData.SubSequence: Hashable {
95+
private func decodeItemField<StructuredField: Decodable, BaseData: RandomAccessCollection>(_ type: StructuredField.Type = StructuredField.self, from data: BaseData) throws -> StructuredField where BaseData.Element == UInt8 {
9696
let parser = StructuredFieldParser(data)
9797
let decoder = _StructuredFieldDecoder(parser, keyDecodingStrategy: self.keyDecodingStrategy)
9898
try decoder.parseItemField()
@@ -111,7 +111,7 @@ extension StructuredFieldDecoder {
111111
}
112112
}
113113

114-
class _StructuredFieldDecoder<BaseData: RandomAccessCollection> where BaseData.Element == UInt8, BaseData.SubSequence: Hashable {
114+
class _StructuredFieldDecoder<BaseData: RandomAccessCollection> where BaseData.Element == UInt8 {
115115
private var parser: StructuredFieldParser<BaseData>
116116

117117
// For now we use a stack here because the CoW operations on Array would suck. Ideally I'd just have us decode
@@ -220,13 +220,13 @@ extension _StructuredFieldDecoder: Decoder {
220220
extension _StructuredFieldDecoder {
221221
/// The basic elements that make up a Structured Header
222222
fileprivate enum Element {
223-
case dictionary(OrderedMap<String, ItemOrInnerList<BaseData.SubSequence>>)
224-
case list([ItemOrInnerList<BaseData.SubSequence>])
225-
case item(Item<BaseData.SubSequence>)
226-
case innerList(InnerList<BaseData.SubSequence>)
227-
case bareItem(BareItem<BaseData.SubSequence>)
228-
case bareInnerList(BareInnerList<BaseData.SubSequence>)
229-
case parameters(OrderedMap<String, BareItem<BaseData.SubSequence>>)
223+
case dictionary(OrderedMap<String, ItemOrInnerList>)
224+
case list([ItemOrInnerList])
225+
case item(Item)
226+
case innerList(InnerList)
227+
case bareItem(BareItem)
228+
case bareInnerList(BareInnerList)
229+
case parameters(OrderedMap<String, BareItem>)
230230

231231
func innerElement(for key: _StructuredHeaderCodingKey) throws -> Element {
232232
switch self {

Sources/CodableStructuredHeaders/Decoder/TopLevelListDecoder.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@
1414
import Foundation
1515
import StructuredHeaders
1616

17-
struct TopLevelListDecoder<BaseData: RandomAccessCollection> where BaseData.Element == UInt8, BaseData.SubSequence: Hashable {
18-
private var list: [ItemOrInnerList<BaseData.SubSequence>]
17+
struct TopLevelListDecoder<BaseData: RandomAccessCollection> where BaseData.Element == UInt8 {
18+
private var list: [ItemOrInnerList]
1919

2020
private var currentOffset: Int
2121

2222
private var decoder: _StructuredFieldDecoder<BaseData>
2323

24-
init(_ list: [ItemOrInnerList<BaseData.SubSequence>], decoder: _StructuredFieldDecoder<BaseData>) {
24+
init(_ list: [ItemOrInnerList], decoder: _StructuredFieldDecoder<BaseData>) {
2525
self.list = list
2626
self.currentOffset = 0
2727
self.decoder = decoder

Sources/CodableStructuredHeaders/Encoder/StructuredFieldEncoder.swift

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ extension _StructuredFieldEncoder: SingleValueEncodingContainer {
276276
}
277277

278278
func encode(_ data: Data) throws {
279-
let encoded = data.base64EncodedData()
279+
let encoded = data.base64EncodedString()
280280
try self.currentStackEntry.storage.insertBareItem(.undecodedByteSequence(encoded))
281281
}
282282

@@ -434,7 +434,7 @@ extension _StructuredFieldEncoder {
434434
}
435435

436436
func append(_ value: Data) throws {
437-
try self.currentStackEntry.storage.appendBareItem(.undecodedByteSequence(value.base64EncodedData()))
437+
try self.currentStackEntry.storage.appendBareItem(.undecodedByteSequence(value.base64EncodedString()))
438438
}
439439

440440
func append(_ value: Decimal) throws {
@@ -601,7 +601,7 @@ extension _StructuredFieldEncoder {
601601

602602
func encode(_ value: Data, forKey key: String) throws {
603603
let key = self.sanitizeKey(key)
604-
try self.currentStackEntry.storage.insertBareItem(.undecodedByteSequence(value.base64EncodedData()), atKey: key)
604+
try self.currentStackEntry.storage.insertBareItem(.undecodedByteSequence(value.base64EncodedString()), atKey: key)
605605
}
606606

607607
func encode(_ value: Decimal, forKey key: String) throws {
@@ -758,24 +758,22 @@ extension _StructuredFieldEncoder {
758758
/// Note that we never have a bare item here. This is deliberate: bare items
759759
/// are not a container for anything else, and so can never appear.
760760
internal enum NodeType {
761-
typealias DataType = Data
762-
763761
case dictionaryHeader
764762
case listHeader
765763
case itemHeader
766-
case dictionary(OrderedMap<String, ItemOrInnerList<DataType>>)
767-
case list([ItemOrInnerList<DataType>])
768-
case innerList(InnerList<DataType>)
764+
case dictionary(OrderedMap<String, ItemOrInnerList>)
765+
case list([ItemOrInnerList])
766+
case innerList(InnerList)
769767
case item(PartialItem)
770-
case bareInnerList(BareInnerList<DataType>)
771-
case parameters(OrderedMap<String, BareItem<DataType>>)
772-
case itemOrInnerList(OrderedMap<String, BareItem<DataType>>)
768+
case bareInnerList(BareInnerList)
769+
case parameters(OrderedMap<String, BareItem>)
770+
case itemOrInnerList(OrderedMap<String, BareItem>)
773771

774772
/// A helper struct used to tolerate the fact that we need partial items,
775773
/// but our `Item` struct doesn't like that much.
776774
struct PartialItem {
777-
var bareItem: BareItem<DataType>?
778-
var parameters: OrderedMap<String, BareItem<DataType>>
775+
var bareItem: BareItem?
776+
var parameters: OrderedMap<String, BareItem>
779777
}
780778

781779
/// This is called when a complete object has been built.
@@ -848,7 +846,7 @@ extension _StructuredFieldEncoder {
848846
///
849847
/// If the key is missing we will require the type to be `item`, in which case
850848
/// this will be for the "item" key.
851-
mutating func insertBareItem(_ bareItem: BareItem<DataType>, atKey key: String? = nil) throws {
849+
mutating func insertBareItem(_ bareItem: BareItem, atKey key: String? = nil) throws {
852850
switch self {
853851
case .itemHeader:
854852
guard key == nil || key == "item" else {
@@ -875,7 +873,7 @@ extension _StructuredFieldEncoder {
875873

876874
case .dictionaryHeader:
877875
// Ok cool, this is a dictionary.
878-
var map = OrderedMap<String, ItemOrInnerList<DataType>>()
876+
var map = OrderedMap<String, ItemOrInnerList>()
879877

880878
// Bare item here means item, no parameters.
881879
map[key!] = .item(.init(bareItem: bareItem, parameters: [:]))
@@ -897,7 +895,7 @@ extension _StructuredFieldEncoder {
897895

898896
/// Appends a bare item to the given container. This must be a list-type
899897
/// container that stores either bare items, or items.
900-
mutating func appendBareItem(_ bareItem: BareItem<DataType>) throws {
898+
mutating func appendBareItem(_ bareItem: BareItem) throws {
901899
switch self {
902900
case .listHeader:
903901
self = .list([.item(Item(bareItem: bareItem, parameters: [:]))])
@@ -926,7 +924,7 @@ extension _StructuredFieldEncoder {
926924
}
927925
}
928926

929-
extension Item where BaseData == _StructuredFieldEncoder.NodeType.DataType {
927+
extension Item {
930928
fileprivate init(_ partialItem: _StructuredFieldEncoder.NodeType.PartialItem) {
931929
self.init(bareItem: partialItem.bareItem!, parameters: partialItem.parameters)
932930
}

0 commit comments

Comments
 (0)