forked from m-rimestad/BinaryCoder
-
Notifications
You must be signed in to change notification settings - Fork 2
/
BinaryDecoder.swift
243 lines (191 loc) · 7.73 KB
/
BinaryDecoder.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
import Foundation
/// A protocol for types which can be decoded from binary.
public protocol BinaryDecodable: Decodable {
init(fromBinary decoder: BinaryDecoder) throws
}
/// Provide a default implementation which calls through to `Decodable`. This
/// allows `BinaryDecodable` to use the `Decodable` implementation generated by the
/// compiler.
public extension BinaryDecodable {
public init(fromBinary decoder: BinaryDecoder) throws {
try self.init(from: decoder)
}
}
/// The actual binary decoder class.
public class BinaryDecoder {
fileprivate let data: [UInt8]
fileprivate var cursor = 0
public init(data: [UInt8]) {
self.data = data
}
}
/// A convenience function for creating a decoder from some data and decoding it
/// into a value all in one shot.
public extension BinaryDecoder {
static func decode<T: BinaryDecodable>(_ type: T.Type, data: [UInt8]) throws -> T {
return try BinaryDecoder(data: data).decode(T.self)
}
}
/// The error type.
public extension BinaryDecoder {
/// All errors which `BinaryDecoder` itself can throw.
enum Error: Swift.Error {
/// The decoder hit the end of the data while the values it was decoding expected
/// more.
case prematureEndOfData
/// Attempted to decode a type which is `Decodable`, but not `BinaryDecodable`. (We
/// require `BinaryDecodable` because `BinaryDecoder` doesn't support full keyed
/// coding functionality.)
case typeNotConformingToBinaryDecodable(Decodable.Type)
/// Attempted to decode a type which is not `Decodable`.
case typeNotConformingToDecodable(Any.Type)
/// Attempted to decode an `Int` which can't be represented. This happens in 32-bit
/// code when the stored `Int` doesn't fit into 32 bits.
case intOutOfRange(Int64)
/// Attempted to decode a `UInt` which can't be represented. This happens in 32-bit
/// code when the stored `UInt` doesn't fit into 32 bits.
case uintOutOfRange(UInt64)
/// Attempted to decode a `Bool` where the byte representing it was not a `1` or a
/// `0`.
case boolOutOfRange(UInt8)
/// Attempted to decode a `String` but the encoded `String` data was not valid
/// UTF-8.
case invalidUTF8([UInt8])
}
}
/// Methods for decoding various types.
public extension BinaryDecoder {
func decode(_ type: Bool.Type) throws -> Bool {
switch try decode(UInt8.self) {
case 0: return false
case 1: return true
case let x: throw Error.boolOutOfRange(x)
}
}
func decode(_ type: Float.Type) throws -> Float {
var swapped = CFSwappedFloat32()
try read(into: &swapped)
return CFConvertFloatSwappedToHost(swapped)
}
func decode(_ type: Double.Type) throws -> Double {
var swapped = CFSwappedFloat64()
try read(into: &swapped)
return CFConvertDoubleSwappedToHost(swapped)
}
func decode<T: Decodable>(_ type: T.Type) throws -> T {
switch type {
case is Int.Type:
let v = try decode(Int64.self)
if let v = Int(exactly: v) {
return v as! T
} else {
throw Error.intOutOfRange(v)
}
case is UInt.Type:
let v = try decode(UInt64.self)
if let v = UInt(exactly: v) {
return v as! T
} else {
throw Error.uintOutOfRange(v)
}
case is Float.Type:
return try decode(Float.self) as! T
case is Double.Type:
return try decode(Double.self) as! T
case is Bool.Type:
return try decode(Bool.self) as! T
case let binaryT as BinaryDecodable.Type:
return try binaryT.init(fromBinary: self) as! T
default:
throw Error.typeNotConformingToBinaryDecodable(type)
}
}
/// Read the appropriate number of raw bytes directly into the given value. No byte
/// swapping or other postprocessing is done.
func read<T>(into: inout T) throws {
try read(MemoryLayout<T>.size, into: &into)
}
}
/// Internal methods for decoding raw data.
private extension BinaryDecoder {
/// Read the given number of bytes into the given pointer, advancing the cursor
/// appropriately.
func read(_ byteCount: Int, into: UnsafeMutableRawPointer) throws {
if cursor + byteCount > data.count {
throw Error.prematureEndOfData
}
data.withUnsafeBytes({
let from = $0.baseAddress! + cursor
memcpy(into, from, byteCount)
})
cursor += byteCount
}
}
extension BinaryDecoder: Decoder {
public var codingPath: [CodingKey] { return [] }
public var userInfo: [CodingUserInfoKey : Any] { return [:] }
public func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> where Key : CodingKey {
return KeyedDecodingContainer(KeyedContainer<Key>(decoder: self))
}
public func unkeyedContainer() throws -> UnkeyedDecodingContainer {
return UnkeyedContainer(decoder: self)
}
public func singleValueContainer() throws -> SingleValueDecodingContainer {
return UnkeyedContainer(decoder: self)
}
private struct KeyedContainer<Key: CodingKey>: KeyedDecodingContainerProtocol {
var decoder: BinaryDecoder
var codingPath: [CodingKey] { return [] }
var allKeys: [Key] { return [] }
func contains(_ key: Key) -> Bool {
return true
}
func decode<T>(_ type: T.Type, forKey key: Key) throws -> T where T : Decodable {
return try decoder.decode(T.self)
}
func decodeNil(forKey key: Key) throws -> Bool {
return true
}
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
return try decoder.container(keyedBy: type)
}
func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer {
return try decoder.unkeyedContainer()
}
func superDecoder() throws -> Decoder {
return decoder
}
func superDecoder(forKey key: Key) throws -> Decoder {
return decoder
}
}
private struct UnkeyedContainer: UnkeyedDecodingContainer, SingleValueDecodingContainer {
var decoder: BinaryDecoder
var codingPath: [CodingKey] { return [] }
var count: Int? { return nil }
var currentIndex: Int { return 0 }
var isAtEnd: Bool { return false }
func decode<T>(_ type: T.Type) throws -> T where T : Decodable {
return try decoder.decode(type)
}
func decodeNil() -> Bool {
return true
}
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
return try decoder.container(keyedBy: type)
}
func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer {
return self
}
func superDecoder() throws -> Decoder {
return decoder
}
}
}
private extension FixedWidthInteger {
static func from(binaryDecoder: BinaryDecoder) throws -> Self {
var v = Self.init()
try binaryDecoder.read(into: &v)
return self.init(bigEndian: v)
}
}