From b23ed1e83d5daa3dfaf73846bee06a4681fea55f Mon Sep 17 00:00:00 2001 From: Filippos Sakellaropoulos Date: Fri, 22 Dec 2023 00:09:45 +0200 Subject: [PATCH 1/8] CBOR.map uses OrderedDictionary --- Package.swift | 32 ++++++++++++++++++-------------- Sources/CBOR.swift | 5 +++-- Sources/CBORDecoder.swift | 9 +++++---- Sources/CBOREncodable.swift | 5 +++-- Sources/CBOREncoder.swift | 24 ++++++++++++++++++++++-- 5 files changed, 51 insertions(+), 24 deletions(-) diff --git a/Package.swift b/Package.swift index 04f511e..541b831 100644 --- a/Package.swift +++ b/Package.swift @@ -3,18 +3,22 @@ import PackageDescription let package = Package( - name: "SwiftCBOR", - platforms: [.macOS(.v10_10), .iOS(.v10)], - products: [ - .library(name: "SwiftCBOR", targets: ["SwiftCBOR"]) - ], - targets: [ - .target(name: "SwiftCBOR", path: "Sources", exclude: ["Info.plist"]), - .testTarget( - name: "SwiftCBORTests", - dependencies: ["SwiftCBOR"], - path: "Tests", - exclude: ["Info.plist"] - ) - ] + name: "SwiftCBOR", + platforms: [.macOS(.v10_10), .iOS(.v10)], + products: [ + .library(name: "SwiftCBOR", targets: ["SwiftCBOR"]) + ], + dependencies: [ + .package(url: "https://github.com/apple/swift-collections.git", from: "1.0.6"), + ], + targets: [ + .target(name: "SwiftCBOR", dependencies: [.product(name: "Collections", package: "swift-collections")], + path: "Sources", exclude: ["Info.plist"]), + .testTarget( + name: "SwiftCBORTests", + dependencies: ["SwiftCBOR"], + path: "Tests", + exclude: ["Info.plist"] + ) + ] ) diff --git a/Sources/CBOR.swift b/Sources/CBOR.swift index cc80904..26fcf71 100644 --- a/Sources/CBOR.swift +++ b/Sources/CBOR.swift @@ -1,6 +1,7 @@ #if canImport(Foundation) import Foundation #endif +import OrderedCollections public indirect enum CBOR : Equatable, Hashable, ExpressibleByNilLiteral, ExpressibleByIntegerLiteral, ExpressibleByStringLiteral, @@ -12,7 +13,7 @@ public indirect enum CBOR : Equatable, Hashable, case byteString([UInt8]) case utf8String(String) case array([CBOR]) - case map([CBOR : CBOR]) + case map(OrderedDictionary) case tagged(Tag, CBOR) case simple(UInt8) case boolean(Bool) @@ -84,7 +85,7 @@ public indirect enum CBOR : Equatable, Hashable, public init(stringLiteral value: String) { self = .utf8String(value) } public init(arrayLiteral elements: CBOR...) { self = .array(elements) } public init(dictionaryLiteral elements: (CBOR, CBOR)...) { - var result = [CBOR : CBOR]() + var result = OrderedDictionary() for (key, value) in elements { result[key] = value } diff --git a/Sources/CBORDecoder.swift b/Sources/CBORDecoder.swift index c5659a4..5ae2b8a 100644 --- a/Sources/CBORDecoder.swift +++ b/Sources/CBORDecoder.swift @@ -1,6 +1,7 @@ #if canImport(Foundation) import Foundation #endif +import OrderedCollections public enum CBORError : Error { case unfinishedSequence @@ -79,8 +80,8 @@ public class CBORDecoder { return result } - private func readNPairs(_ n: Int) throws -> [CBOR : CBOR] { - var result: [CBOR: CBOR] = [:] + private func readNPairs(_ n: Int) throws -> OrderedDictionary { + var result = OrderedDictionary() for _ in (0.. [CBOR : CBOR] { - var result: [CBOR: CBOR] = [:] + func readPairsUntilBreak() throws -> OrderedDictionary { + var result = OrderedDictionary() var key = try decodeItem() if key == CBOR.break { return result diff --git a/Sources/CBOREncodable.swift b/Sources/CBOREncodable.swift index 82d0a92..9dab49a 100644 --- a/Sources/CBOREncodable.swift +++ b/Sources/CBOREncodable.swift @@ -1,6 +1,7 @@ #if canImport(Foundation) import Foundation #endif +import OrderedCollections public protocol CBOREncodable { /// Optional function that can potentially serve as an opportunity to optimize encoding. @@ -25,7 +26,7 @@ extension CBOR: CBOREncodable { case let .byteString(bs): return CBOR.encodeByteString(bs, options: options) case let .utf8String(str): return str.encode(options: options) case let .array(a): return CBOR.encodeArray(a, options: options) - case let .map(m): return CBOR.encodeMap(m, options: options) + case let .map(m): return CBOR.encodeCBORMap(m, options: options) #if canImport(Foundation) case let .date(d): return CBOR.encodeDate(d, options: options) #endif @@ -210,7 +211,7 @@ extension Dictionary where Key: CBOREncodable, Value: CBOREncodable { } public func toCBOR(options: CBOROptions = CBOROptions()) -> CBOR { - return CBOR.map(Dictionary(uniqueKeysWithValues: self.map { ($0.key.toCBOR(options: options), $0.value.toCBOR(options: options)) })) + return CBOR.map(OrderedDictionary(uniqueKeysWithValues: self.map { ($0.key.toCBOR(options: options), $0.value.toCBOR(options: options)) })) } } diff --git a/Sources/CBOREncoder.swift b/Sources/CBOREncoder.swift index 8ba85fd..2b65eae 100644 --- a/Sources/CBOREncoder.swift +++ b/Sources/CBOREncoder.swift @@ -1,6 +1,7 @@ #if canImport(Foundation) import Foundation #endif +import OrderedCollections let isBigEndian = Int(bigEndian: 42) == 42 @@ -153,6 +154,14 @@ extension CBOR { try CBOR.encodeMap(map, into: &res, options: options) return res } + + public static func encodeCBORMap(_ map: OrderedDictionary, options: CBOROptions = CBOROptions()) -> [UInt8] { + var res: [UInt8] = [] + res = map.count.encode(options: options) + res[0] = res[0] | 0b101_00000 + CBOR.encodeCBORMap(map, into: &res, options: options) + return res + } // MARK: - major 6: tagged values @@ -271,7 +280,7 @@ extension CBOR { dateCBOR = CBOR.unsignedInt(UInt64(seconds)) } - let map: [String: CBOREncodable] = [ + let map: Dictionary = [ AnnotatedMapDateStrategy.typeKey: AnnotatedMapDateStrategy.typeValue, AnnotatedMapDateStrategy.valueKey: dateCBOR ] @@ -405,7 +414,7 @@ extension CBOR { return try CBOR.array(anyArr.map { try cborFromAny($0) }) case is [String: Any?]: let anyMap = any as! [String: Any?] - return try CBOR.map(Dictionary( + return try CBOR.map(OrderedDictionary( uniqueKeysWithValues: anyMap.map { try (cborFromAny($0.key), cborFromAny($0.value)) } )) case is Void: @@ -454,6 +463,17 @@ extension CBOR { res.append(contentsOf: encodedVal) } } + + private static func encodeCBORMap(_ map: OrderedDictionary, into res: inout [UInt8], options: CBOROptions = CBOROptions()) { + let sortedKeysWithEncodedKeys = map.keys + + sortedKeysWithEncodedKeys.forEach { keyTuple in + let encodedKey = keyTuple.encode(options: options) + res.append(contentsOf: encodedKey) + let encodedVal = map[keyTuple]!.encode(options: options) + res.append(contentsOf: encodedVal) + } + } } public enum CBOREncoderError: Error { From 74763c2624c09e8702967d8bef01767d12a71615 Mon Sep 17 00:00:00 2001 From: Filippos Sakellaropoulos Date: Mon, 8 Jan 2024 12:18:25 +0200 Subject: [PATCH 2/8] Fix unit tests, add encode, toCBOR extensions to OrderedDictionary --- Package.resolved | 16 ++++++++++++++++ Sources/CBOREncodable.swift | 11 +++++++++++ Tests/CBOREncodableTests.swift | 4 +++- Tests/CBORTests.swift | 3 ++- 4 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 Package.resolved diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000..b60ad09 --- /dev/null +++ b/Package.resolved @@ -0,0 +1,16 @@ +{ + "object": { + "pins": [ + { + "package": "swift-collections", + "repositoryURL": "https://github.com/apple/swift-collections.git", + "state": { + "branch": null, + "revision": "d029d9d39c87bed85b1c50adee7c41795261a192", + "version": "1.0.6" + } + } + ] + }, + "version": 1 +} diff --git a/Sources/CBOREncodable.swift b/Sources/CBOREncodable.swift index 9dab49a..74cccea 100644 --- a/Sources/CBOREncodable.swift +++ b/Sources/CBOREncodable.swift @@ -215,6 +215,17 @@ extension Dictionary where Key: CBOREncodable, Value: CBOREncodable { } } + +extension OrderedDictionary where Key: CBOREncodable, Value: CBOREncodable { + public func encode(options: CBOROptions = CBOROptions()) -> [UInt8] { + return CBOR.encodeCBORMap(OrderedDictionary(uniqueKeysWithValues: self.map { ($0.key.toCBOR(options: options), $0.value.toCBOR(options: options)) }), options: options) + } + + public func toCBOR(options: CBOROptions = CBOROptions()) -> CBOR { + return CBOR.map(OrderedDictionary(uniqueKeysWithValues: self.map { ($0.key.toCBOR(options: options), $0.value.toCBOR(options: options)) })) + } +} + extension Optional where Wrapped: CBOREncodable { public func encode(options: CBOROptions = CBOROptions()) -> [UInt8] { switch self { diff --git a/Tests/CBOREncodableTests.swift b/Tests/CBOREncodableTests.swift index 83f7a0d..e2e3630 100644 --- a/Tests/CBOREncodableTests.swift +++ b/Tests/CBOREncodableTests.swift @@ -1,5 +1,6 @@ import XCTest @testable import SwiftCBOR +import OrderedCollections class CBOREncodableTests: XCTestCase { func testToCBOR() { @@ -58,9 +59,10 @@ class CBOREncodableTests: XCTestCase { XCTAssertEqual(CBOR.array([CBOR.unsignedInt(1), CBOR.unsignedInt(2)]), [1, 2].toCBOR(options: CBOROptions())) + let orderedDict: OrderedDictionary = ["a": 1, "b": 2] XCTAssertEqual( CBOR.map([CBOR.utf8String("a"): CBOR.unsignedInt(1), CBOR.utf8String("b"): CBOR.unsignedInt(2)]), - ["a": 1, "b": 2].toCBOR(options: CBOROptions()) + orderedDict.toCBOR(options: CBOROptions()) ) } } diff --git a/Tests/CBORTests.swift b/Tests/CBORTests.swift index e888f24..fdef8d4 100644 --- a/Tests/CBORTests.swift +++ b/Tests/CBORTests.swift @@ -1,4 +1,5 @@ import XCTest +import OrderedCollections @testable import SwiftCBOR class CBORTests: XCTestCase { @@ -50,7 +51,7 @@ class CBORTests: XCTestCase { let cborEncoded: [UInt8] = try! CBOR.encodeMap(dictionary) var cbor = try! CBOR.decode(cborEncoded)! - let nestedMap: [CBOR: CBOR] = [ + let nestedMap: OrderedDictionary = [ "joe": "schmoe", "age": 56 ] From 9dd7aadbbca6840d77978b818090086fe604161a Mon Sep 17 00:00:00 2001 From: Filippos Sakellaropoulos Date: Tue, 9 Apr 2024 15:33:27 +0300 Subject: [PATCH 3/8] Update README.md with fork information and PR link --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 67a7420..e817766 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ [![unlicense](https://img.shields.io/badge/un-license-green.svg?style=flat)](http://unlicense.org) +## SwiftCBOR (Scytales clone) + # SwiftCBOR +This is a fork of the original SwiftCBOR library based on the following [PR #96](https://github.com/valpackett/SwiftCBOR/pull/96) . The original library is available [here](https://github.com/valpackett/SwiftCBOR) + A [CBOR (RFC 7049 Concise Binary Object Representation)](http://cbor.io) decoder and encoder in Swift. Encode directly from Swift types or use a wrapper object. Decode to a CBOR value type that can be accessed with native Swift subscripting and expressed with the equivalent literal notation. From 77a3e928fb6b191e8450675251fcb62b88c21563 Mon Sep 17 00:00:00 2001 From: Filippos Sakellaropoulos Date: Wed, 18 Sep 2024 22:04:21 +0300 Subject: [PATCH 4/8] Refactor CBOR.swift Tag struct to conform to Sendable protocol --- Sources/CBOR.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/CBOR.swift b/Sources/CBOR.swift index 26fcf71..3bf2445 100644 --- a/Sources/CBOR.swift +++ b/Sources/CBOR.swift @@ -133,7 +133,7 @@ public indirect enum CBOR : Equatable, Hashable, } } - public struct Tag: RawRepresentable, Equatable, Hashable { + public struct Tag: RawRepresentable, Hashable, Sendable { public let rawValue: UInt64 public init(rawValue: UInt64) { From c012506c4d72f4e07db4b52df867b30560451799 Mon Sep 17 00:00:00 2001 From: Filippos Sakellaropoulos Date: Wed, 18 Sep 2024 22:04:23 +0300 Subject: [PATCH 5/8] Update Package.swift and Package.resolved --- Package.resolved | 25 ++++++++++++------------- Package.swift | 4 ++-- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/Package.resolved b/Package.resolved index b60ad09..4651d99 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,16 +1,15 @@ { - "object": { - "pins": [ - { - "package": "swift-collections", - "repositoryURL": "https://github.com/apple/swift-collections.git", - "state": { - "branch": null, - "revision": "d029d9d39c87bed85b1c50adee7c41795261a192", - "version": "1.0.6" - } + "originHash" : "1658920b6ff75b4d90f9cc1d550887ae5383d8a4f1bb602d37aa7b303cab1228", + "pins" : [ + { + "identity" : "swift-collections", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-collections.git", + "state" : { + "revision" : "9bf03ff58ce34478e66aaee630e491823326fd06", + "version" : "1.1.3" } - ] - }, - "version": 1 + } + ], + "version" : 3 } diff --git a/Package.swift b/Package.swift index 541b831..46d04ec 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.5 +// swift-tools-version:6.0 import PackageDescription @@ -9,7 +9,7 @@ let package = Package( .library(name: "SwiftCBOR", targets: ["SwiftCBOR"]) ], dependencies: [ - .package(url: "https://github.com/apple/swift-collections.git", from: "1.0.6"), + .package(url: "https://github.com/apple/swift-collections.git", from: "1.1.3"), ], targets: [ .target(name: "SwiftCBOR", dependencies: [.product(name: "Collections", package: "swift-collections")], From 3f55f67b945599339fffb80b05f10c0b7510bd4d Mon Sep 17 00:00:00 2001 From: Filippos Sakellaropoulos Date: Wed, 18 Sep 2024 22:32:03 +0300 Subject: [PATCH 6/8] Refactor CBOR struct to conform to Sendable protocol --- Sources/CBOR.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/CBOR.swift b/Sources/CBOR.swift index 3bf2445..813ff55 100644 --- a/Sources/CBOR.swift +++ b/Sources/CBOR.swift @@ -3,7 +3,7 @@ import Foundation #endif import OrderedCollections -public indirect enum CBOR : Equatable, Hashable, +public indirect enum CBOR : Equatable, Hashable, Sendable, ExpressibleByNilLiteral, ExpressibleByIntegerLiteral, ExpressibleByStringLiteral, ExpressibleByArrayLiteral, ExpressibleByDictionaryLiteral, ExpressibleByBooleanLiteral, ExpressibleByFloatLiteral { From 2c8c55273d4c4aae21bb46c2afbae79ee072eff4 Mon Sep 17 00:00:00 2001 From: Filippos Sakellaropoulos Date: Wed, 18 Sep 2024 22:40:15 +0300 Subject: [PATCH 7/8] Update platforms in Package.swift to support macOS 10.13 and iOS 13 --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 46d04ec..bb3f1dc 100644 --- a/Package.swift +++ b/Package.swift @@ -4,7 +4,7 @@ import PackageDescription let package = Package( name: "SwiftCBOR", - platforms: [.macOS(.v10_10), .iOS(.v10)], + platforms: [.macOS(.v10_13), .iOS(.v13)], products: [ .library(name: "SwiftCBOR", targets: ["SwiftCBOR"]) ], From 7d6271345212f3864309666b6eb9f9941be6873d Mon Sep 17 00:00:00 2001 From: Filippos Sakellaropoulos Date: Mon, 22 Sep 2025 14:53:29 +0300 Subject: [PATCH 8/8] inline constant for NSEC_PER_SEC --- Sources/CBOREncoder.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/CBOREncoder.swift b/Sources/CBOREncoder.swift index 2b65eae..420b054 100644 --- a/Sources/CBOREncoder.swift +++ b/Sources/CBOREncoder.swift @@ -265,7 +265,8 @@ extension CBOR { let (integral, fractional) = modf(timeInterval) let seconds = Int64(integral) - let nanoseconds = Int32(fractional * Double(NSEC_PER_SEC)) + // The NSEC_PER_SEC value is 1,000,000,000 (one billion), representing the number of nanoseconds in one second. + let nanoseconds = Int32(fractional * Double(1_000_000_000)) switch options.dateStrategy { case .annotatedMap: