Skip to content

Commit 5ecd636

Browse files
Merge pull request #9 from mrackwitz/keyed-container-in-single-value-container
Decoding fails for keyed structs in a `singleValueContainer`
2 parents 97d257c + 66b66ae commit 5ecd636

File tree

4 files changed

+79
-11
lines changed

4 files changed

+79
-11
lines changed

Sources/BinaryCodable/Decoding/DecodingNode.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ final class DecodingNode: AbstractDecodingNode, Decoder {
88

99
private let isInUnkeyedContainer: Bool
1010

11+
init(storage: Storage, isOptional: Bool = false, path: [CodingKey], info: UserInfo, isInUnkeyedContainer: Bool = false) {
12+
self.storage = storage
13+
self.isOptional = isOptional
14+
self.isInUnkeyedContainer = isInUnkeyedContainer
15+
super.init(path: path, info: info)
16+
}
17+
1118
init(data: Data, isOptional: Bool = false, path: [CodingKey], info: UserInfo) {
1219
self.storage = .data(data)
1320
self.isOptional = isOptional
@@ -33,7 +40,7 @@ final class DecodingNode: AbstractDecodingNode, Decoder {
3340

3441
func singleValueContainer() throws -> SingleValueDecodingContainer {
3542
return ValueDecoder(
36-
data: storage.useAsDecoder(),
43+
storage: storage,
3744
isOptional: isOptional,
3845
isInUnkeyedContainer: isInUnkeyedContainer,
3946
path: codingPath,

Sources/BinaryCodable/Decoding/ValueDecoder.swift

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,28 @@ import Foundation
22

33
final class ValueDecoder: AbstractDecodingNode, SingleValueDecodingContainer {
44

5-
let data: BinaryStreamProvider
5+
private var storage: Storage
66

77
private let isOptional: Bool
88

99
private let isInUnkeyedContainer: Bool
1010

11-
init(data: BinaryStreamProvider, isOptional: Bool, isInUnkeyedContainer: Bool, path: [CodingKey], info: UserInfo) {
12-
self.data = data
11+
init(storage: Storage, isOptional: Bool, isInUnkeyedContainer: Bool, path: [CodingKey], info: UserInfo) {
12+
self.storage = storage
1313
self.isOptional = isOptional
1414
self.isInUnkeyedContainer = isInUnkeyedContainer
1515
super.init(path: path, info: info)
1616
}
1717

18+
private func asDecoder() -> BinaryStreamProvider {
19+
let decoder = self.storage.useAsDecoder()
20+
self.storage = .decoder(decoder)
21+
return decoder
22+
}
23+
1824
func decodeNil() -> Bool {
1925
do {
20-
let byte = try data.getByte(path: codingPath)
26+
let byte = try asDecoder().getByte(path: codingPath)
2127
return byte == 0
2228
} catch {
2329
return false
@@ -26,18 +32,28 @@ final class ValueDecoder: AbstractDecodingNode, SingleValueDecodingContainer {
2632

2733
func decode<T>(_ type: T.Type) throws -> T where T : Decodable {
2834
if type is AnyOptional.Type {
29-
let node = DecodingNode(decoder: data, isOptional: true, path: codingPath, info: userInfo)
35+
let node = DecodingNode(storage: storage, isOptional: true, path: codingPath, info: userInfo)
3036
return try T.init(from: node)
3137
} else if let Primitive = type as? DecodablePrimitive.Type {
3238
let data: Data
33-
if !isInUnkeyedContainer, Primitive.dataType == .variableLength, !isOptional, let d = self.data as? DataDecoder {
34-
data = d.getAllData()
39+
if !isInUnkeyedContainer, Primitive.dataType == .variableLength, !isOptional {
40+
switch storage {
41+
case .data(let d):
42+
data = d
43+
case .decoder(let decoder):
44+
if let d = decoder as? DataDecoder {
45+
data = d.getAllData()
46+
} else {
47+
data = try decoder.getData(for: Primitive.dataType, path: codingPath)
48+
}
49+
}
3550
} else {
36-
data = try self.data.getData(for: Primitive.dataType, path: codingPath)
51+
let decoder = asDecoder()
52+
data = try decoder.getData(for: Primitive.dataType, path: codingPath)
3753
}
3854
return try Primitive.init(decodeFrom: data, path: codingPath) as! T
3955
} else {
40-
let node = DecodingNode(decoder: data, path: codingPath, info: userInfo)
56+
let node = DecodingNode(storage: storage, path: codingPath, info: userInfo)
4157
return try T.init(from: node)
4258
}
4359
}

Tests/BinaryCodableTests/CustomDecodingTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ final class CustomDecodingTests: XCTestCase {
133133

134134
func testEncodingAsDifferentType() throws {
135135
let version = Version(major: 1, minor: 2, patch: 3)
136-
let time = Date.now
136+
let time = Date()
137137
let sValue = Timestamped(value: version.rawValue, timestamp: time)
138138
let vValue = Timestamped(value: version, timestamp: time)
139139
let encoder = BinaryEncoder()

Tests/BinaryCodableTests/StructEncodingTests.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,49 @@ final class StructEncodingTests: XCTestCase {
153153
let decoded: [String: Int] = try BinaryDecoder.decode(from: encoded)
154154
XCTAssertEqual(decoded, ["a" : 123, "b": 0, "c": -123456])
155155
}
156+
157+
func testDecodeKeyedContainerInSingleValueContainer() throws {
158+
struct Wrapper: Codable, Equatable {
159+
let wrapped: Wrapped
160+
161+
init(wrapped: Wrapped) {
162+
self.wrapped = wrapped
163+
}
164+
165+
init(from decoder: Decoder) throws {
166+
let container = try decoder.singleValueContainer()
167+
self.wrapped = try container.decode(Wrapped.self)
168+
}
169+
170+
func encode(to encoder: Encoder) throws {
171+
var container = encoder.singleValueContainer()
172+
try container.encode(wrapped)
173+
}
174+
}
175+
struct Wrapped: Codable, Equatable {
176+
let val: String
177+
}
178+
179+
let expected: [UInt8] = [
180+
0b00111010, 118, 97, 108, // String key 'val', varint
181+
4, // Length 4
182+
83, 111, 109, 101, // String "Some"
183+
]
184+
185+
let wrapped = Wrapped(val: "Some")
186+
let encodedWrapped = try BinaryEncoder.encode(wrapped)
187+
188+
try compare(encodedWrapped, to: expected)
189+
190+
let decodedWrapped: Wrapped = try BinaryDecoder.decode(from: encodedWrapped)
191+
XCTAssertEqual(decodedWrapped, wrapped)
192+
193+
let wrapper = Wrapper(wrapped: wrapped)
194+
let encodedWrapper = try BinaryEncoder.encode(wrapper)
195+
196+
try compare(encodedWrapper, to: expected)
197+
198+
let decodedWrapper: Wrapper = try BinaryDecoder.decode(from: encodedWrapper)
199+
XCTAssertEqual(decodedWrapper, wrapper)
200+
}
156201
}

0 commit comments

Comments
 (0)