forked from m-rimestad/BinaryCoder
-
Notifications
You must be signed in to change notification settings - Fork 2
/
BinaryEncoder.swift
167 lines (128 loc) · 5.06 KB
/
BinaryEncoder.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
import CoreFoundation
/// A protocol for types which can be encoded to binary.
public protocol BinaryEncodable: Encodable {
func binaryEncode(to encoder: BinaryEncoder) throws
}
/// Provide a default implementation which calls through to `Encodable`. This
/// allows `BinaryEncodable` to use the `Encodable` implementation generated by the
/// compiler.
public extension BinaryEncodable {
func binaryEncode(to encoder: BinaryEncoder) throws {
try self.encode(to: encoder)
}
}
/// The actual binary encoder class.
public class BinaryEncoder {
fileprivate var data: [UInt8] = []
public init() {}
}
/// A convenience function for creating an encoder, encoding a value, and
/// extracting the resulting data.
public extension BinaryEncoder {
static func encode(_ value: BinaryEncodable) throws -> [UInt8] {
let encoder = BinaryEncoder()
try value.binaryEncode(to: encoder)
return encoder.data
}
}
/// The error type.
public extension BinaryEncoder {
/// All errors which `BinaryEncoder` itself can throw.
enum Error: Swift.Error {
/// Attempted to encode a type which is `Encodable`, but not `BinaryEncodable`. (We
/// require `BinaryEncodable` because `BinaryEncoder` doesn't support full keyed
/// coding functionality.)
case typeNotConformingToBinaryEncodable(Encodable.Type)
/// Attempted to encode a type which is not `Encodable`.
case typeNotConformingToEncodable(Any.Type)
}
}
/// Methods for encoding various types.
public extension BinaryEncoder {
func encode(_ value: Bool) throws {
try encode(value ? 1 as UInt8 : 0 as UInt8)
}
func encode(_ value: Float) {
appendBytes(of: CFConvertFloatHostToSwapped(value))
}
func encode(_ value: Double) {
appendBytes(of: CFConvertDoubleHostToSwapped(value))
}
func encode(_ encodable: Encodable) throws {
switch encodable {
case let v as Int:
try encode(Int64(v))
case let v as UInt:
try encode(UInt64(v))
case let v as Float:
encode(v)
case let v as Double:
encode(v)
case let v as Bool:
try encode(v)
case let binary as BinaryEncodable:
try binary.binaryEncode(to: self)
default:
throw Error.typeNotConformingToBinaryEncodable(type(of: encodable))
}
}
/// Append the raw bytes of the parameter to the encoder's data. No byte-swapping
/// or other encoding is done.
func appendBytes<T>(of: T) {
var target = of
withUnsafeBytes(of: &target) {
data.append(contentsOf: $0)
}
}
}
extension BinaryEncoder: Encoder {
public var codingPath: [CodingKey] { return [] }
public var userInfo: [CodingUserInfoKey : Any] { return [:] }
public func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key : CodingKey {
return KeyedEncodingContainer(KeyedContainer<Key>(encoder: self))
}
public func unkeyedContainer() -> UnkeyedEncodingContainer {
return UnkeyedContainer(encoder: self)
}
public func singleValueContainer() -> SingleValueEncodingContainer {
return UnkeyedContainer(encoder: self)
}
private struct KeyedContainer<Key: CodingKey>: KeyedEncodingContainerProtocol {
var encoder: BinaryEncoder
var codingPath: [CodingKey] { return [] }
func encode<T>(_ value: T, forKey key: Key) throws where T : Encodable {
try encoder.encode(value)
}
func encodeNil(forKey key: Key) throws {}
func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> where NestedKey : CodingKey {
return encoder.container(keyedBy: keyType)
}
func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
return encoder.unkeyedContainer()
}
func superEncoder() -> Encoder {
return encoder
}
func superEncoder(forKey key: Key) -> Encoder {
return encoder
}
}
private struct UnkeyedContainer: UnkeyedEncodingContainer, SingleValueEncodingContainer {
var encoder: BinaryEncoder
var codingPath: [CodingKey] { return [] }
var count: Int { return 0 }
func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> where NestedKey : CodingKey {
return encoder.container(keyedBy: keyType)
}
func nestedUnkeyedContainer() -> UnkeyedEncodingContainer {
return self
}
func superEncoder() -> Encoder {
return encoder
}
func encodeNil() throws {}
func encode<T>(_ value: T) throws where T : Encodable {
try encoder.encode(value)
}
}
}