diff --git a/MsgPack.xcodeproj/project.pbxproj b/MsgPack.xcodeproj/project.pbxproj index 6bc6701..da1ff0f 100644 --- a/MsgPack.xcodeproj/project.pbxproj +++ b/MsgPack.xcodeproj/project.pbxproj @@ -12,7 +12,7 @@ 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 */; }; + 74FA3B611F2C908D005CE521 /* Decoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74FA3B601F2C908D005CE521 /* Decoder.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -34,7 +34,7 @@ 74FA3B4F1F2C9060005CE521 /* MsgPackTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MsgPackTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 74FA3B541F2C9060005CE521 /* MsgPackTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MsgPackTests.swift; sourceTree = ""; }; 74FA3B561F2C9060005CE521 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 74FA3B601F2C908D005CE521 /* DecoderHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecoderHelper.swift; sourceTree = ""; }; + 74FA3B601F2C908D005CE521 /* Decoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Decoder.swift; sourceTree = ""; }; 74FA3B621F2C9096005CE521 /* Encoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Encoder.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -79,7 +79,7 @@ isa = PBXGroup; children = ( 74FA3B491F2C9060005CE521 /* MsgPack.h */, - 74FA3B601F2C908D005CE521 /* DecoderHelper.swift */, + 74FA3B601F2C908D005CE521 /* Decoder.swift */, 74FA3B621F2C9096005CE521 /* Encoder.swift */, 74A1AE161F2E3E8300B139A3 /* Format.swift */, 74FA3B4A1F2C9060005CE521 /* Info.plist */, @@ -208,7 +208,7 @@ files = ( 7495EA251F30B393000EBDF6 /* Encoder.swift in Sources */, 74A1AE171F2E3E8300B139A3 /* Format.swift in Sources */, - 74FA3B611F2C908D005CE521 /* DecoderHelper.swift in Sources */, + 74FA3B611F2C908D005CE521 /* Decoder.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/MsgPack/Decoder.swift b/MsgPack/Decoder.swift new file mode 100644 index 0000000..f7ae951 --- /dev/null +++ b/MsgPack/Decoder.swift @@ -0,0 +1,156 @@ +// +// Decoder.swift +// MsgPack +// +// Created by Damiaan on 29/07/17. +// Copyright © 2017 dPro. All rights reserved. +// + +import Foundation + +public class Decoder { + public func decode(_ type: T.Type, from data: Data) throws -> T { + let decoder = IntermediateDecoder(with: data) + return try T(from: decoder) + } + + public init() {} +} + +class IntermediateDecoder: Swift.Decoder { + var codingPath = [CodingKey]() + + var userInfo = [CodingUserInfoKey : Any]() + + var storage: Data + var offset = 0 + + init(with data: Data) { + storage = data + } + + func container(keyedBy type: Key.Type) throws -> KeyedDecodingContainer where Key : CodingKey { + fatalError() + } + + func unkeyedContainer() throws -> UnkeyedDecodingContainer { + fatalError() + } + + func singleValueContainer() throws -> SingleValueDecodingContainer { + return MsgPckSingleValueDecodingContainer(refferingTo: self) + } +} + +struct MsgPckSingleValueDecodingContainer: SingleValueDecodingContainer { + var codingPath = [CodingKey]() + var decoder: IntermediateDecoder + var base = 0 + + enum Error: Swift.Error { + case invalidFormat(UInt8) + } + + init(refferingTo decoder: IntermediateDecoder) { + self.decoder = decoder + } + + func decodeNil() -> Bool { + return decoder.storage[base] == FormatID.nil.rawValue + } + + func decode(_ type: Bool.Type) throws -> Bool { + let byte = decoder.storage[base] + guard byte & 0b11111110 == FormatID.false.rawValue else { + throw Error.invalidFormat(byte) + } + return byte == FormatID.true.rawValue + } + + func decode(_ type: Int.Type) throws -> Int { + fatalError("not implemented") + } + + func decode(_ type: Int8.Type) throws -> Int8 { + guard decoder.storage[base] == FormatID.int8.rawValue else { + throw Error.invalidFormat(decoder.storage[base]) + } + return decoder.storage.read(at: base+1) + } + + func decode(_ type: Int16.Type) throws -> Int16 { + guard decoder.storage[base] == FormatID.int16.rawValue else { + throw Error.invalidFormat(decoder.storage[base]) + } + return decoder.storage.bigEndianInteger(at: base+1) + } + + func decode(_ type: Int32.Type) throws -> Int32 { + guard decoder.storage[base] == FormatID.int32.rawValue else { + throw Error.invalidFormat(decoder.storage[base]) + } + return decoder.storage.bigEndianInteger(at: base+1) + } + + func decode(_ type: Int64.Type) throws -> Int64 { + guard decoder.storage[base] == FormatID.int64.rawValue else { + throw Error.invalidFormat(decoder.storage[base]) + } + return decoder.storage.bigEndianInteger(at: base+1) + } + + func decode(_ type: UInt.Type) throws -> UInt { + fatalError("not implemented") + } + + func decode(_ type: UInt8.Type) throws -> UInt8 { + guard decoder.storage[base] == FormatID.uInt8.rawValue else { + throw Error.invalidFormat(decoder.storage[base]) + } + return decoder.storage.read(at: base+1) + } + + func decode(_ type: UInt16.Type) throws -> UInt16 { + guard decoder.storage[base] == FormatID.uInt16.rawValue else { + throw Error.invalidFormat(decoder.storage[base]) + } + return decoder.storage.bigEndianInteger(at: base+1) + } + + func decode(_ type: UInt32.Type) throws -> UInt32 { + guard decoder.storage[base] == FormatID.uInt32.rawValue else { + throw Error.invalidFormat(decoder.storage[base]) + } + return decoder.storage.bigEndianInteger(at: base+1) + } + + func decode(_ type: UInt64.Type) throws -> UInt64 { + guard decoder.storage[base] == FormatID.uInt64.rawValue else { + throw Error.invalidFormat(decoder.storage[base]) + } + return decoder.storage.bigEndianInteger(at: base+1) + } + + func decode(_ type: Float.Type) throws -> Float { + guard decoder.storage[base] == FormatID.float32.rawValue else { + throw Error.invalidFormat(decoder.storage[base]) + } + return decoder.storage.read(at: base + 1) + } + + func decode(_ type: Double.Type) throws -> Double { + guard decoder.storage[base] == FormatID.float64.rawValue else { + throw Error.invalidFormat(decoder.storage[base]) + } + let bitPattern: UInt64 = decoder.storage.read(at: base + 1) + return .init(bitPattern) + } + + func decode(_ type: String.Type) throws -> String { + fatalError("not implemented") + } + + func decode(_ type: T.Type) throws -> T where T : Decodable { + fatalError("not implemented") + } +} diff --git a/MsgPack/DecoderHelper.swift b/MsgPack/DecoderHelper.swift deleted file mode 100644 index 043ce59..0000000 --- a/MsgPack/DecoderHelper.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// Decoder.swift -// MsgPack -// -// Created by Damiaan on 29/07/17. -// Copyright © 2017 dPro. All rights reserved. -// - -import Foundation - -//class MessagePackDecoder: Decoder { -// var codingPath = [CodingKey?]() -// -// var userInfo = [CodingUserInfoKey : Any]() -// -// func container(keyedBy type: Key.Type) throws -> KeyedDecodingContainer where Key : CodingKey { -// <#code#> -// } -// -// func unkeyedContainer() throws -> UnkeyedDecodingContainer { -// <#code#> -// } -// -// func singleValueContainer() throws -> SingleValueDecodingContainer { -// <#code#> -// } -// -// -//} - diff --git a/MsgPack/Format.swift b/MsgPack/Format.swift index 8b2cd28..81fadcd 100644 --- a/MsgPack/Format.swift +++ b/MsgPack/Format.swift @@ -14,7 +14,7 @@ enum Format { case `nil` case boolean(Bool) - + case positiveInt7(UInt8) case negativeInt5(UInt8) @@ -45,6 +45,56 @@ enum Format { case map32 ([(key: Format, value: Format)]) } +public enum FormatID: UInt8 { + // Do not reorder the cases because their raw values are infered based on the ordering + case `nil` = 0xC0 + + case `false` = 0xC2 + case `true` + + case bin8 + case bin16 + case bin32 + + case ext8 + case ext16 + case ext32 + + case float32 + case float64 + + case positiveInt7 = 0 + case negativeInt5 = 0b11100000 + + case uInt8 = 0xCC + case uInt16 + case uInt32 + case uInt64 + + case int8 + case int16 + case int32 + case int64 + + case fixExt1 + case fixExt2 + case fixExt4 + case fixExt8 + case fixExt16 + + case fixString = 0b10100000 + case string8 = 0xD9 + case string16 + case string32 + + case fixArray = 0b10010000 + case array16 = 0xDC + case array32 + + case fixMap = 0b10000000 + case map16 = 0xDE + case map32 +} extension Format { func appendTo(data: inout Data) { @@ -52,89 +102,91 @@ extension Format { // MARK: Optional case .nil: - data.append(0xC0) + data.append(FormatID.nil.rawValue) // MARK: Boolean case .boolean(let boolean): - data.append(boolean ? 0xC3 : 0xC2) + data.append(boolean ? FormatID.true.rawValue : FormatID.false.rawValue) // MARK: Small integers (< 8 bit) case .positiveInt7(let value): - data.append(value | 0b10000000) + data.append(value | FormatID.positiveInt7.rawValue) case .negativeInt5(let value): - data.append(value | 0b11100000) + data.append(value | FormatID.negativeInt5.rawValue) // MARK: Unsigned integers case .uInt8(let value): - data.append(0xCC) - data.append(value) + data.append(contentsOf: [ + FormatID.uInt8.rawValue, + value + ]) case .uInt16(let value): var newData = Data(count: 3) - newData[0] = 0xCD + newData[0] = FormatID.uInt16.rawValue newData.write(value: value.bigEndian, offset: 1) data.append(newData) case .uInt32(let value): var newData = Data(count: 5) - newData[0] = 0xCE + newData[0] = FormatID.uInt32.rawValue newData.write(value: value.bigEndian, offset: 1) data.append(newData) case .uInt64(let value): var newData = Data(count: 9) - newData[0] = 0xCF + newData[0] = FormatID.uInt64.rawValue newData.write(value: value.bigEndian, offset: 1) data.append(newData) // MARK: Signed integers case .int8(let value): var newData = Data(count: 2) - newData[0] = 0xD0 + newData[0] = FormatID.int8.rawValue newData.write(value: value.bigEndian, offset: 1) data.append(newData) case .int16(let value): var newData = Data(count: 3) - newData[0] = 0xD1 + newData[0] = FormatID.int16.rawValue newData.write(value: value.bigEndian, offset: 1) data.append(newData) case .int32(let value): var newData = Data(count: 5) - newData[0] = 0xD2 + newData[0] = FormatID.int32.rawValue newData.write(value: value.bigEndian, offset: 1) data.append(newData) case .int64(let value): var newData = Data(count: 9) - newData[0] = 0xD3 + newData[0] = FormatID.int64.rawValue newData.write(value: value.bigEndian, offset: 1) data.append(newData) // MARK: Floats case .float32(let value): var newData = Data(count: 5) - newData[0] = 0xCA + newData[0] = FormatID.float32.rawValue newData.write(value: value.bitPattern.bigEndian, offset: 1) data.append(newData) case .float64(let value): var newData = Data(count: 9) - newData[0] = 0xCB + newData[0] = FormatID.float64.rawValue newData.write(value: value.bitPattern.bigEndian, offset: 1) data.append(newData) // MARK: Strings case .fixString(let utf8Data): precondition(utf8Data.count < 32, "fix strings cannot contain more than 31 bytes") - data.append( UInt8(utf8Data.count) | 0b10100000) + data.append( UInt8(utf8Data.count) | FormatID.fixString.rawValue) data.append(utf8Data) case .string8(let utf8Data): - data.append(contentsOf: [0xD9, UInt8(utf8Data.count)]) + data.append(contentsOf: [FormatID.string8.rawValue, UInt8(utf8Data.count)]) data.append(utf8Data) case .string16(let utf8Data): var prefix = Data(count: 3) - prefix[0] = 0xDA + prefix[0] = FormatID.string16.rawValue prefix.write(value: UInt16(utf8Data.count).bigEndian, offset: 1) data.append(prefix) data.append(utf8Data) case .string32(let utf8Data): var prefix = Data(count: 5) - prefix[0] = 0xDB + prefix[0] = FormatID.string32.rawValue prefix.write(value: UInt32(utf8Data.count).bigEndian, offset: 1) data.append(prefix) data.append(utf8Data) @@ -142,13 +194,13 @@ extension Format { // MARK: Arrays case .fixArray(let array): precondition(array.count < 16, "fix arrays cannot contain more than 15 elements") - data.append( UInt8(array.count) | 0b10010000) + data.append( UInt8(array.count) | FormatID.fixArray.rawValue) for element in array { element.appendTo(data: &data) } case .array16(let array): var prefix = Data(count: 3) - prefix[0] = 0xDC + prefix[0] = FormatID.array16.rawValue prefix.write(value: UInt16(array.count).bigEndian, offset: 1) data.append(prefix) for element in array { @@ -156,7 +208,7 @@ extension Format { } case .array32(let array): var prefix = Data(count: 5) - prefix[0] = 0xDD + prefix[0] = FormatID.array32.rawValue prefix.write(value: UInt32(array.count).bigEndian, offset: 1) data.append(prefix) for element in array { @@ -166,14 +218,14 @@ extension Format { // 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) + data.append( UInt8(pairs.count) | FormatID.fixMap.rawValue) 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[0] = FormatID.map16.rawValue prefix.write(value: UInt16(pairs.count).bigEndian, offset: 1) data.append(prefix) for (key, value) in pairs { @@ -182,7 +234,7 @@ extension Format { } case .map32(let pairs): var prefix = Data(count: 5) - prefix[0] = 0xDE + prefix[0] = FormatID.map32.rawValue prefix.write(value: UInt32(pairs.count).bigEndian, offset: 1) data.append(prefix) for (key, value) in pairs { @@ -201,8 +253,21 @@ extension Data { } } } + + func read(at offset: Int) -> T { + return withUnsafeBytes {(byteContainer: UnsafePointer) -> T in + byteContainer.advanced(by: offset).withMemoryRebound(to: T.self, capacity: 1) {$0.pointee} + } + } + + func bigEndianInteger(at offset: Int) -> T { + return withUnsafeBytes {(byteContainer: UnsafePointer) -> T in + byteContainer.advanced(by: offset).withMemoryRebound(to: T.self, capacity: 1) {T.init(bigEndian: $0.pointee)} + } + } } +// MARK: encoding helpers extension Format { static func from(string: String) throws -> Format { guard let data = string.data(using: .utf8) else {throw MsgPackEncodingError.stringNotConvertibleToUTF8(string)} diff --git a/Playground.playground/Contents.swift b/Playground.playground/Contents.swift index 16d7a23..38e5849 100644 --- a/Playground.playground/Contents.swift +++ b/Playground.playground/Contents.swift @@ -37,3 +37,8 @@ let encodedGraph = try encoder.encode(graph) for byte in encodedGraph { print(String(byte, radix:16)) } + +let decoder = Decoder() + +let int8 = try encoder.encode(Int8(-57)) +try decoder.decode(Int8.self, from: int8)