Skip to content

Commit b797a6f

Browse files
committed
Add support for encoding/decoding binary data
1 parent 5aaca63 commit b797a6f

11 files changed

+211
-12
lines changed

Sources/CodableStructuredHeaders/Decoder/BareInnerListDecoder.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
// SPDX-License-Identifier: Apache-2.0
1212
//
1313
//===----------------------------------------------------------------------===//
14+
import Foundation
1415
import StructuredHeaders
1516

1617
struct BareInnerListDecoder<BaseData: RandomAccessCollection> where BaseData.Element == UInt8, BaseData.SubSequence: Hashable {
@@ -64,7 +65,13 @@ extension BareInnerListDecoder: UnkeyedDecodingContainer {
6465
defer {
6566
self.decoder.pop()
6667
}
67-
return try type.init(from: self.decoder)
68+
69+
if type is Data.Type {
70+
let container = try self.decoder.singleValueContainer()
71+
return try container.decode(Data.self) as! T
72+
} else {
73+
return try type.init(from: self.decoder)
74+
}
6875
}
6976

7077
mutating func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {

Sources/CodableStructuredHeaders/Decoder/BareItemDecoder.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
// SPDX-License-Identifier: Apache-2.0
1212
//
1313
//===----------------------------------------------------------------------===//
14+
import Foundation
1415
import StructuredHeaders
1516

1617
struct BareItemDecoder<BaseData: RandomAccessCollection> where BaseData.Element == UInt8, BaseData.SubSequence == BaseData, BaseData: Hashable {
@@ -96,6 +97,18 @@ extension BareItemDecoder: SingleValueDecodingContainer {
9697
return bool
9798
}
9899

100+
func decode(_ type: Data.Type) throws -> Data {
101+
guard case .undecodedByteSequence(let data) = self.item else {
102+
throw StructuredHeaderError.invalidTypeForItem
103+
}
104+
105+
guard let decoded = Data(base64Encoded: Data(data)) else {
106+
throw StructuredHeaderError.invalidByteSequence
107+
}
108+
109+
return decoded
110+
}
111+
99112
func decodeNil() -> Bool {
100113
// Items are never nil.
101114
return false
@@ -131,6 +144,8 @@ extension BareItemDecoder: SingleValueDecodingContainer {
131144
return try self.decode(String.self) as! T
132145
case is Bool.Type:
133146
return try self.decode(Bool.self) as! T
147+
case is Data.Type:
148+
return try self.decode(Data.self) as! T
134149
default:
135150
// Some other codable type. Not sure what to do here yet.
136151
// TODO: What about binary data here? For now we'll ignore it.

Sources/CodableStructuredHeaders/Decoder/DictionaryKeyedContainer.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
// SPDX-License-Identifier: Apache-2.0
1212
//
1313
//===----------------------------------------------------------------------===//
14+
import Foundation
1415
import StructuredHeaders
1516

1617
struct DictionaryKeyedContainer<Key: CodingKey, BaseData: RandomAccessCollection> where BaseData.Element == UInt8, BaseData.SubSequence: Hashable {
@@ -47,7 +48,13 @@ extension DictionaryKeyedContainer: KeyedDecodingContainerProtocol {
4748
defer {
4849
self.decoder.pop()
4950
}
50-
return try type.init(from: self.decoder)
51+
52+
if type is Data.Type {
53+
let container = try self.decoder.singleValueContainer()
54+
return try container.decode(Data.self) as! T
55+
} else {
56+
return try type.init(from: self.decoder)
57+
}
5158
}
5259

5360
func nestedContainer<NestedKey: CodingKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer<NestedKey> {

Sources/CodableStructuredHeaders/Decoder/KeyedInnerListDecoder.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
// SPDX-License-Identifier: Apache-2.0
1212
//
1313
//===----------------------------------------------------------------------===//
14+
import Foundation
1415
import StructuredHeaders
1516

1617
private let keyedInnerListDecoderSupportedKeys = ["items", "parameters"]
@@ -52,7 +53,13 @@ extension KeyedInnerListDecoder: KeyedDecodingContainerProtocol {
5253
defer {
5354
self.decoder.pop()
5455
}
55-
return try type.init(from: self.decoder)
56+
57+
if type is Data.Type {
58+
let container = try self.decoder.singleValueContainer()
59+
return try container.decode(Data.self) as! T
60+
} else {
61+
return try type.init(from: self.decoder)
62+
}
5663
}
5764

5865
func nestedContainer<NestedKey: CodingKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer<NestedKey> {

Sources/CodableStructuredHeaders/Decoder/KeyedItemDecoder.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
// SPDX-License-Identifier: Apache-2.0
1212
//
1313
//===----------------------------------------------------------------------===//
14+
import Foundation
1415
import StructuredHeaders
1516

1617
private let keyedItemDecoderSupportedKeys = ["item", "parameters"]
@@ -52,7 +53,13 @@ extension KeyedItemDecoder: KeyedDecodingContainerProtocol {
5253
defer {
5354
self.decoder.pop()
5455
}
55-
return try type.init(from: self.decoder)
56+
57+
if type is Data.Type {
58+
let container = try self.decoder.singleValueContainer()
59+
return try container.decode(Data.self) as! T
60+
} else {
61+
return try type.init(from: self.decoder)
62+
}
5663
}
5764

5865
func nestedContainer<NestedKey: CodingKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer<NestedKey> {

Sources/CodableStructuredHeaders/Decoder/ParametersDecoder.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
// SPDX-License-Identifier: Apache-2.0
1212
//
1313
//===----------------------------------------------------------------------===//
14+
import Foundation
1415
import StructuredHeaders
1516

1617
struct ParametersDecoder<Key: CodingKey, BaseData: RandomAccessCollection> where BaseData.Element == UInt8, BaseData.SubSequence: Hashable {
@@ -47,7 +48,13 @@ extension ParametersDecoder: KeyedDecodingContainerProtocol {
4748
defer {
4849
self.decoder.pop()
4950
}
50-
return try type.init(from: self.decoder)
51+
52+
if type is Data.Type {
53+
let container = try self.decoder.singleValueContainer()
54+
return try container.decode(Data.self) as! T
55+
} else {
56+
return try type.init(from: self.decoder)
57+
}
5158
}
5259

5360
func nestedContainer<NestedKey: CodingKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer<NestedKey> {

Sources/CodableStructuredHeaders/Decoder/StructuredFieldDecoder.swift

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
// SPDX-License-Identifier: Apache-2.0
1212
//
1313
//===----------------------------------------------------------------------===//
14+
import Foundation
1415
import StructuredHeaders
1516

1617
public struct StructuredFieldDecoder {
@@ -58,7 +59,15 @@ extension StructuredFieldDecoder {
5859
let parser = StructuredFieldParser(data)
5960
let decoder = _StructuredFieldDecoder(parser, keyDecodingStrategy: self.keyDecodingStrategy)
6061
try decoder.parseItemField()
61-
return try type.init(from: decoder)
62+
63+
// An escape hatch here for top-level data: if we don't do this, it'll ask for
64+
// an unkeyed container and get very confused.
65+
if type is Data.Type {
66+
let container = try decoder.singleValueContainer()
67+
return try container.decode(Data.self) as! StructuredField
68+
} else {
69+
return try type.init(from: decoder)
70+
}
6271
}
6372
}
6473

Sources/CodableStructuredHeaders/Decoder/TopLevelListDecoder.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
// SPDX-License-Identifier: Apache-2.0
1212
//
1313
//===----------------------------------------------------------------------===//
14+
import Foundation
1415
import StructuredHeaders
1516

1617
struct TopLevelListDecoder<BaseData: RandomAccessCollection> where BaseData.Element == UInt8, BaseData.SubSequence: Hashable {
@@ -64,7 +65,13 @@ extension TopLevelListDecoder: UnkeyedDecodingContainer {
6465
defer {
6566
self.decoder.pop()
6667
}
67-
return try type.init(from: self.decoder)
68+
69+
if type is Data.Type {
70+
let container = try self.decoder.singleValueContainer()
71+
return try container.decode(Data.self) as! T
72+
} else {
73+
return try type.init(from: self.decoder)
74+
}
6875
}
6976

7077
mutating func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {

Sources/CodableStructuredHeaders/Encoder/StructuredFieldEncoder.swift

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
// SPDX-License-Identifier: Apache-2.0
1212
//
1313
//===----------------------------------------------------------------------===//
14+
import Foundation
1415
import StructuredHeaders
1516

1617
public struct StructuredFieldEncoder {
@@ -105,7 +106,17 @@ class _StructuredFieldEncoder {
105106

106107
fileprivate func encodeItemField<StructuredField: Encodable>(_ data: StructuredField) throws -> [UInt8] {
107108
self.push(key: .init(stringValue: ""), newStorage: .itemHeader)
108-
try data.encode(to: self)
109+
110+
// There's an awkward special hook here: if the outer type is `Data`,
111+
// we skip the regular encoding path. This is because otherwise `Data` will
112+
// ask for an unkeyed container and it all falls apart.
113+
//
114+
// Everything else goes through the normal flow.
115+
if let value = data as? Data {
116+
try self.encode(value)
117+
} else {
118+
try data.encode(to: self)
119+
}
109120

110121
switch self.currentStackEntry.storage {
111122
case .item(let item):
@@ -223,6 +234,11 @@ extension _StructuredFieldEncoder: SingleValueEncodingContainer {
223234
try self._encodeFixedWidthInteger(value)
224235
}
225236

237+
func encode(_ data: Data) throws {
238+
let encoded = data.base64EncodedData()
239+
try self.currentStackEntry.storage.insertBareItem(.undecodedByteSequence(encoded))
240+
}
241+
226242
func encode<T>(_ value: T) throws where T : Encodable {
227243
switch value {
228244
case let value as UInt8:
@@ -253,9 +269,9 @@ extension _StructuredFieldEncoder: SingleValueEncodingContainer {
253269
try self.encode(value)
254270
case let value as Bool:
255271
try self.encode(value)
272+
case let value as Data:
273+
try self.encode(value)
256274
default:
257-
// Some other codable type. Not sure what to do yet.
258-
// TODO: what about binary data here?
259275
throw StructuredHeaderError.invalidTypeForItem
260276
}
261277
}
@@ -364,6 +380,10 @@ extension _StructuredFieldEncoder {
364380
try self._appendFixedWidthInteger(value)
365381
}
366382

383+
func append(_ value: Data) throws {
384+
try self.currentStackEntry.storage.appendBareItem(.undecodedByteSequence(value.base64EncodedData()))
385+
}
386+
367387
func append<T>(_ value: T) throws where T : Encodable {
368388
switch value {
369389
case let value as UInt8:
@@ -394,6 +414,8 @@ extension _StructuredFieldEncoder {
394414
try self.append(value)
395415
case let value as Bool:
396416
try self.append(value)
417+
case let value as Data:
418+
try self.append(value)
397419
default:
398420
// Some other codable type.
399421
switch self.currentStackEntry.storage {
@@ -512,6 +534,11 @@ extension _StructuredFieldEncoder {
512534
try self._encodeFixedWidthInteger(value, forKey: key)
513535
}
514536

537+
func encode(_ value: Data, forKey key: String) throws {
538+
let key = self.sanitizeKey(key)
539+
try self.currentStackEntry.storage.insertBareItem(.undecodedByteSequence(value.base64EncodedData()), atKey: key)
540+
}
541+
515542
func encode<T>(_ value: T, forKey key: String) throws where T: Encodable {
516543
let key = self.sanitizeKey(key)
517544

@@ -544,6 +571,8 @@ extension _StructuredFieldEncoder {
544571
try self.encode(value, forKey: key)
545572
case let value as Bool:
546573
try self.encode(value, forKey: key)
574+
case let value as Data:
575+
try self.encode(value, forKey: key)
547576
default:
548577
// Ok, we don't know what this is. This can only happen for a dictionary, or
549578
// for anything with parameters, or for inner lists.
@@ -642,7 +671,7 @@ extension _StructuredFieldEncoder {
642671
/// Note that we never have a bare item here. This is deliberate: bare items
643672
/// are not a container for anything else, and so can never appear.
644673
internal enum NodeType {
645-
typealias DataType = ArraySlice<UInt8>
674+
typealias DataType = Data
646675

647676
case dictionaryHeader
648677
case listHeader
@@ -808,7 +837,7 @@ extension _StructuredFieldEncoder {
808837
}
809838

810839

811-
extension Item where BaseData == ArraySlice<UInt8> {
840+
extension Item where BaseData == _StructuredFieldEncoder.NodeType.DataType {
812841
fileprivate init(_ partialItem: _StructuredFieldEncoder.NodeType.PartialItem) {
813842
self.init(bareItem: partialItem.bareItem!, parameters: partialItem.parameters)
814843
}

0 commit comments

Comments
 (0)