Skip to content

Commit a4ac0df

Browse files
committed
Add ability to peek at type before committing to read of data.
Since we need dual type support without introducing Maps, we need to be able to "try" a type before reading it. See TestEncodeDecodeDualType for an example of this. Actually we should change all the decpde implementations but that is left for later.
1 parent af36824 commit a4ac0df

3 files changed

Lines changed: 152 additions & 23 deletions

File tree

Sources/MessagePack/Decoder/SingleValueDecodingContainer.swift

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,21 @@ extension _MessagePackDecoder {
2828
throw DecodingError.typeMismatch(type, context)
2929
}
3030
}
31+
32+
public func peekType() throws -> UInt8 {
33+
let nextIndex = Data.Index(self.index).advanced(by: 1)
34+
guard nextIndex <= self.data.endIndex else {
35+
let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "Unexpected end of data")
36+
throw DecodingError.dataCorrupted(context)
37+
}
38+
39+
return [UInt8](self.data.subdata(in: self.index..<nextIndex))[0]
40+
}
41+
3142
}
3243
}
3344

34-
extension _MessagePackDecoder.SingleValueContainer: SingleValueDecodingContainer {
45+
extension _MessagePackDecoder.SingleValueContainer: SingleValueDecodingContainer {
3546
func decodeNil() -> Bool {
3647
let format = try? readByte()
3748
return format == 0xc0
@@ -50,36 +61,24 @@ extension _MessagePackDecoder.SingleValueContainer: SingleValueDecodingContainer
5061

5162
func decode(_ type: String.Type) throws -> String {
5263
let length: Int
53-
let format = try readByte()
64+
let format = try peekType()
65+
5466
switch format {
5567
case 0xa0...0xbf:
68+
let _ = try readByte()
5669
length = Int(format - 0xa0)
5770
case 0xd9:
71+
let _ = try readByte()
5872
length = Int(try read(UInt8.self))
5973
case 0xda:
74+
let _ = try readByte()
6075
length = Int(try read(UInt16.self))
6176
case 0xdb:
77+
let _ = try readByte()
6278
length = Int(try read(UInt32.self))
63-
64-
// interpret bin as string
65-
case 0xc4:
66-
length = Int(try read(UInt8.self))
67-
case 0xc5:
68-
length = Int(try read(UInt16.self))
69-
case 0xc6:
70-
length = Int(try read(UInt32.self))
71-
72-
// interpret array as string
73-
case 0x90...0x9f:
74-
length = Int(format - 0x90)
75-
case 0xdc:
76-
length = Int(try read(UInt16.self))
77-
case 0xdd:
78-
length = Int(try read(UInt32.self))
79-
8079
default:
81-
let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "Invalid format: \(format)")
82-
throw DecodingError.typeMismatch(Double.self, context)
80+
let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "Couldn't decode string with UTF-8 encoding")
81+
throw DecodingError.valueNotFound(String.self, context)
8382
}
8483

8584
let data = try read(length)
@@ -90,7 +89,7 @@ extension _MessagePackDecoder.SingleValueContainer: SingleValueDecodingContainer
9089

9190
return string
9291
}
93-
92+
9493
func decode(_ type: Double.Type) throws -> Double {
9594
let format = try readByte()
9695
switch format {
@@ -122,7 +121,7 @@ extension _MessagePackDecoder.SingleValueContainer: SingleValueDecodingContainer
122121
throw DecodingError.typeMismatch(Double.self, context)
123122
}
124123
}
125-
124+
126125
func decode<T>(_ type: T.Type) throws -> T where T : BinaryInteger & Decodable {
127126
let format = try readByte()
128127
var t: T?
@@ -209,12 +208,38 @@ extension _MessagePackDecoder.SingleValueContainer: SingleValueDecodingContainer
209208
return self.data.subdata(in: self.index..<self.index.advanced(by: length))
210209
}
211210

211+
func decode(_ type: [UInt8].Type) throws -> [UInt8] {
212+
let format = try peekType()
213+
let length : Int
214+
215+
switch format {
216+
// bin formats
217+
case 0x90...0x9f:
218+
let _ = try readByte()
219+
length = Int(format - 0x90)
220+
case 0xdc:
221+
let _ = try readByte()
222+
length = Int(try read(UInt16.self))
223+
case 0xdd:
224+
let _ = try readByte()
225+
length = Int(try read(UInt32.self))
226+
default:
227+
let context = DecodingError.Context(codingPath: self.codingPath, debugDescription: "Couldn't decode data as binary data")
228+
throw DecodingError.valueNotFound([UInt8].self, context)
229+
}
230+
231+
let data = try read(length)
232+
return [UInt8](data)
233+
}
234+
212235
func decode<T>(_ type: T.Type) throws -> T where T : Decodable {
213236
switch type {
214237
case is Data.Type:
215238
return try decode(Data.self) as! T
216239
case is Date.Type:
217240
return try decode(Date.self) as! T
241+
case is [UInt8].Type:
242+
return try decode([UInt8].self) as! T
218243
default:
219244
let decoder = _MessagePackDecoder(data: self.data)
220245
let value = try T(from: decoder)

Tests/LinuxMain.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ XCTMain([
55
testCase(MessagePackDecodingTests.allTests),
66
testCase(MessagePackEncodingTests.allTests),
77
testCase(MessagePackRoundTripTests.allTests),
8+
testCase(TestEncodeDecodeDualType.allTests),
89
])
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
//
2+
// File.swift
3+
//
4+
//
5+
// Created by Jim Wilenius on 2020-06-29.
6+
//
7+
8+
import Foundation
9+
10+
11+
import XCTest
12+
@testable import MessagePack
13+
14+
class TestEncodeDecodeDualType: XCTestCase {
15+
static var allTests = [
16+
("testEncodeDecodeDualTypeString", testEncodeDecodeDualTypeString),
17+
("testEncodeDecodeDualTypeBytes", testEncodeDecodeDualTypeBytes),
18+
("testEncodeDecodeDualTypeBytes2", testEncodeDecodeDualTypeBytes2),
19+
]
20+
21+
func testEncodeDecodeDualTypeString() {
22+
do {
23+
let data : Data = try MessagePackEncoder().encode(DualHolder(DualType.string("Hi!")))
24+
let d : DualHolder = try MessagePackDecoder().decode(DualHolder.self, from: data)
25+
XCTAssertEqual(d.dualType.getAsString(), "Hi!")
26+
} catch let e {
27+
print("\(String(describing: e))")
28+
XCTFail()
29+
}
30+
}
31+
32+
func testEncodeDecodeDualTypeBytes() {
33+
do {
34+
let data : Data = try MessagePackEncoder().encode(DualHolder(DualType.bytes([1,2,3])))
35+
let d : DualHolder = try MessagePackDecoder().decode(DualHolder.self, from: data)
36+
XCTAssertEqual(d.dualType.getAsBytes(), [1,2,3])
37+
} catch let e {
38+
print("\(String(describing: e))")
39+
XCTFail()
40+
}
41+
}
42+
43+
func testEncodeDecodeDualTypeBytes2() {
44+
do {
45+
let bytes : [UInt8] = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
46+
let data : Data = try MessagePackEncoder().encode(DualHolder(DualType.bytes(bytes)))
47+
let d : DualHolder = try MessagePackDecoder().decode(DualHolder.self, from: data)
48+
XCTAssertEqual(d.dualType.getAsBytes(), bytes)
49+
} catch let e {
50+
print("\(String(describing: e))")
51+
XCTFail()
52+
}
53+
}
54+
55+
class DualHolder : Codable {
56+
let dualType : DualType
57+
init(_ type : DualType) {
58+
dualType = type
59+
}
60+
}
61+
62+
enum DualType : Codable, Equatable {
63+
case bytes([UInt8])
64+
case string(String)
65+
66+
public func getAsString() -> String? {
67+
switch self {
68+
case .string(let id):
69+
return Optional.some(id)
70+
default:
71+
return Optional.none
72+
}
73+
}
74+
75+
public func getAsBytes() -> [UInt8]? {
76+
switch self {
77+
case .bytes(let mac):
78+
return Optional.some(mac)
79+
default:
80+
return Optional.none
81+
}
82+
}
83+
84+
public init(from decoder: Decoder) throws {
85+
let container = try decoder.singleValueContainer()
86+
do {
87+
self = .bytes(try container.decode([UInt8].self))
88+
} catch DecodingError.valueNotFound {
89+
self = .string(try container.decode(String.self))
90+
}
91+
}
92+
93+
public func encode(to encoder: Encoder) throws {
94+
var container = encoder.singleValueContainer()
95+
switch self {
96+
case .bytes(let value):
97+
try container.encode(value)
98+
case .string(let value):
99+
try container.encode(value)
100+
}
101+
}
102+
}
103+
}

0 commit comments

Comments
 (0)