diff --git a/MsgPack.xcodeproj/project.pbxproj b/MsgPack.xcodeproj/project.pbxproj index da1ff0f..826d309 100644 --- a/MsgPack.xcodeproj/project.pbxproj +++ b/MsgPack.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* 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 */; }; + 74E08F571F3360B2008C0F36 /* Reference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74E08F561F3360B2008C0F36 /* Reference.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, ); }; }; @@ -28,6 +29,7 @@ /* Begin PBXFileReference section */ 74A1AE161F2E3E8300B139A3 /* Format.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Format.swift; sourceTree = ""; }; 74A426DB1F30774F001BE9C4 /* JSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSON.swift; sourceTree = ""; }; + 74E08F561F3360B2008C0F36 /* Reference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Reference.swift; sourceTree = ""; }; 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 = ""; }; 74FA3B4A1F2C9060005CE521 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -82,6 +84,7 @@ 74FA3B601F2C908D005CE521 /* Decoder.swift */, 74FA3B621F2C9096005CE521 /* Encoder.swift */, 74A1AE161F2E3E8300B139A3 /* Format.swift */, + 74E08F561F3360B2008C0F36 /* Reference.swift */, 74FA3B4A1F2C9060005CE521 /* Info.plist */, 74A426DB1F30774F001BE9C4 /* JSON.swift */, ); @@ -207,6 +210,7 @@ buildActionMask = 2147483647; files = ( 7495EA251F30B393000EBDF6 /* Encoder.swift in Sources */, + 74E08F571F3360B2008C0F36 /* Reference.swift in Sources */, 74A1AE171F2E3E8300B139A3 /* Format.swift in Sources */, 74FA3B611F2C908D005CE521 /* Decoder.swift in Sources */, ); diff --git a/MsgPack/Decoder.swift b/MsgPack/Decoder.swift index f5c130a..76af322 100644 --- a/MsgPack/Decoder.swift +++ b/MsgPack/Decoder.swift @@ -17,12 +17,20 @@ public class Decoder { public init() {} } -typealias PartiallyDecodedDictionary = [String:PartiallyDecodedValue] +class PartiallyDecodedDictionary: Reference<[String:PartiallyDecodedValue]> { + let pointer: Int + var byteCount = 0 + init(pointer: Int) { + self.pointer = pointer + super.init(to: [:]) + } +} + enum PartiallyDecodedValue { case constant(FormatID) case fixedWidth(FormatID, pointer: Int) case variableWidth(FormatID, pointer: Int, length: Int) - case array([PartiallyDecodedValue]) + case array([PartiallyDecodedValue], pointer: Int, byteCount: Int) case dictionary(PartiallyDecodedDictionary) } @@ -32,16 +40,27 @@ class IntermediateDecoder: Swift.Decoder { var userInfo = [CodingUserInfoKey : Any]() var storage: Data - var offset = 0 - var dictionary = PartiallyDecodedDictionary() + let dictionary: PartiallyDecodedDictionary init(with data: Data) { storage = data + dictionary = PartiallyDecodedDictionary(pointer: 0) } + init(withUnsafeDataFrom otherDecoder: IntermediateDecoder, start: Int, length: Int) { + storage = otherDecoder.storage.withUnsafeMutableBytes { + Data(bytesNoCopy: $0.advanced(by: start), count: length, deallocator: .none) + } + dictionary = otherDecoder.dictionary + } + + convenience init(withUnsafeDataFrom otherDecoder: IntermediateDecoder, start: Int) { + self.init(withUnsafeDataFrom: otherDecoder, start: start, length: otherDecoder.storage.count - start) + } + func container(keyedBy type: Key.Type) throws -> KeyedDecodingContainer where Key : CodingKey { - return KeyedDecodingContainer(try MsgPckKeyedDecodingContainer(referringTo: self, at: offset, with: [])) + return KeyedDecodingContainer(try MsgPckKeyedDecodingContainer(referringTo: self, with: codingPath)) } func unkeyedContainer() throws -> UnkeyedDecodingContainer { @@ -50,12 +69,12 @@ class IntermediateDecoder: Swift.Decoder { func singleValueContainer() throws -> SingleValueDecodingContainer { return MsgPckSingleValueDecodingContainer(decoder: self, base: 0, codingPath: []) - } + } } struct MsgPckSingleValueDecodingContainer: SingleValueDecodingContainer { let decoder: IntermediateDecoder - var base: Int + let base: Int var codingPath: [CodingKey] @@ -165,42 +184,63 @@ struct MsgPckSingleValueDecodingContainer: SingleValueDecodingContainer { } } +struct TemporaryCodingKey: CodingKey { + let stringValue: String + let intValue: Int? = nil + + init?(stringValue: String) { + self.stringValue = stringValue + } + + init?(intValue: Int) { + return nil + } +} + struct MsgPckKeyedDecodingContainer: KeyedDecodingContainerProtocol { var codingPath: [CodingKey] + let relativeDictionary: PartiallyDecodedDictionary + let decoder: IntermediateDecoder - var base = 0 var allKeys = [K]() - init(referringTo decoder: IntermediateDecoder, at base: Int, with codingPath: [CodingKey]) throws { + init(referringTo decoder: IntermediateDecoder, with codingPath: [CodingKey]) throws { self.codingPath = codingPath self.decoder = decoder - self.base = base - - if decoder.dictionary.count == 0 { + + var link = decoder.dictionary + for x in codingPath { + guard case let .dictionary(newLink) = link.storage[x.stringValue]! else {fatalError()} + link = newLink + } + relativeDictionary = link + var cursor = relativeDictionary.pointer + + if relativeDictionary.storage.count == 0 { let elementCount: Int - switch decoder.storage[base] { + switch decoder.storage[cursor] { case FormatID.fixMapRange: - elementCount = Int(decoder.storage[base] - FormatID.fixMap.rawValue) - self.base += 1 + elementCount = Int(decoder.storage[cursor] - FormatID.fixMap.rawValue) + cursor += 1 case FormatID.map16.rawValue: - elementCount = Int(decoder.storage.bigEndianInteger(at: base+1) as UInt16) - self.base += 3 + elementCount = Int(decoder.storage.bigEndianInteger(at: cursor+1) as UInt16) + cursor += 3 case FormatID.map32.rawValue: - elementCount = Int(decoder.storage.bigEndianInteger(at: base+1) as UInt32) - self.base += 5 + elementCount = Int(decoder.storage.bigEndianInteger(at: cursor+1) as UInt32) + cursor += 5 default: - throw DecodingError.typeMismatch(Dictionary.self, .init(codingPath: codingPath, debugDescription: "Expected a MsgPack map format, but found 0x\(String(decoder.storage[base], radix: 16))")) + throw DecodingError.typeMismatch(Dictionary.self, .init(codingPath: codingPath, debugDescription: "Expected a MsgPack map format, but found 0x\(String(decoder.storage[cursor], radix: 16))")) } - for cursor in 0 ..< elementCount { - let key = try Format.string(from: &decoder.storage, base: &self.base) + for _ in 0 ..< elementCount { + let key = try Format.string(from: &decoder.storage, base: &cursor) let valueFormat: FormatID - if let valueFormatLookup = FormatID(rawValue: decoder.storage[self.base]) { + if let valueFormatLookup = FormatID(rawValue: decoder.storage[cursor]) { valueFormat = valueFormatLookup } else { - switch decoder.storage[self.base] { + switch decoder.storage[cursor] { case FormatID.fixMapRange: valueFormat = .fixMap case FormatID.fixStringRange: @@ -210,55 +250,65 @@ struct MsgPckKeyedDecodingContainer: KeyedDecodingContainerProtoco case FormatID.negativeInt5Range: valueFormat = .negativeInt5 default: - throw DecodingError.dataCorrupted(.init(codingPath: codingPath, debugDescription: "Unknown format: 0x\(String(decoder.storage[self.base], radix: 16))")) + throw DecodingError.dataCorrupted(.init(codingPath: codingPath, debugDescription: "Unknown format: 0x\(String(decoder.storage[cursor], radix: 16))")) } } switch valueFormat { case .nil, .false, .true: - decoder.dictionary[key] = .constant(valueFormat) - self.base += 1 + relativeDictionary.storage[key] = .constant(valueFormat) + cursor += 1 case .positiveInt7, .negativeInt5: - decoder.dictionary[key] = .fixedWidth(valueFormat, pointer: self.base) - self.base += 1 + relativeDictionary.storage[key] = .fixedWidth(valueFormat, pointer: cursor) + cursor += 1 case .uInt8, .int8, .string8: - decoder.dictionary[key] = .fixedWidth(valueFormat, pointer: self.base + 1) - self.base += 2 + relativeDictionary.storage[key] = .fixedWidth(valueFormat, pointer: cursor + 1) + cursor += 2 case .uInt16, .int16: - decoder.dictionary[key] = .fixedWidth(valueFormat, pointer: self.base + 1) - self.base += 3 + relativeDictionary.storage[key] = .fixedWidth(valueFormat, pointer: cursor + 1) + cursor += 3 case .uInt32, .int32, .float32: - decoder.dictionary[key] = .fixedWidth(valueFormat, pointer: self.base + 1) - self.base += 5 + relativeDictionary.storage[key] = .fixedWidth(valueFormat, pointer: cursor + 1) + cursor += 5 case .uInt64, .int64, .float64: - decoder.dictionary[key] = .fixedWidth(valueFormat, pointer: self.base + 1) - self.base += 9 - case .fixArray, .fixMap, .fixString: - let length = Int(decoder.storage[self.base] - valueFormat.rawValue) - self.base += 1 - decoder.dictionary[key] = .variableWidth(valueFormat, pointer: self.base, length: length) - self.base += length - case .array16, .map16, .string16: - let length = Int(decoder.storage.bigEndianInteger(at: self.base + 1) as UInt16) - self.base += 3 - decoder.dictionary[key] = .variableWidth(valueFormat, pointer: self.base, length: length) - self.base += length - case .string32, .array32, .map32: - let length = Int(decoder.storage.bigEndianInteger(at: self.base + 1) as UInt16) - self.base += 5 - decoder.dictionary[key] = .variableWidth(valueFormat, pointer: self.base, length: length) - self.base += length + relativeDictionary.storage[key] = .fixedWidth(valueFormat, pointer: cursor + 1) + cursor += 9 + case .fixString: + let length = Int(decoder.storage[cursor] - valueFormat.rawValue) + cursor += 1 + relativeDictionary.storage[key] = .variableWidth(valueFormat, pointer: cursor, length: length) + cursor += length + case .string16: + let length = Int(decoder.storage.bigEndianInteger(at: cursor + 1) as UInt16) + cursor += 3 + relativeDictionary.storage[key] = .variableWidth(valueFormat, pointer: cursor, length: length) + cursor += length + case .string32: + let length = Int(decoder.storage.bigEndianInteger(at: cursor + 1) as UInt16) + cursor += 5 + relativeDictionary.storage[key] = .variableWidth(valueFormat, pointer: cursor, length: length) + cursor += length + case .fixArray, .array16, .array32: + let length = Int(decoder.storage[cursor] - valueFormat.rawValue) + cursor += 1 + fatalError("not implemented") + case .fixMap, .map16, .map32: + let partial = PartiallyDecodedDictionary(pointer: cursor) + relativeDictionary.storage[key] = .dictionary(partial) + try MsgPckKeyedDecodingContainer(referringTo: decoder, with: codingPath + [TemporaryCodingKey(stringValue: key)!]) + cursor += partial.byteCount } } } + relativeDictionary.byteCount = cursor - relativeDictionary.pointer } func contains(_ key: K) -> Bool { - return decoder.dictionary[key.stringValue] != nil + return relativeDictionary.storage[key.stringValue] != nil } func decodeNil(forKey key: K) throws -> Bool { - switch decoder.dictionary[key.stringValue]! { + switch relativeDictionary.storage[key.stringValue]! { case .constant(let format): return format == .nil default: @@ -267,8 +317,8 @@ struct MsgPckKeyedDecodingContainer: KeyedDecodingContainerProtoco } func decode(_ type: Bool.Type, forKey key: K) throws -> Bool { - guard let value = decoder.dictionary[key.stringValue] else { - throw DecodingError.keyNotFound(key, .init(codingPath: codingPath, debugDescription: "Key not found")) + guard let value = relativeDictionary.storage[key.stringValue] else { + throw DecodingError.keyNotFound(key, .init(codingPath: codingPath, debugDescription: "Key not found: \(key.stringValue)")) } guard case let .constant(format) = value else { throw DecodingError.typeMismatch(type, .init(codingPath: codingPath, debugDescription: "Expected bool but found \(value)")) @@ -282,8 +332,8 @@ struct MsgPckKeyedDecodingContainer: KeyedDecodingContainerProtoco } func formattedPointer(for key: K, format expectedFormat: FormatID, type: T.Type) throws -> Int { - guard let value = decoder.dictionary[key.stringValue] else { - throw DecodingError.keyNotFound(key, .init(codingPath: codingPath, debugDescription: "Key not found")) + guard let value = relativeDictionary.storage[key.stringValue] else { + throw DecodingError.keyNotFound(key, .init(codingPath: codingPath, debugDescription: "Key not found: \(key.stringValue)")) } guard case let .fixedWidth(format, pointer) = value else { throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "Expected fixed width value but found \(value)") @@ -345,11 +395,11 @@ struct MsgPckKeyedDecodingContainer: KeyedDecodingContainerProtoco } func decode(_ type: String.Type, forKey key: K) throws -> String { - guard let value = decoder.dictionary[key.stringValue] else { - throw DecodingError.keyNotFound(key, .init(codingPath: codingPath, debugDescription: "Key not found")) + guard let value = relativeDictionary.storage[key.stringValue] else { + throw DecodingError.keyNotFound(key, .init(codingPath: codingPath, debugDescription: "Key not found: \(key.stringValue)")) } guard case let .variableWidth(format, base, length) = value else { - throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "Expected fixed width value but found \(value)") + throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "Expected variable width value but found \(value)") } guard [.string8, .string16, .string32, .fixString].contains(format) else { throw DecodingError.typeMismatch(type, .init(codingPath: codingPath, debugDescription: "Expected a string but found \(format)")) @@ -363,7 +413,25 @@ struct MsgPckKeyedDecodingContainer: KeyedDecodingContainerProtoco } func decode(_ type: T.Type, forKey key: K) throws -> T where T : Decodable { - fatalError("not implemented") + guard let value = relativeDictionary.storage[key.stringValue] else { + throw DecodingError.keyNotFound(key, .init(codingPath: codingPath, debugDescription: "Key not found: \(key.stringValue)")) + } + let sliceDecoder: IntermediateDecoder + switch value { + + case .constant(let format): + sliceDecoder = IntermediateDecoder(with: Data([format.rawValue])) + case let .fixedWidth(format, pointer): + sliceDecoder = IntermediateDecoder(withUnsafeDataFrom: decoder, start: pointer - format.length) + case let .variableWidth(format, pointer, length): + sliceDecoder = IntermediateDecoder(withUnsafeDataFrom: decoder, start: pointer - format.length, length: length + format.length) + case .array(_): + fatalError("not implemented") + case let .dictionary(reference): + sliceDecoder = IntermediateDecoder(withUnsafeDataFrom: decoder, start: reference.pointer, length: reference.byteCount) + } + sliceDecoder.codingPath = codingPath + [key] + return try T(from: sliceDecoder) } func nestedContainer(keyedBy type: NestedKey.Type, forKey key: K) throws -> KeyedDecodingContainer where NestedKey : CodingKey { diff --git a/MsgPack/Encoder.swift b/MsgPack/Encoder.swift index 4419d26..ed6eedc 100644 --- a/MsgPack/Encoder.swift +++ b/MsgPack/Encoder.swift @@ -266,10 +266,9 @@ class MsgPackKeyedEncodingContainer: MessagePackEncodingContainer, } } -class KeyedStorageContainer { - var storage: [String: MessagePackEncodingContainer] - init(storage: [String:MessagePackEncodingContainer] = [:]) { - self.storage = storage +class KeyedStorageContainer: Reference<[String: MessagePackEncodingContainer]> { + init() { + super.init(to: [:]) } } diff --git a/MsgPack/Format.swift b/MsgPack/Format.swift index 4b3e687..73f9602 100644 --- a/MsgPack/Format.swift +++ b/MsgPack/Format.swift @@ -101,6 +101,62 @@ public enum FormatID: UInt8 { static let fixMapRange = FormatID.fixMap.rawValue ..< FormatID.fixArray.rawValue static let fixArrayRange = FormatID.fixArray.rawValue ..< FormatID.fixString.rawValue static let fixStringRange = FormatID.fixString.rawValue ..< FormatID.nil.rawValue + + var length: Int { + switch self { + + case .nil: + return 0 + case .false: + return 0 + case .true: + return 0 + case .float32: + return 1 + case .float64: + return 1 + case .positiveInt7: + return 0 + case .negativeInt5: + return 0 + case .uInt8: + return 1 + case .uInt16: + return 1 + case .uInt32: + return 1 + case .uInt64: + return 1 + case .int8: + return 1 + case .int16: + return 1 + case .int32: + return 1 + case .int64: + return 1 + case .fixString: + return 1 + case .string8: + return 2 + case .string16: + return 3 + case .string32: + return 5 + case .fixArray: + return 1 + case .array16: + return 3 + case .array32: + return 5 + case .fixMap: + return 1 + case .map16: + return 3 + case .map32: + return 5 + } + } } extension Format { diff --git a/MsgPack/Reference.swift b/MsgPack/Reference.swift new file mode 100644 index 0000000..54fc0c3 --- /dev/null +++ b/MsgPack/Reference.swift @@ -0,0 +1,16 @@ +// +// Reference.swift +// MsgPack +// +// Created by Damiaan on 3/08/17. +// Copyright © 2017 dPro. All rights reserved. +// + +import Foundation + +class Reference { + var storage: T + init(to value: T) { + storage = value + } +} diff --git a/MsgPackTests/MsgPackTests.swift b/MsgPackTests/MsgPackTests.swift index ad9ba4e..a01e5c8 100644 --- a/MsgPackTests/MsgPackTests.swift +++ b/MsgPackTests/MsgPackTests.swift @@ -110,14 +110,28 @@ class MsgPackTests: XCTestCase { } } - struct Simple: Codable { - let a: Bool - let b: Bool - let c: Bool? - let d: Bool? - let e: Bool? - let f: Int8 + struct Graph: Codable { + let title: String + let circles: Circle } + + struct Circle: Codable { + let radius: UInt64 + let center: Position + } + + struct Position: Codable { + let x: Int8 + let y: Int8 + } + + let graph = Graph( + title: "My graph", + circles: Circle( + radius: 0x0102030405060708, + center: Position(x: -123, y: 2) + ) + ) func roundtrip(value: T) throws -> T { return try decoder.decode(T.self, from: encoder.encode(value)) @@ -125,7 +139,7 @@ class MsgPackTests: XCTestCase { func testExample() { do { - print("roundtrip:", try roundtrip(value: Simple(a: true, b: false, c: true, d: false, e: nil, f: -8))) + print("roundtrip:", try roundtrip(value: graph)) } catch { print(error) } diff --git a/Playground.playground/Contents.swift b/Playground.playground/Contents.swift index 14d8aa1..5210b02 100644 --- a/Playground.playground/Contents.swift +++ b/Playground.playground/Contents.swift @@ -4,33 +4,34 @@ import MsgPack let encoder = Encoder() -struct Graph: Encodable { +struct Graph: Codable { let title: String - let circles: [Circle] + let circles: Circle } -struct Circle: Encodable { - let radius: UInt +struct Circle: Codable { + let radius: UInt64 let center: Position } -struct Position: Encodable { +struct Position: Codable { let x: Int8 let y: Int8 } let graph = Graph( title: "My graph", - circles: [ + circles: Circle( radius: 0x0102030405060708, center: Position(x: -123, y: 2) - ), - Circle( - radius: 1000, - center: Position(x: 116, y: 81) ) - ] +// , +// Circle( +// radius: 1000, +// center: Position(x: 116, y: 81) +// ) + ) try encoder.encode(graph) @@ -52,4 +53,9 @@ struct Simple: Codable { try roundtrip(value: -56.4) try roundtrip(value: "Hello world 😎") -try roundtrip(value: Simple(a: true, b: false, c: true, d: false, e: nil, f: "😜")) +do { + try roundtrip(value: graph) +} catch { + error + print(error) +}