Encode keyed containers

This commit is contained in:
Damiaan Dufaux 2017-08-01 17:26:55 +02:00
parent 43dff2cc5b
commit 0490d2cd6f
6 changed files with 363 additions and 156 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/MsgPack/JSON.swift

View file

@ -7,12 +7,12 @@
objects = {
/* Begin PBXBuildFile section */
7495EA251F30B393000EBDF6 /* Encoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FA3B621F2C9096005CE521 /* Encoder.swift */; };
74A1AE171F2E3E8300B139A3 /* Format.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74A1AE161F2E3E8300B139A3 /* Format.swift */; };
74FA3B501F2C9060005CE521 /* MsgPack.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 74FA3B461F2C9060005CE521 /* MsgPack.framework */; };
74FA3B551F2C9060005CE521 /* MsgPackTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FA3B541F2C9060005CE521 /* MsgPackTests.swift */; };
74FA3B571F2C9060005CE521 /* MsgPack.h in Headers */ = {isa = PBXBuildFile; fileRef = 74FA3B491F2C9060005CE521 /* MsgPack.h */; settings = {ATTRIBUTES = (Public, ); }; };
74FA3B611F2C908D005CE521 /* DecoderHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FA3B601F2C908D005CE521 /* DecoderHelper.swift */; };
74FA3B631F2C9096005CE521 /* Encoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FA3B621F2C9096005CE521 /* Encoder.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -27,6 +27,7 @@
/* Begin PBXFileReference section */
74A1AE161F2E3E8300B139A3 /* Format.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Format.swift; sourceTree = "<group>"; };
74A426DB1F30774F001BE9C4 /* JSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSON.swift; sourceTree = "<group>"; };
74FA3B461F2C9060005CE521 /* MsgPack.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MsgPack.framework; sourceTree = BUILT_PRODUCTS_DIR; };
74FA3B491F2C9060005CE521 /* MsgPack.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MsgPack.h; sourceTree = "<group>"; };
74FA3B4A1F2C9060005CE521 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@ -82,6 +83,7 @@
74FA3B621F2C9096005CE521 /* Encoder.swift */,
74A1AE161F2E3E8300B139A3 /* Format.swift */,
74FA3B4A1F2C9060005CE521 /* Info.plist */,
74A426DB1F30774F001BE9C4 /* JSON.swift */,
);
path = MsgPack;
sourceTree = "<group>";
@ -204,7 +206,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74FA3B631F2C9096005CE521 /* Encoder.swift in Sources */,
7495EA251F30B393000EBDF6 /* Encoder.swift in Sources */,
74A1AE171F2E3E8300B139A3 /* Format.swift in Sources */,
74FA3B611F2C908D005CE521 /* DecoderHelper.swift in Sources */,
);
@ -353,7 +355,7 @@
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_VERSION = A;
GCC_OPTIMIZATION_LEVEL = fast;
GCC_OPTIMIZATION_LEVEL = 0;
INFOPLIST_FILE = MsgPack/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";

View file

@ -9,26 +9,29 @@
import Foundation
public class Encoder {
let serialiser = Serialiser()
let intermediate = IntermediateEncoder()
public init() {}
public func encode<T : Encodable>(_ value: T) throws -> Data {
try value.encode(to: serialiser)
try value.encode(to: intermediate)
var data = Data()
try serialiser.storage?.appendTo(data: &data)
try intermediate.container?.getFormat().appendTo(data: &data)
return data
}
}
class Serialiser: Swift.Encoder {
class IntermediateEncoder: Swift.Encoder {
var codingPath = [CodingKey]()
var userInfo = [CodingUserInfoKey : Any]()
var storage: Format?
var container: MessagePackEncodingContainer?
func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key : CodingKey {
preconditionFailure()
let keyedContainer = MsgPackKeyedEncodingContainer<Key>()
container = keyedContainer
return KeyedEncodingContainer(keyedContainer)
}
func unkeyedContainer() -> UnkeyedEncodingContainer {
@ -36,94 +39,239 @@ class Serialiser: Swift.Encoder {
}
func singleValueContainer() -> SingleValueEncodingContainer {
return self
let singleValueContainer = MsgPackSingleValueEncodingContainer()
container = singleValueContainer
return singleValueContainer
}
}
extension MsgPack.Serialiser: SingleValueEncodingContainer {
extension Format {
static func from(string: String) throws -> Format {
guard let data = string.data(using: .utf8) else {throw MsgPackEncodingError.stringNotConvertibleToUTF8(string)}
switch data.count {
case 1..<32:
return .fixString(data)
case 32..<256:
return .string8(data)
case 256..<65536:
return .string16(data)
default:
return .string32(data)
}
}
static func from(keyValuePairs: [(Format, Format)]) -> Format {
switch keyValuePairs.count {
case 1..<16:
return .fixMap(keyValuePairs)
case 16..<65536:
return .map16(keyValuePairs)
default:
return .map32(keyValuePairs)
}
}
static func from(int: Int) -> Format {
#if arch(arm) || arch(i386)
return .int32(Int32(int))
#else
return .int64(Int64(int))
#endif
}
static func from(uInt: UInt) -> Format {
#if arch(arm) || arch(i386)
return .uInt32(UInt32(uInt))
#else
return .uInt64(UInt64(uInt))
#endif
}
}
class MessagePackEncodingContainer {
var codingPath: [CodingKey] = []
func getFormat() throws -> Format {
preconditionFailure()
}
}
enum MsgPackEncodingError: Swift.Error {
case notImplemented, stringNotConvertibleToUTF8(String)
}
class MsgPackSingleValueEncodingContainer: MessagePackEncodingContainer, SingleValueEncodingContainer {
var storage: Format?
enum Error: Swift.Error {
case notImplemented, stringNotConvertibleToUTF8
case noValue
}
override func getFormat() throws -> Format {
guard let format = storage else {throw Error.noValue}
return format
}
init(with storage: Format? = nil) {
self.storage = storage
}
func encodeNil() throws {
storage = .nil
}
func encode(_ value: Bool) throws {
storage = .boolean(value)
}
func encode(_ value: Int) throws {
#if arch(arm) || arch(i386)
storage = .int32(Int32(value))
#else
storage = .int64(Int64(value))
#endif
storage = .from(int: value)
}
func encode(_ value: Int8) throws {
storage = .int8(value)
}
func encode(_ value: Int16) throws {
storage = .int16(value)
}
func encode(_ value: Int32) throws {
storage = .int32(value)
}
func encode(_ value: Int64) throws {
storage = .int64(value)
}
func encode(_ value: UInt) throws {
#if arch(arm) || arch(i386)
storage = .uInt32(UInt32(value))
#else
storage = .uInt64(UInt64(value))
#endif
storage = .from(uInt: value)
}
func encode(_ value: UInt8) throws {
storage = .uInt8(value)
}
func encode(_ value: UInt16) throws {
storage = .uInt16(value)
}
func encode(_ value: UInt32) throws {
storage = .uInt32(value)
}
func encode(_ value: UInt64) throws {
storage = .uInt64(value)
}
func encode(_ value: Float) throws {
storage = .float32(value)
}
func encode(_ value: Double) throws {
storage = .float64(value)
}
func encode(_ value: String) throws {
guard let data = value.data(using: .utf8) else {throw Error.stringNotConvertibleToUTF8}
switch data.count {
case 1..<32:
storage = .fixString(data)
case 32..<256:
storage = .string8(data)
case 256..<65536:
storage = .string16(data)
default:
storage = .string32(data)
}
storage = try .from(string: value)
}
func encode<T : Encodable>(_ value: T) throws {
throw Error.notImplemented
throw MsgPackEncodingError.notImplemented
}
}
class MsgPackKeyedEncodingContainer<K: CodingKey>: MessagePackEncodingContainer, KeyedEncodingContainerProtocol {
var userInfo = [CodingUserInfoKey : Any]()
var storage = [String: MessagePackEncodingContainer]()
override func getFormat() throws -> Format {
return try Format.from(keyValuePairs: storage.map {
(try Format.from(string: $0.key), try $0.value.getFormat())
})
}
func encodeNil(forKey key: K) throws {
storage[key.stringValue] = MsgPackSingleValueEncodingContainer(with: .nil)
}
func encode(_ value: Bool, forKey key: K) throws {
storage[key.stringValue] = MsgPackSingleValueEncodingContainer(with: .boolean(value) )
}
func encode(_ value: Int, forKey key: K) throws {
storage[key.stringValue] = MsgPackSingleValueEncodingContainer(with: .from(int: value) )
}
func encode(_ value: Int8, forKey key: K) throws {
storage[key.stringValue] = MsgPackSingleValueEncodingContainer(with: .int8(value) )
}
func encode(_ value: Int16, forKey key: K) throws {
storage[key.stringValue] = MsgPackSingleValueEncodingContainer(with: .int16(value) )
}
func encode(_ value: Int32, forKey key: K) throws {
storage[key.stringValue] = MsgPackSingleValueEncodingContainer(with: .int32(value) )
}
func encode(_ value: Int64, forKey key: K) throws {
storage[key.stringValue] = MsgPackSingleValueEncodingContainer(with: .int64(value) )
}
func encode(_ value: UInt, forKey key: K) throws {
storage[key.stringValue] = MsgPackSingleValueEncodingContainer(with: .from(uInt: value) )
}
func encode(_ value: UInt8, forKey key: K) throws {
storage[key.stringValue] = MsgPackSingleValueEncodingContainer(with: .uInt8(value) )
}
func encode(_ value: UInt16, forKey key: K) throws {
storage[key.stringValue] = MsgPackSingleValueEncodingContainer(with: .uInt16(value) )
}
func encode(_ value: UInt32, forKey key: K) throws {
storage[key.stringValue] = MsgPackSingleValueEncodingContainer(with: .uInt32(value) )
}
func encode(_ value: UInt64, forKey key: K) throws {
storage[key.stringValue] = MsgPackSingleValueEncodingContainer(with: .uInt64(value) )
}
func encode(_ value: Float, forKey key: K) throws {
storage[key.stringValue] = MsgPackSingleValueEncodingContainer(with: .float32(value) )
}
func encode(_ value: Double, forKey key: K) throws {
storage[key.stringValue] = MsgPackSingleValueEncodingContainer(with: .float64(value) )
}
func encode(_ value: String, forKey key: K) throws {
storage[key.stringValue] = try MsgPackSingleValueEncodingContainer(with: .from(string: value) )
}
func encode<T>(_ value: T, forKey key: K) throws where T : Encodable {
throw MsgPackEncodingError.notImplemented
}
func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: K) -> KeyedEncodingContainer<NestedKey> where NestedKey : CodingKey {
let keyedContainer = MsgPackKeyedEncodingContainer<NestedKey>()
storage[key.stringValue] = keyedContainer
return KeyedEncodingContainer(keyedContainer)
}
func nestedUnkeyedContainer(forKey key: K) -> UnkeyedEncodingContainer {
preconditionFailure("not implemented")
}
func superEncoder() -> Swift.Encoder {
preconditionFailure("not implemented")
}
func superEncoder(forKey key: K) -> Swift.Encoder {
preconditionFailure("not implemented")
}
}

View file

@ -32,10 +32,21 @@ enum Format {
case float64(Double)
case fixString(Data)
case string8(Data)
case string16(Data)
case string32(Data)
case string8 (Data)
case string16 (Data)
case string32 (Data)
case fixArray([Format])
case array16 ([Format])
case array32 ([Format])
case fixMap([(key: Format, value: Format)])
case map16 ([(key: Format, value: Format)])
case map32 ([(key: Format, value: Format)])
}
extension Format {
func appendTo(data: inout Data) {
switch self {
@ -59,117 +70,136 @@ enum Format {
data.append(value)
case .uInt16(let value):
var newData = Data(count: 3)
newData.withUnsafeMutableBytes({ (byteContainer: UnsafeMutablePointer<UInt8>) -> Void in
byteContainer.pointee = 0xCD
byteContainer.advanced(by: 1).withMemoryRebound(to: UInt16.self, capacity: 1) {
$0.pointee = value.bigEndian
}
})
newData[0] = 0xCD
newData.write(value: value.bigEndian, offset: 1)
data.append(newData)
case .uInt32(let value):
var newData = Data(count: 5)
newData.withUnsafeMutableBytes({ (byteContainer: UnsafeMutablePointer<UInt8>) -> Void in
byteContainer.pointee = 0xCE
byteContainer.advanced(by: 1).withMemoryRebound(to: UInt32.self, capacity: 1) {
$0.pointee = value.bigEndian
}
})
newData[0] = 0xCE
newData.write(value: value.bigEndian, offset: 1)
data.append(newData)
case .uInt64(let value):
var newData = Data(count: 9)
newData.withUnsafeMutableBytes({ (byteContainer: UnsafeMutablePointer<UInt8>) -> Void in
byteContainer.pointee = 0xCF
byteContainer.advanced(by: 1).withMemoryRebound(to: UInt64.self, capacity: 1) {
$0.pointee = value.bigEndian
}
})
newData[0] = 0xCF
newData.write(value: value.bigEndian, offset: 1)
data.append(newData)
// MARK: Signed integers
case .int8(let value):
var newData = Data(count: 2)
newData.withUnsafeMutableBytes({ (byteContainer: UnsafeMutablePointer<UInt8>) -> Void in
byteContainer.pointee = 0xD1
byteContainer.advanced(by: 1).withMemoryRebound(to: Int8.self, capacity: 1) {
$0.pointee = value.bigEndian
}
})
newData[0] = 0xD0
newData.write(value: value.bigEndian, offset: 1)
data.append(newData)
case .int16(let value):
var newData = Data(count: 3)
newData.withUnsafeMutableBytes({ (byteContainer: UnsafeMutablePointer<UInt8>) -> Void in
byteContainer.pointee = 0xD2
byteContainer.advanced(by: 1).withMemoryRebound(to: Int16.self, capacity: 1) {
$0.pointee = value.bigEndian
}
})
newData[0] = 0xD1
newData.write(value: value.bigEndian, offset: 1)
data.append(newData)
case .int32(let value):
var newData = Data(count: 5)
newData.withUnsafeMutableBytes({ (byteContainer: UnsafeMutablePointer<UInt8>) -> Void in
byteContainer.pointee = 0xD2
byteContainer.advanced(by: 1).withMemoryRebound(to: Int32.self, capacity: 1) {
$0.pointee = value.bigEndian
}
})
newData[0] = 0xD2
newData.write(value: value.bigEndian, offset: 1)
data.append(newData)
case .int64(let value):
var newData = Data(count: 9)
newData.withUnsafeMutableBytes({ (byteContainer: UnsafeMutablePointer<UInt8>) -> Void in
byteContainer.pointee = 0xD3
byteContainer.advanced(by: 1).withMemoryRebound(to: Int64.self, capacity: 1) {
$0.pointee = value.bigEndian
}
})
newData[0] = 0xD3
newData.write(value: value.bigEndian, offset: 1)
data.append(newData)
// MARK: Floats
case .float32(let value):
var newData = Data(count: 5)
newData.withUnsafeMutableBytes({ (byteContainer: UnsafeMutablePointer<UInt8>) -> Void in
byteContainer.pointee = 0xCA
byteContainer.advanced(by: 1).withMemoryRebound(to: UInt32.self, capacity: 1) {
$0.pointee = value.bitPattern.bigEndian
}
})
newData[0] = 0xCA
newData.write(value: value.bitPattern.bigEndian, offset: 1)
data.append(newData)
case .float64(let value):
var newData = Data(count: 9)
newData.withUnsafeMutableBytes({ (byteContainer: UnsafeMutablePointer<UInt8>) -> Void in
byteContainer.pointee = 0xCB
byteContainer.advanced(by: 1).withMemoryRebound(to: UInt64.self, capacity: 1) {
$0.pointee = value.bitPattern.bigEndian
}
})
newData[0] = 0xCB
newData.write(value: value.bitPattern.bigEndian, offset: 1)
data.append(newData)
// MARK: Strings
case .fixString(let value):
data.append(UInt8(value.count) & 0b00011111 | 0b10100000)
data.append(value)
case .string8(let value):
data.append(contentsOf: [0xD9, UInt8(value.count)])
data.append(value)
case .string16(let value):
case .fixString(let utf8Data):
precondition(utf8Data.count < 32, "fix strings cannot contain more than 31 bytes")
data.append( UInt8(utf8Data.count) | 0b10100000)
data.append(utf8Data)
case .string8(let utf8Data):
data.append(contentsOf: [0xD9, UInt8(utf8Data.count)])
data.append(utf8Data)
case .string16(let utf8Data):
var prefix = Data(count: 3)
prefix.withUnsafeMutableBytes({ (byteContainer: UnsafeMutablePointer<UInt8>) -> Void in
byteContainer.pointee = 0xDA
byteContainer.advanced(by: 1).withMemoryRebound(to: UInt16.self, capacity: 1) {
$0.pointee = UInt16(value.count).bigEndian
}
})
prefix[0] = 0xDA
prefix.write(value: UInt16(utf8Data.count).bigEndian, offset: 1)
data.append(prefix)
data.append(value)
case .string32(let value):
data.append(utf8Data)
case .string32(let utf8Data):
var prefix = Data(count: 5)
prefix.withUnsafeMutableBytes({ (byteContainer: UnsafeMutablePointer<UInt8>) -> Void in
byteContainer.pointee = 0xDB
byteContainer.advanced(by: 1).withMemoryRebound(to: UInt32.self, capacity: 1) {
$0.pointee = UInt32(value.count).bigEndian
}
})
prefix[0] = 0xDB
prefix.write(value: UInt32(utf8Data.count).bigEndian, offset: 1)
data.append(prefix)
data.append(value)
data.append(utf8Data)
// MARK: Arrays
case .fixArray(let array):
precondition(array.count < 16, "fix arrays cannot contain more than 15 elements")
data.append( UInt8(array.count) | 0b10010000)
for element in array {
element.appendTo(data: &data)
}
case .array16(let array):
var prefix = Data(count: 3)
prefix[0] = 0xDC
prefix.write(value: UInt16(array.count).bigEndian, offset: 1)
data.append(prefix)
for element in array {
element.appendTo(data: &data)
}
case .array32(let array):
var prefix = Data(count: 5)
prefix[0] = 0xDD
prefix.write(value: UInt32(array.count).bigEndian, offset: 1)
data.append(prefix)
for element in array {
element.appendTo(data: &data)
}
// MARK: Maps
case .fixMap(let pairs):
precondition(pairs.count < 16, "fix maps cannot contain more than 15 key-value pairs")
data.append( UInt8(pairs.count) | 0b10000000)
for (key, value) in pairs {
key.appendTo(data: &data)
value.appendTo(data: &data)
}
case .map16(let pairs):
var prefix = Data(count: 3)
prefix[0] = 0xDE
prefix.write(value: UInt16(pairs.count).bigEndian, offset: 1)
data.append(prefix)
for (key, value) in pairs {
key.appendTo(data: &data)
value.appendTo(data: &data)
}
case .map32(let pairs):
var prefix = Data(count: 5)
prefix[0] = 0xDE
prefix.write(value: UInt32(pairs.count).bigEndian, offset: 1)
data.append(prefix)
for (key, value) in pairs {
key.appendTo(data: &data)
value.appendTo(data: &data)
}
}
}
}
extension Data {
mutating func write<T>(value: T, offset: Int) {
withUnsafeMutableBytes {(byteContainer: UnsafeMutablePointer<UInt8>) -> Void in
byteContainer.advanced(by: offset).withMemoryRebound(to: T.self, capacity: 1) {
$0.pointee = value
}
}
}
}

View file

@ -10,20 +10,20 @@ import XCTest
@testable import MsgPack
class MsgPackTests: XCTestCase {
var encoder: MsgPack.Encoder!
override func setUp() {
super.setUp()
encoder = Encoder()
}
// override func tearDown() {
// // Put teardown code here. This method is called after the invocation of each test method in the class.
// super.tearDown()
// }
var encoder: MsgPack.Encoder!
override func setUp() {
super.setUp()
encoder = Encoder()
}
func testEncodeTrue() {
do {
let data = try encoder.encode(true)
@ -99,7 +99,7 @@ class MsgPackTests: XCTestCase {
XCTFail(error.localizedDescription)
}
}
func testPerformanceOf2MilionUInt32Encodings() {
self.measure {
for _ in 0 ..< 2000000 {
@ -107,5 +107,27 @@ class MsgPackTests: XCTestCase {
}
}
}
// var encoder = MsgPack.JSONEncoder2()
//
// struct Obj: Encodable {
// let i: Int
// let p: Person
// }
//
// struct Person: Encodable {
// let name: String
// let friend: Friend
// }
//
// struct Friend: Encodable {
// let count: Double
// }
//
// func testDinges() {
// try! encoder.encode(
// Obj(i: 3, p: Person(name: "Dinges", friend: Friend(count: 8)))
// )
// }
}

View file

@ -5,23 +5,27 @@ import Foundation
let encoder = Encoder()
let integerData = try encoder.encode(0x0102030405060708)
String(integerData[0], radix: 16)
try encoder.encode(0x0102030405060708)
integerData[1]
integerData[2]
integerData[3]
integerData[4]
integerData[5]
integerData[6]
integerData[7]
integerData[8]
try encoder.encode("Hello")
let doubleData = try encoder.encode("Hello there 123456789012345678901")
String(doubleData[0], radix: 16)
try encoder.encode("😇")
String(doubleData[1], radix: 16)
String(doubleData[2], radix: 16)
String(doubleData[3], radix: 16)
String(doubleData[4], radix: 16)
String(doubleData[5], radix: 16)
var n: Int?
try encoder.encode(n)
struct Position: Encodable {
let x: Int8
let y: Int8
}
struct Circle: Encodable {
let center: Position
let radius: UInt
}
do {
try encoder.encode(Circle(center: Position(x: -1, y: 2), radius: 50)).forEach { print(String($0, radix: 16)) }
} catch {
error
}