Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 35 additions & 7 deletions Sources/MessagePackDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,19 @@
import Foundation

open class MessagePackDecoder: Decoder {
public var allowIntKeys: Bool
public var codingPath: [CodingKey] = []
public var userInfo: [CodingUserInfoKey : Any] = [:]
private var storage = MessagePackStorage()

public init() {}
public init(allowIntKeys: Bool = false) {
self.allowIntKeys = allowIntKeys
}

public init(referencing: Data, codingPath: [CodingKey] = []) {
public init(referencing: Data, codingPath: [CodingKey] = [], allowIntKeys: Bool = false) {
storage.push(container: referencing)
self.codingPath = codingPath
self.allowIntKeys = allowIntKeys
}

public func container<Key: CodingKey>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> {
Expand Down Expand Up @@ -123,25 +127,45 @@ private extension MessagePackDecoder {
}

extension MessagePackDecoder {
private func convert<Key: CodingKey>(packKey: MessagePackType.MapType.Key, to: Key.Type) -> Key? {
switch packKey {
case .int(let intValue):
if allowIntKeys {
return Key(intValue: intValue)
} else {
return Key(stringValue: String(intValue))
}
case .string(let stringValue):
return Key(stringValue: stringValue)
}
}
private func convertToPackKey(from codingKey: CodingKey) -> MessagePackType.MapType.Key {
if allowIntKeys, let intValue = codingKey.intValue {
return .int(intValue)
} else {
return .string(codingKey.stringValue)
}
}

struct KeyedContainer<Key: CodingKey>: KeyedDecodingContainerProtocol {
private let decoder: MessagePackDecoder
private(set) var codingPath: [CodingKey]
private(set) var allKeys: [Key]
private var container: [String: Data] = [:]
private var container: [MessagePackType.MapType.Key: Data] = [:]

init(referencing decoder: MessagePackDecoder, container: [String: Data]) {
init(referencing decoder: MessagePackDecoder, container: [MessagePackType.MapType.Key: Data]) {
self.decoder = decoder
self.codingPath = decoder.codingPath
self.allKeys = container.keys.compactMap { Key(stringValue: $0) }
self.allKeys = container.keys.compactMap { decoder.convert(packKey: $0, to: Key.self) }
self.container = container
}

func contains(_ key: Key) -> Bool {
return container[key.stringValue] != nil
return container[decoder.convertToPackKey(from: key)] != nil
}

func findEntry(by key: CodingKey) throws -> Data {
guard let entry = self.container[key.stringValue] else {
guard let entry = self.container[decoder.convertToPackKey(from: key)] else {
let context = DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")
throw DecodingError.keyNotFound(key, context)
}
Comment on lines 163 to 171

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you change as follows, I think that Decoder does not need allowIntKeys.

        func contains(_ key: Key) -> Bool {
            if let intValue = key.intValue, let _ = container[.int(intValue)] {
                return true
            }
            return container[.string(key.stringValue)] != nil
        }

        func findEntry(by key: CodingKey) throws -> Data {
            if let intValue = key.intValue, let entry = container[.int(intValue)] {
                return entry
            }

            if let entry = self.container[.string(key.stringValue)] {
                return entry
            }

            let context = DecodingError.Context(codingPath: self.decoder.codingPath, debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\").")
            throw DecodingError.keyNotFound(key, context)
        }

Expand Down Expand Up @@ -542,3 +566,7 @@ extension MessagePackDecoder {
}
}
}




23 changes: 17 additions & 6 deletions Sources/MessagePackEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@
import Foundation

open class MessagePackEncoder: Encoder {
public var allowIntKeys: Bool
public var codingPath: [CodingKey] = []
public var userInfo: [CodingUserInfoKey : Any] = [:]
fileprivate var storage = MessagePackStorage()

public init() {}
public init(allowIntKeys: Bool = false) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for a good idea!
But I want to change to:

  1. Add KeyEncodingType.
public enum KeyEncodingType {
    case string
    case int
}
  1. Change the initializer.
    private let keyEncodingType: KeyEncodingType

    public init(keyEncodingType: KeyEncodingType = .string) {
        self.keyEncodingType = keyEncodingType
    }

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds like a good idea.

self.allowIntKeys = allowIntKeys
}

public func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key : CodingKey {
return KeyedEncodingContainer(KeyedContainer<Key>(referencing: self, codingPath: codingPath) { [weak self] in
Expand Down Expand Up @@ -109,8 +112,13 @@ extension MessagePackEncoder {
}

fileprivate func add(_ value: Data, forKey key: CodingKey) {
let key = encoder.boxMessagePack(key.stringValue)
self.packedData.append(key)
let keyData: Data
if encoder.allowIntKeys, let intValue = key.intValue {
keyData = encoder.boxMessagePack(intValue)
} else {
keyData = encoder.boxMessagePack(key.stringValue)
}
self.packedData.append(keyData)
self.packedData.append(value)
count += 1
}
Expand Down Expand Up @@ -420,11 +428,11 @@ extension MessagePackEncoder {
}
}

class MessagePackReferencingKeyedEncoder<Key: CodingKey>: MessagePackEncoder {
class MessagePackReferencingKeyedEncoder<Key: CodingKey, Key2: CodingKey>: MessagePackEncoder {
private let container: KeyedContainer<Key>
private let key: CodingKey
private let key: Key2

init(container: KeyedContainer<Key>, key: CodingKey) {
init(container: KeyedContainer<Key>, key: Key2) {
self.container = container
self.key = key
super.init()
Expand All @@ -450,3 +458,6 @@ extension MessagePackEncoder {
}
}
}



18 changes: 15 additions & 3 deletions Sources/MessagePackTypes/MessagePackType+MapType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -102,18 +102,29 @@ extension MessagePackType.MapType {
}

extension MessagePackType.MapType {
static func split(for value: Data) throws -> [String : Data] {
enum Key: Hashable {
case string(String)
case int(Int)
}

static func split(for value: Data) throws -> [Key : Data] {
guard let firstByte = value.first else { throw MessagePackError.emptyData }

let type = try MessagePackType.MapType(firstByte)
let startIndex = type.lengthRange.upperBound
let length = try type.length(value)

return try (0..<length)
.reduce(into: (dictionary: [String : Data](), index: startIndex)) { args, _ in
.reduce(into: (dictionary: [Key : Data](), index: startIndex)) { args, _ in
let key = try value.subdata(startIndex: args.index).firstMessagePackeValue()
let value = try value.subdata(startIndex: args.index + key.count).firstMessagePackeValue()
args.dictionary[try String.unpack(for: key)] = value
let keyType = try MessagePackType(key.first!)
switch keyType {
case .signedInteger, .unsignedInteger:
args.dictionary[.int(try Int.unpack(for: key))] = value
default:
args.dictionary[.string(try String.unpack(for: key))] = value
}
args.index += (key.count + value.count)
}
.dictionary
Expand All @@ -139,3 +150,4 @@ extension MessagePackType.MapType {
}
}
}

22 changes: 22 additions & 0 deletions Tests/MessagePackerTests/MapPackedTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,32 @@ class MapPackedTests: XCTestCase {
XCTAssertEqual((try encoder.encode(input)).count, output)
}

func testMapAllowIntKey() {
let input = [8: 1.1]
let output = Data([129, 8, 203, 63, 241, 153, 153, 153, 153, 153, 154])
let encoder = MessagePackEncoder(allowIntKeys: true)
XCTAssertEqual(try encoder.encode(input), output)
}

func testMapAllowIntKeyWithString() {
let input = ["A": 9]
let output = Data([129, 161, 65, 9])
let encoder = MessagePackEncoder(allowIntKeys: true)
XCTAssertEqual(try encoder.encode(input), output)
}

func testMapAllowIntKeyWithIntegerInString() {
let input = ["1": 9]
let output = Data([129, 1, 9])
let encoder = MessagePackEncoder(allowIntKeys: true)
XCTAssertEqual(try encoder.encode(input), output)
}

func test2DimensionalMap() {
let input = ["a": ["b": 2]]
let output = Data([129, 161, 97, 129, 161, 98, 2])
XCTAssertEqual(try encoder.encode(input), output)
}
}


27 changes: 27 additions & 0 deletions Tests/MessagePackerTests/MapUnpackedTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,37 @@ class MapUnpackedTests: XCTestCase {
}
}

func testAllowIntKey() {
let input = Data([130, 2, 203, 64, 1, 153, 153, 153, 153, 153, 154, 1, 203, 63, 241, 153, 153, 153, 153, 153, 154])
let output: [Int: Double] = [1: 1.1, 2: 2.2]

do {
let decoder = MessagePackDecoder(allowIntKeys: true)
let result: [Int: Double] = try decoder.decode(Dictionary.self, from: input)
result.forEach { XCTAssertEqual(output[$0.key], $0.value) }
} catch {
XCTFail(error.localizedDescription)
}
}

func testAllowIntKeyWithString() {
let input = Data([130, 161, 65, 9, 1, 9])
let output: [String: Int] = ["1": 9, "A": 9]

do {
let decoder = MessagePackDecoder(allowIntKeys: true)
let result: [String: Int] = try decoder.decode(Dictionary.self, from: input)
result.forEach { XCTAssertEqual(output[$0.key], $0.value) }
} catch {
XCTFail(error.localizedDescription)
}
}

func test2DimensionalMap() {
let input = Data([129, 161, 97, 129, 161, 98, 2])
let output = ["a": ["b": 2]]

XCTAssertEqual(try decoder.decode(Dictionary.self, from: input), output)
}
}