Giter Club home page Giter Club logo

swiftcbor's Introduction

unlicense

SwiftCBOR

A CBOR (RFC 7049 Concise Binary Object Representation) 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.

  • A fully cross-platform Swift 5.x package!
  • Codable support!
  • Negative integers are decoded as NegativeInt(UInt), where the actual number is -1 - i (CBOR's negative integers can be larger than 64-bit signed integers).
  • Tags are decoded, but not processed. Tagged values are encoded, but not type-checked. Do it yourself :-)
  • Literal convertibles are defined for the CBOR type!
  • And subscript too. So you can access CBOR maps and arrays like this: myDecodedObject["numbers"][1].
  • If you want to decode from a stream, implement the CBORInputStream protocol on your stream and create the decoder like this: CBORDecoder(stream: yourStream).
  • Half floats can be decoded to a Float, maybe even correctly. Encoding Float16s are not supported (they do not exist in Swift).
  • Memory efficiency of encoding needs tuning. (Encoding is not typically done in-place.)
  • Encoding indefinite-length data is supported but you need to explicitly add open and close information to your streaming data.
  • cbor.me is recommended for viewing your CBOR-encoded data.

Installation

There are many ways: Swift Package Manager, CocoaPods, git submodule...

The CocoaPod is submitted by contributors, updates can be delayed there.

Swift Package Manager is the recommended dependency manager.

Decoding

import SwiftCBOR

let decoded = try! CBOR.decode([0x9f, 0x18, 255, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2, 0x18, 1, 0x79, 0x00, 3, 0x41, 0x42, 0x43, 0x79, 0x00, 3, 0x41, 0x42, 0x43, 0xff])
print(decoded)
// CBOR.array([CBOR.unsignedInt(255), CBOR.array([CBOR.unsignedInt(1), CBOR.utf8String("ABC")]), CBOR.utf8String("ABC")])

To unwrap the decoded CBOR values, use PATTERN MATCHING!!

Encoding

Encoding a value returns an array of bytes, [UInt8]. You can encode with CBOR.encode(myValue) or myValue.encode(). Any type that conforms to the CBOREncodable protocol may be encoded. You can implement the CBOREncodable protocol for your types and then encode as usual.

CBOR.encode(100)  // --> [0x18, 0x64] of type [UInt8]
Int(100).encode() // --> [0x18, 0x64]. Int conforms to the CBOREncodable protocol
"hello".encode()  // --> [0x65, 0x68, 0x65, 0x6c, 0x6c, 0x6f]. So does String
CBOR.encode(["a", "b", "c"])

let byteString: [UInt8] = [0x01, 0x02]
CBOR.encode(byteString, asByteString: true)

Due to Swift's incomplete generics system, you cannot call someArray.encode() or someDictionary.encode(), but you can simply use CBOR.encode(someArrayOrMap) so long as your array items or map key and value types conform to CBOREncodable.

In some cases it may be necessary to create a CBOR intermediate representation before encoding. For example, if you want to encode an array or dictionary containing heterogeneous types, as is common for JSON-like objects, you can't use native Swift maps yet. You can implement CBOREncodable on your type that would build a CBOR value and encode that, or do the CBOR value thing without CBOREncodable.

The CBOR enum can be expressed with literals, but note that variables are not literals, so you might have to call the constructors manually.

public protocol CBOREncodable {
    func encode(options: CBOROptions) -> [UInt8]
}

struct MyStruct: CBOREncodable {
    var x: Int
    var y: String

    public func encode(options: CBOROptions = CBOROption()) -> [UInt8] {
        let cborWrapper: CBOR = [
            "x": CBOR(integerLiteral: self.x), // You can use the literal constructors
            "y": CBOR.utf8String(self.y), // Or the enum variants
            "z": 123 // Or literals
        ]
        return cborWrapper.encode()
    }
}

MyStruct(x: 42, y: "words").encode()
// --> bytes (as hex): a2 61 79 65 77 6f 72 64 73 61 78 18 2a

The encode function doesn't have to look like that. If you want to do something custom, like preserving the order of map keys, you can build the [UInt8] manually. Look at the Encoder functions for inspiration.

Encoding API

The current general-purpose API is listed below. When you need fine grained control over the type you are encoding, use the following.

func encode<T: CBOREncodable>(_ value: T) -> [UInt8]
func encode<A: CBOREncodable, B: CBOREncodable>(_ dict: [A: B]) -> [UInt8]

// NOTE: Please see the note on encoding byte strings at the end of this readme.
func encode<T: CBOREncodable>(_ array: [T], asByteString: Bool = false) -> [UInt8]

/// Only needed for fine-grained control:
func encodeUInt{8, 16, 32, 64}(_ x: UInt8) -> [UInt8]
func encodeNegativeInt(_ x: Int) -> [UInt8]
func encodeByteString(_ bs: [UInt8]) -> [UInt8] // does no endian interpretation
func encodeString(_ str: String) -> [UInt8]
func encodeArray<T: CBOREncodable>(_ arr: [T]) -> [UInt8]
func encodeMap<A: CBOREncodable, B: CBOREncodable>(_ map: [A: B]) -> [UInt8]
func encodeTagged<T: CBOREncodable>(tag: UInt8, value: T) -> [UInt8]
func encodeSimpleValue(_ x: UInt8) -> [UInt8]
func encode{Null, Undefined, Break}() -> [UInt8]
func encodeFloat(_ x: Float) -> [UInt8]
func encodeDouble(_ x: Double) -> [UInt8]
func encodeBool(_ x: Bool) -> [UInt8]

Indefinite-length data

To encode indefinite length arrays, maps, strings, and byte strings, explicitly use the open- and close-stream CBOR values. In between these two values, use encoded array and map chunks with CBOR.encodeArrayChunk and CBOR.encodeMapChunk. Indefinite string and bytestrings can be encoded as normal (i.e. they don't need their own 'chunk' function).

let map: [String: Int] = ["a": 1]
let map2 = ["B": 2]
CBOR.encodeMapStreamStart() + CBOR.encodeMapChunk(map) + CBOR.encodeMapChunk(map2) + CBOR.encodeStreamEnd()

let bs: [UInt8] = [0xf0]
let bs2: [UInt8] = [0xff]
CBOR.encodeByteStringStreamStart()
    + CBOR.encode(bs, asByteString: true)
    + CBOR.encode(bs2, asByteString: true)
    + CBOR.encodeStreamEnd()

// Current stream-encoding API:
func encodeArrayStreamStart() -> [UInt8]
func encodeMapStreamStart() -> [UInt8]
func encodeStringStreamStart() -> [UInt8]
func encodeByteStringStreamStart() -> [UInt8]
func encodeStreamEnd() -> [UInt8] // Equal to CBOR.encodeBreak()
func encodeArrayChunk<T: CBOREncodable>(_ chunk: [T]) -> [UInt8]
func encodeMapChunk<A: CBOREncodable, B: CBOREncodable>(_ map: [A: B]) -> [UInt8]

Note on endian reversal

Finally, a technical note on encoding byte string when using the general purpose array encoder, CBOR.encode(..). If the function parameter asByteString is true, then arrays of ALL types EXCEPT UInt8 will be have the raw bytes of each item reversed (but not the order of the items together) if the computer is little endian (CBOR uses big endian or network byte order). Arrays of UInt8, are considered to be already in network byte order.

Contributing

By participating in this project you agree to follow the Contributor Code of Conduct.

The list of contributors is available on GitHub.

License

This is free and unencumbered software released into the public domain. For more information, please refer to the UNLICENSE file or unlicense.org.

swiftcbor's People

Contributors

bgiori avatar blanu avatar brycehammond avatar dhiraj avatar dimitribouniol avatar hamchapman avatar headlessme avatar martinreichart avatar mbalex99 avatar mrcljx avatar oliviermartin avatar satori-ytolstoguzov avatar silkypants avatar thomaspockrandt avatar timds avatar valpackett avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

swiftcbor's Issues

Encoding arrays of multiple types

Hello,
Is it possible to encode an array of type Any?
I would like to encode array with multiple objects but from the examples its just possible to encode arrays of same type?

Trying to encode an array like this

let barr = [2147483647,[1332, 0, "0.3.0-dev", "0.3.0-dev", "4.0.2", "866191031649643", "8935101811542082547", "M95FAR02A08","AXN_2.32_3337_15010801", 3, 0],[1398, 8],[1376, 7, 1]] as [Any]

Thanks

Orderd Dictionary/Map

Hi Folks,

is there any way to get the ordered map back?

return CBOR.map([ 1: "key1", 2: "key2", 3: "key3", 4: "key4", ])

after this, I'm getting a random map each time and need to get ith sorted somehow.

Could you please help me with this?

Semantic versioning not followed

Thank you for this library. It seems helpful. However there were some breaking API changes between versions 0.4.4 and 0.4.5. This causes builds that rely on CocoaPods to break because it is common to automatically accept patch-version updates. It would be helpful in the future to update the major or minor point versions (e.g., 0.5.0) whenever there are changes to the public API.

Encoding `UInt = 1` encodes to 9 bytes

In 0.4.4 this test succeeded. In 0.4.6 this encodes to a 9 byte array...
With Int instead of UInt, the test also succeeds.

Is this a bug or something that I'm missing in the CBOR standard?

image

Decoding issue

Hi,

I'm trying to decode the following CBOR message:

BF                                      # map(*)
   63                                   # text(3)
      6F6666                            # "off"
   00                                   # unsigned(0)
   64                                   # text(4)
      64617461                          # "data"
   59 012C                              # bytes(300)
      4C6F72656D20697073756D20646F6C6F722073697420616D65742C20636F6E73656374657475
722061646970697363696E6720656C69742E205175697371756520657820616E74652C2073656
D7065722075742066617563696275732070686172657472612C20616363756D73616E20657420
61756775652E20566573746962756C756D2076756C70757461746520656C6974206C6967756C
612C2065752074696E636964756E74206F726369206C6163696E696120717569732E2050726F6
96E207363656C6572697371756520647569206174206D61676E6120706C6163657261742C2069
6420626C616E6469742066656C6973207665686963756C612E204D616563656E617320616320
6E69736C2061206F64696F2076617269757320636F6E64696D656E74756D206C # "Lorem ipsum 
dolor sit amet, consectetur adipiscing elit. Quisque ex ante, semper ut faucibus pharetra, accumsan 
et augue. Vestibulum vulputate elit ligula, eu tincidunt orci lacinia quis. Proin scelerisque dui at 
magna placerat, id blandit felis vehicula. Maecenas ac nisl a odio varius condimentum l"
   62                                   # text(2)
      7263                              # "rc"
   00                                   # unsigned(0)
   63                                   # text(3)
      6C656E                            # "len"
   19 012C                              # unsigned(300)
   FF                                   # primitive(*)

Here's written as HEX:
BF636F666600646461746159012C4C6F72656D20697073756D20646F6C6F722073697420616D65742C20636F6E73656374657475722061646970697363696E6720656C69742E205175697371756520657820616E74652C2073656D7065722075742066617563696275732070686172657472612C20616363756D73616E2065742061756775652E20566573746962756C756D2076756C70757461746520656C6974206C6967756C612C2065752074696E636964756E74206F726369206C6163696E696120717569732E2050726F696E207363656C6572697371756520647569206174206D61676E6120706C6163657261742C20696420626C616E6469742066656C6973207665686963756C612E204D616563656E6173206163206E69736C2061206F64696F2076617269757320636F6E64696D656E74756D206C62726300636C656E19012CFF
Link to CBOR.me

The parser crashes when trying to decode the byte array ("data" field) on:

private func readUInt<T: UnsignedInteger>(_ n: Int) throws -> T {
        return UnsafeRawPointer(Array(try istream.popBytes(n).reversed())).load(as: T.self)
}

with the following error:
Thread 8: Simultaneous accesses to 0x1c0270690, but modification requires exclusive access
I'm using payload = try CBOR.decode([UInt8](payloadData!)) to parse the message.

CC: @bgiori

Incorrect encoding of negative 64 bit int

Hello! Thanks for a great library!

I noticed that the encoding of negative 64 bit integers are wrong. You need to subtract 1 to get the correct value. See the RFC 7049

Major type 1:  a negative integer.  The encoding follows the rules
      for unsigned integers (major type 0), except that the value is
      then -1 minus the encoded unsigned integer.  For example, the
      integer -500 would be 0b001_11001 (major type 1, additional
      information 25) followed by the two bytes 0x01f3, which is 499 in
      decimal.

But you are doing the two's complement:

    public static func encodeNegativeInt(_ x: Int64) -> [UInt8] {
        assert(x < 0)
        var res = encodeVarUInt(~UInt64(bitPattern: x))
        res[0] = res[0] | 0b001_00000
        return res
    }

I got it working by subtracting 1. I also added a static func accepting Int64, like this:

public extension CBOR {
    static func int64(_ int: Int64) -> CBOR {
        if int < 0 {
            return CBOR.negativeInt(UInt64(abs(int)-1))
        } else {
            return CBOR.unsignedInt(UInt64(int))
        }
    }
}

You could probably add a few test cases in your unit test where you explicitly test UInt64 :)

Thx!

Unable to launch the app in Device

Hi, integrated framework through carthage, able to run in simulator but not in device. Always showing "Unable to install" Let me know how to fix this issue.

UnkeyedDecodingContainer - corrupted data causes crash

Could we add a guard check before we touch data array with start and end indexes? We should throw an error if data doesn't contain requested range. It causes fatal error if data array doesn't contain range.startIndex..<(range.endIndex)

let container = _CBORDecoder.SingleValueContainer(data: self.data[range.startIndex..<(range.endIndex)], codingPath: self.codingPath, userInfo: self.userInfo)

CodableCBORDecoder crashes when trying to decode

When trying to decode data using CodableCBORDecoder fails to decode.

Code usage:

// Hex string
let str = "bf6566696c65739fa5626964016474696d651a001b7d4868636865636b73756d1a0ad227576473697a6505646e616d656d2f6c66732f68722f31312e6872a5626964026474696d651a0036f50568636865636b73756d1ae313ea866473697a65181e646e616d656d2f6c66732f68722f31322e6872a5626964036474696d651a00526cc768636865636b73756d1ad154a2506473697a651819646e616d656d2f6c66732f68722f31332e6872a5626964046474696d651a006de48068636865636b73756d1a33d0d3f36473697a651882646e616d656d2f6c66732f68722f31342e6872a5626964056474696d651a00895c4868636865636b73756d1a43e41b1e6473697a6518dc646e616d656d2f6c66732f68722f31352e6872ff6673746174757301ff"

// decoding hexaData
do {
    let result = try CodableCBORDecoder().decode(CborFiles.self, from: str.hexaData)
    print(result.files)
} catch {
    print(error)
}

// MARK: - CborFiles
struct CborFiles: Codable {
    let files: [File]
    let status: Int
}

// MARK: - File
struct File: Codable {
    let name: String
    let time, id, size, checksum: Int
}

Error message:

SwiftCBOR/AnyCodingKey.swift:64: Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.typeMismatch(Swift.Double, Swift.DecodingError.Context(codingPath: [], debugDescription: "Invalid format: [255]", underlyingError: nil))

Extension for string protocol

extension StringProtocol {
    var hexaData: Data { .init(hexa) }
    var hexaBytes: [UInt8] { .init(hexa) }
    private var hexa: UnfoldSequence<UInt8, Index> {
        sequence(state: startIndex) { startIndex in
            guard startIndex < self.endIndex else { return nil }
            let endIndex = self.index(startIndex, offsetBy: 2, limitedBy: self.endIndex) ?? self.endIndex
            defer { startIndex = endIndex }
            return UInt8(self[startIndex..<endIndex], radix: 16)
        }
    }
}

New release 0.4.4

Any plans to tag a 0.4.4 release with the changes merged in October?

Simultaneous accesses crash

Moving to XCode 9 and Swift 4 I encountered this crash in our application.

I tried running the tests in the SwiftCBOR project and they reproduced the problem.

Log running test cases:

Test Suite 'All tests' started at 2017-09-25 15:12:37.081
Test Suite 'SwiftCBORTests.xctest' started at 2017-09-25 15:12:37.082
Test Suite 'CBORDecoderTests' started at 2017-09-25 15:12:37.082
Test Case '-[SwiftCBORTests.CBORDecoderTests testDecodeArrays]' started.
Simultaneous accesses to 0x1025db280, but modification requires exclusive access.
Previous access (a modification) started at SwiftCBOR`CBORDecoder.decodeItem() + 13099 (0x106951fcb).
Current access (a modification) started at:
0 libswiftCore.dylib 0x0000000106cef930 swift_beginAccess + 605
1 SwiftCBOR 0x000000010694cf40 CBORDecoder.readUInt(:) + 120
2 SwiftCBOR 0x000000010694eca0 CBORDecoder.decodeItem() + 13240
3 SwiftCBOR 0x000000010694d8b0 closure #1 in CBORDecoder.readN(
:) + 47
4 SwiftCBOR 0x0000000106959f30 partial apply for closure #1 in CBORDecoder.readN(:) + 22
5 SwiftCBOR 0x000000010694d9d0 thunk for @callee_owned (@unowned Int) -> (@owned CBOR, @error @owned Error) + 29
6 SwiftCBOR 0x0000000106959f90 partial apply for thunk for @callee_owned (@unowned Int) -> (@owned CBOR, @error @owned Error) + 91
7 libswiftCore.dylib 0x0000000106be1530 specialized Collection.map
(:) + 4493
8 libswiftCore.dylib 0x00000001069cb640 Collection.map(:) + 18
9 SwiftCBOR 0x000000010694d750 CBORDecoder.readN(
:) + 256
10 SwiftCBOR 0x000000010694eca0 CBORDecoder.decodeItem() + 17163
11 SwiftCBORTests 0x0000000106911c30 implicit closure #3 in CBORDecoderTests.testDecodeArrays() + 167
12 SwiftCBORTests 0x00000001068fb480 thunk for @callee_owned () -> (@owned CBOR?, @error @owned Error) + 26
13 SwiftCBORTests 0x0000000106911da0 thunk for @callee_owned () -> (@owned CBOR?, @error @owned Error)partial apply + 83
14 libswiftXCTest.dylib 0x00000001074b70d0 specialized closure #1 in XCTAssertEqual(:::file:line:) + 113
15 libswiftXCTest.dylib 0x00000001074c6c50 partial apply for closure #1 in XCTAssertEqual
(:::file:line:) + 125
16 libswiftXCTest.dylib 0x00000001074c6c10 partial apply for closure #1 in XCTAssertEqual(:::file:line:) + 16
17 libswiftXCTest.dylib 0x00000001074c6510 partial apply for closure #1 in XCTRunThrowableBlock(:) + 59
18 libswiftXCTest.dylib 0x00000001074ad9e0 thunk for @callee_owned () -> () + 32
19 libswiftXCTest.dylib 0x00000001074c7250 XCTRunThrowableBlockBridge + 14
20 libswiftXCTest.dylib 0x00000001074b22c0 specialized XCTRunThrowableBlock(:) + 202
21 libswiftXCTest.dylib 0x00000001074b6430 specialized XCTAssertEqual
(:::file:line:) + 295
22 libswiftXCTest.dylib 0x00000001074ade50 XCTAssertEqual
(:::file:line:) + 43
23 SwiftCBORTests 0x0000000106911330 CBORDecoderTests.testDecodeArrays() + 647
24 SwiftCBORTests 0x0000000106912920 @objc CBORDecoderTests.testDecodeArrays() + 36
25 CoreFoundation 0x00007fff7a488b00 invoking_ + 140
26 CoreFoundation 0x00007fff7a4888f0 -[NSInvocation invoke] + 289
27 XCTest 0x000000010034f01d __24-[XCTestCase invokeTest]_block_invoke.272 + 50
28 XCTest 0x000000010039e0b9 -[XCTMemoryChecker _assertInvalidObjectsDeallocatedAfterScope:] + 37
29 XCTest 0x000000010034eb13 __24-[XCTestCase invokeTest]_block_invoke + 722
30 XCTest 0x000000010039754b -[XCUITestContext performInScope:] + 183
31 XCTest 0x000000010034ea7b -[XCTestCase invokeTest] + 141
32 XCTest 0x000000010034fc1c __26-[XCTestCase performTest:]_block_invoke.379 + 42
33 XCTest 0x000000010039ae6d +[XCTContext runInContextForTestCase:block:] + 163
34 XCTest 0x000000010034f382 -[XCTestCase performTest:] + 608
35 XCTest 0x000000010034b33c __27-[XCTestSuite performTest:]_block_invoke + 363
36 XCTest 0x000000010034adf4 -[XCTestSuite _performProtectedSectionForTest:testSection:] + 26
37 XCTest 0x000000010034af1c -[XCTestSuite performTest:] + 239
38 XCTest 0x000000010034b33c __27-[XCTestSuite performTest:]_block_invoke + 363
39 XCTest 0x000000010034adf4 -[XCTestSuite _performProtectedSectionForTest:testSection:] + 26
40 XCTest 0x000000010034af1c -[XCTestSuite performTest:] + 239
41 XCTest 0x000000010034b33c __27-[XCTestSuite performTest:]_block_invoke + 363
42 XCTest 0x000000010034adf4 -[XCTestSuite _performProtectedSectionForTest:testSection:] + 26
43 XCTest 0x000000010034af1c -[XCTestSuite performTest:] + 239
44 XCTest 0x00000001003a9b75 __44-[XCTTestRunSession runTestsAndReturnError:]_block_invoke + 40
45 XCTest 0x0000000100366105 -[XCTestObservationCenter _observeTestExecutionForBlock:] + 477
46 XCTest 0x00000001003a9922 -[XCTTestRunSession runTestsAndReturnError:] + 281
47 XCTest 0x000000010033af7b -[XCTestDriver runTestsAndReturnError:] + 314
48 XCTest 0x0000000100399f0f _XCTestMain + 833
49 xctest 0x00000001000023d2 + 9170
50 libdyld.dylib 0x00007fff8fc13234 start + 1

Error on encoding empty Array inside a Map

Hi, I am using array inside a generic map object: it works good for not empty array, but just in case of an empty array c inside a Map, it will be encoded by encodeMap() as [0x97, 0x99, 0x64] instead of [0x97, 0x99, 0x128].
Forcing in the definition the element type as "c": [String]() or "c": [Int]() has no effects.

I think it could be related to https://github.com/myfreeweb/SwiftCBOR/blob/97a3d94906db62899688b332e5eda8436bfd8530/Sources/SwiftCBOR/CBOREncoder.swift#L290
the following conditions are all true for an empty array.

(lldb) print (any is [UInt8])
(Bool) $R2 = (_value = 1)
(lldb) print (any is [String])
(Bool) $R4 = (_value = 1)
(lldb) print (any is [Int])
(Bool) $R6 = (_value = 1)
(lldb) print (any is [Any])
(Bool) $R14 = (_value = 1)

Do you have any suggestions?

Test example:

        let mapToAnyEmpty: [String: Any] = [
            "a": 1,
            "b": [2, 3],
            "c": []
        ]
        let encodedMapToAnyEmpty = try! CBOR.encodeMap(mapToAnyEmpty)
        XCTAssertEqual(encodedMapToAnyEmpty, [0xa2, 0x61, 0x61, 0x01, 0x61, 0x62, 0x82, 0x02, 0x03, 0x61, 0x63, 0x80])

Assert failure:

error: -[SwiftCBORTests.CBOREncoderTests testEncodeMaps] : XCTAssertEqual failed: ("[163, 97, 97, 1, 97, 98, 130, 2, 3, 97, 99, 64]") is not equal to ("[162, 97, 97, 1, 97, 98, 130, 2, 3, 97, 99, 128]")

iOS 16.1 UnsafeRawPointer is depreciated and decodeItem returns nil value

UnsafeRawPointer is depreciated:

note: use the 'withUnsafeBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope

and on iOS 16.1 devices readUInt returns incorrect value

private func readUInt<T: UnsignedInteger>(_ n: Int) throws -> T {
        return UnsafeRawPointer(Array(try istream.popBytes(n).reversed())).load(as: T.self)
}

Solution:
It could be potentially refactored by:

private func readUInt<T: UnsignedInteger>(_ n: Int) throws -> T {
        let array = Array(try istream.popBytes(n).reversed())
        return array.withUnsafeBytes { $0.load(as: T.self) }
}

Notice:
Probably all UnsafeRawPointer( occurrence should be replaced by .withUnsafeBytes method call

fatalError terminates application

I am using CodableCBORDecoder but the cbor contains possibly a tagged cbor.
Please use throw instead of fatalError. do .. catch cannot prevent crashing from fatalError

incorrect work with Codable

function checkCborCodable(testObject) return false when function checkJsonCodable(testObject) return true

let testObject: AnyRecursive = .object([
            "intValue": .number(10),
            "floatValue": .number(130.1),
            "stringValue": .string("name 1"),
            "boolValue": .bool(true),
            "nilValue": .null,
            "arrayValue": .array([.number(1000), .number(2000), .number(3000)]),
            "objectValue": .object(["intValue": .number(5100)])
        ])
    func checkCborCodable(expected: AnyRecursive) -> Bool {
        guard let data = try? CodableCBOREncoder().encode(expected) else {
            return false
        }
        guard let result = try? CodableCBORDecoder().decode(AnyRecursive.self, from: data) else {
            return false
        }
        return expected == result
    }

    func checkJsonCodable(expected: AnyRecursive) -> Bool {
        guard let data = try? JSONEncoder().encode(expected) else {
            return false
        }
        guard let result = try? JSONDecoder().decode(AnyRecursive.self, from: data) else {
            return false
        }
        return expected == result
    }


public enum AnyRecursive: Equatable {

    // MARK: - Cases

    case string(String)
    case number(Float)
    case object([String: AnyRecursive])
    case array([AnyRecursive])
    case bool(Bool)
    case null
}

extension AnyRecursive: Codable {

    // MARK: - Initialization

    public init(from decoder: Decoder) throws {

        let container = try decoder.singleValueContainer()

        if let object = try? container.decode([String: AnyRecursive].self) {
            self = .object(object)
        } else if let array = try? container.decode([AnyRecursive].self) {
            self = .array(array)
        } else if let string = try? container.decode(String.self) {
            self = .string(string)
        } else if let bool = try? container.decode(Bool.self) {
            self = .bool(bool)
        } else if let number = try? container.decode(Float.self) {
            self = .number(number)
        } else if container.decodeNil() {
            self = .null
        } else {
            throw DecodingError.dataCorrupted(
                .init(codingPath: decoder.codingPath,
                      debugDescription: "Invalid \(String(describing: AnyRecursive.self)) value.")
            )
        }
    }

    // MARK: - Functions

    public func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()

        switch self {
        case let .array(array):
            try container.encode(array)
        case let .object(object):
            try container.encode(object)
        case let .string(string):
            try container.encode(string)
        case let .number(number):
            try container.encode(number)
        case let .bool(bool):
            try container.encode(bool)
        case .null:
            try container.encodeNil()
        }
    }
}

Exclusive Access to Memory Issues

While running the unit tests I noticed that Swift 4 is complaining a lot because it tries to enforce exclusive access to memory.

After some testing it looks like this is mostly because of the compactness of decodeItem() and splitting the lines could fix this:

case 0x5b: return CBOR.byteString(Array(try istream.popBytes(Int(try readUInt(8) as UInt64))))
case 0x5b:
  let n = Int(try readUInt(8) as UInt64)
  n = return CBOR.byteString(Array(try istream.popBytes(n)))

Unable to compile for release to the AppStore

When trying to compile with the target device set to "Generic iOS Device" I get the compilation error:

"/CBOR/SwiftCBOR/CBOREncodable.swift:38:35: Integer literal '4294967295' overflows when stored into 'Int'"

Decoding Example

Hi,

Am new to swift / iOS development in general. I have been able to use the Encoding implementation of SwiftCBOR without much trouble. However I have not been able to get the decoding done.

I have reached the statement
let decode = try! CBOR.decode(responseCMD)

However, am not able to understand how to break that data down further.

It would be great if you can provide working example in the Decoding tests you have written / provide a simple example of how to do that.

Tag release for CocoaPods

Hi Greg,

I will be using SwiftCBOR in a framework that I am helping build. The package manager we have chosen is CocoaPods which unfortunately does not allow pulling in source dependencies in the podspec (this issue was also brought up in #7).

Since there have been a number of changes since 0.2.0, could you tag a new release?

Thanks,
Brian

Unclear documentation on how to decode Array of Map values.

With the following CBOR binary message (hex string):
82A102A361760C6175016174187BA103A3617662323361750261741901C8

Which correspond to the following JSON:

[
  {
    2: {
      "v": 12,
      "u": 1,
      "t": 123
    }
  },
  {
    3: {
      "v": "23",
      "u": 2,
      "t": 456
    }
  }
]

From the documentation it is not clear how to read it in an efficient and clean way. The way it is right now:

            cbor = try CBORDecoder.init(input: data).decodeItem()
            var index = 0
            // We loop over each item from the array.
            while let item = cbor![CBOR.init(integerLiteral: index)] {
                index = index + 1
                // Here I have to know in advance that on first item I will get it like that
                // let first = item[2]
                // and that the second one I will get it by doing
                // let second = item[3]
            }

As 2 and 3 are variable as well, which are 4 byte long, looping over each possible value is not a solution.

Any thoughts ?

All CBOR.Tag static constants need to be public?

When I try to access CBOR.Tag.uri from my code, I get this compile error from my Xcode:
'uri' is inaccessible due to 'internal' protection level.

Don't those static let statements need to have a public in front of them? I'm using Xcode 9.4.1.

Sample full statement looks like this:
CBOR.tagged(CBOR.Tag.uri, ...)

Or are these supposed to be used in a different way that I haven't wrapped my head around, yet?

Decoding an array using CodableCBORDecoder()

Hello, i'm trying to use the Decodable protocol to decode a cbor struct. The struct contain an optional array of integers. Just learning this (and swift) so i just have a very simple struct:

    public struct SimpleStruct: Codable {
        public let stringVal: String
        public let bytesVal: Data
        public let integer: Int
        public let booleanVal: Bool
        public let doubleVal: Double
        public let arrayVal: [Int]?
    }

I've written some "sanity" tests where i use the Encodable protocol to encode it, then using the Decodable protocol to decode that data. This works fine when there is no arrayVal.

See this code:

    func testSimpleStruct() {
        
        // Sanity 1
        let _ = try! CodableCBORDecoder().decode(SimpleStruct.self, from: CodableCBOREncoder().encode(SimpleStruct(stringVal: "foo", bytesVal: Data(hex: "aabbcc")!, integer: 4711, booleanVal: true, doubleVal: 3.14, arrayVal: nil)))

        // Sanity 2
        let _ = try! CodableCBORDecoder().decode(SimpleStruct.self, from: CodableCBOREncoder().encode(SimpleStruct(stringVal: "foo", bytesVal: Data(hex: "aabbcc")!, integer: 4711, booleanVal: true, doubleVal: 3.14, arrayVal: [])))
        
        // Sanity 3
        let _ = try! CodableCBORDecoder().decode(SimpleStruct.self, from: CodableCBOREncoder().encode(SimpleStruct(stringVal: "foo", bytesVal: Data(hex: "aabbcc")!, integer: 4711, booleanVal: true, doubleVal: 3.14, arrayVal: [1,2,3,-4711])))
    }

Both the sanity 1 and 2 test cases causes an exception.

Test Suite 'CborCoderTests' started at 2020-05-20 01:17:15.846
Test Case '-[CborCoderTests testSimpleStruct]' started.
Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.typeMismatch(Swift.Optional<Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "cannot decode nil for key: CodingKeys(stringValue: \"arrayVal\", intValue: nil)", underlyingError: nil)): file CborCoderTests.swift, line 54
2020-05-20 01:17:27.735169+0200 xctest[48020:1831561] Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.typeMismatch(Swift.Optional<Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "cannot decode nil for key: CodingKeys(stringValue: \"arrayVal\", intValue: nil)", underlyingError: nil)): file CborCoderTests.swift, line 54
Program ended with exit code: 9

I don't know whether this is a bug or if I simply misuse the codable protocol. Any suggestions?

Complete file if you want to reproduce:

import XCTest
import SwiftCBOR

extension Data {
    init?(hex: String) {
        let len = hex.count / 2
        var data = Data(capacity: len)
        for i in 0..<len {
            let j = hex.index(hex.startIndex, offsetBy: i*2)
            let k = hex.index(j, offsetBy: 2)
            let bytes = hex[j..<k]
            if var num = UInt8(bytes, radix: 16) {
                data.append(&num, count: 1)
            } else {
                return nil
            }
        }
        self = data
    }
}

extension Data {
    var hex: String {
        return reduce("") {$0 + String(format: "%02x", $1)}
    }
}

class CborCoderTests: XCTestCase {
    
    public struct SimpleStruct: Codable {
        public let stringVal: String
        public let bytesVal: Data
        public let integer: Int
        public let booleanVal: Bool
        public let doubleVal: Double
        public let arrayVal: [Int]?
    }
    
    func testSimpleStruct() {
        
        // Sanity 1
        let _ = try! CodableCBORDecoder().decode(SimpleStruct.self, from: CodableCBOREncoder().encode(SimpleStruct(stringVal: "foo", bytesVal: Data(hex: "aabbcc")!, integer: 4711, booleanVal: true, doubleVal: 3.14, arrayVal: nil)))

        // Sanity 2 - fail
        let _ = try! CodableCBORDecoder().decode(SimpleStruct.self, from: CodableCBOREncoder().encode(SimpleStruct(stringVal: "foo", bytesVal: Data(hex: "aabbcc")!, integer: 4711, booleanVal: true, doubleVal: 3.14, arrayVal: [])))
        
        // Sanity 3 - fail
        let _ = try! CodableCBORDecoder().decode(SimpleStruct.self, from: CodableCBOREncoder().encode(SimpleStruct(stringVal: "foo", bytesVal: Data(hex: "aabbcc")!, integer: 4711, booleanVal: true, doubleVal: 3.14, arrayVal: [1,2,3,-4711])))
        
        let tmp = SimpleStruct(stringVal: "foo", bytesVal: Data(hex: "aabbcc")!, integer: 4711, booleanVal: true, doubleVal: 3.14, arrayVal: [1,2,-3,4711])
        print(try! CodableCBOREncoder().encode(tmp).hex)
        
        // Definite map, no arrayVal
        let _ = try! CodableCBORDecoder().decode(SimpleStruct.self, from: Data(hex: "a56a626f6f6c65616e56616cf569737472696e6756616c63666f6f69646f75626c6556616cfb40091eb851eb851f67696e746567657219126768627974657356616c43aabbcc")!)
        
        // Using indefinite map, no arrayVal
        let _ = try! CodableCBORDecoder().decode(SimpleStruct.self, from: Data(hex: "bf6a626f6f6c65616e56616cf569737472696e6756616c63666f6f69646f75626c6556616cfb40091eb851eb851f67696e746567657219126768627974657356616c43aabbccff")!)
        
        // This works fine
        let _ = try! CBOR.decode(Array(Data(hex: "a669737472696e6756616c63666f6f6a626f6f6c65616e56616cf567696e746567657219126769646f75626c6556616cfb40091eb851eb851f68627974657356616c43aabbcc68617272617956616c84010222191267")!))
        
        // not this
        let _ = try! CodableCBORDecoder().decode(SimpleStruct.self, from: Data(hex: "a669737472696e6756616c63666f6f6a626f6f6c65616e56616cf567696e746567657219126769646f75626c6556616cfb40091eb851eb851f68627974657356616c43aabbcc68617272617956616c84010222191267")!)
        
    }
    
}

Parsing error with 0.4.2

Hi there,

I just upgraded my project from 0.3.0 to 0.4.2 and I got an error decoding the following sequence:

bf186564 67766572 18666a6f 45545166 73714c5a 33c11867 1a5685c1 8019012d 9f19015f 18cbf4ff 19012e9f f5ff1901 2f9f0af5 ff190130 9f020004 f4ff1901 319f01f4 ff190132 64495345 4f190133 68697365 6f2d3030 42ff>

The error message is:

Fatal error: unexpected enum case while switching on value of type '(CBOR, CBOR)'

Breaking Changes in 0.4.5

It looks like there were many changes implemented in 0.4.5, some of which were breaking changes preventing other dependencies from compiling because they no longer conform to CBOREncodable, for example. Can you please revert the release and re-release using a major version bump so that package managers don't accidentally pull in breaking changes? Thank you.

Issues encoding Int

Hello!

I've noticed a peculiar behavior when calling .encode() on Integers. Not sure if it's expected behavior.

My code:

//Start testing random
        for i in 0...100 {
            print("Number: \(i)")
            print("Hex: \(i.encode().hexa)")
            print("")
        }

And a sample of the output I'm getting is:

Number: 20
Hex: 14

Number: 21
Hex: 15

Number: 22
Hex: 16

Number: 23
Hex: 17

Number: 24
Hex: 1818

Number: 25
Hex: 1819

Number: 26
Hex: 181A

Number: 27
Hex: 181B

Number: 28
Hex: 181C

Number: 29
Hex: 181D

Hexa:

extension Collection where Iterator.Element == UInt8 {
    var data: Data {
        return Data(self)
    }
    var hexa: String {
        return map{ String(format: "%02X", $0) }.joined()
    }
}

Ordering of keys in encoded map.

Hello!

I using this example as reference:

public func encode() -> [UInt8] {
        let cborWrapper = CBOR(dictionaryLiteral:
            ("x", CBOR(integerLiteral: self.x)),
            ("y", CBOR(stringLiteral: self.y)))
        return cborWrapper.encode()
    }

With my code:

    public func encode() -> [UInt8] {
        let cborWrapper = CBOR(dictionaryLiteral:
                                (CBOR(integerLiteral: 0), CBOR(integerLiteral: 1001)),
                               (CBOR(integerLiteral: 1), CBOR(integerLiteral: self.some_int32)),
                               (CBOR(integerLiteral: 2), self.hasSome_int8() ? CBOR(integerLiteral: self.some_int8!) : CBOR(nilLiteral: ())),
                               (CBOR(integerLiteral: 3), CBOR(booleanLiteral: self.some_bool)),
                               (CBOR(integerLiteral: 4), self.hasSome_btyearray() ? CBOR.byteString(self.some_bytearray!) : CBOR(nilLiteral: ())),
                               (CBOR(integerLiteral: 5), self.hasSome_string() ? CBOR(stringLiteral: self.some_string!) : CBOR(nilLiteral: ()))
        )
        return cborWrapper.encode()
    }

The hex it generates is : A60441FF0566737472696E670208001903E901182003F5

My question is, would it be possible to get the keys (integerLiteral) ordered in ascending order?

Thank you very much and any advice would be appreciated.

No Cocoapod has been submitted for this repo

Hi guys,
Looks like really good work you've done here.
I've noticed the project comes with a podspec file but no Cocoapod has been submitted.

Is this something you'd be open to doing?
Cheers

CBOR decode crashes

When passing random data to CBOR.decode() the entire app just crashes with an EXC_BAD_ACCESS (code=2, address=0x16f603f60)

Is that intended? If so, how can I add a safety check?

Example:

import Foundation
import SwiftCBOR

@main
public struct CBORCrash {
    public static func main() throws {
        for _ in 1...50 {
            let length = Int.random(in: 1...1_000_000)
            let randomData = Data([UInt8](repeating: UInt8.random(in: 0...255), count: length))
            let result = try CBOR.decode([UInt8](randomData))
        }
    }
}

CBOR Decode error with special characters

Hey guys,

I have an issue as a french developer. I use special characters such as "é", "è", "ë" for example.
And other users from other countries write in other language which need to use special characters.

Unfortunately, CBOR.decode() catch an error, that we cannot parse this data.

242676622-6b9a72be-6d09-40e0-b768-4c1fad8cb8ae

This method decode() try to return decodeItem()

242627870-0db6783c-f40a-404a-a95c-13675891fc13

As you can see below, it tries to decodeUtf8 from Util class,

242627670-0f9404dd-2727-457e-b112-9f367811138c

decodingResult returns an error because decoder(UTF8) can't decode(&generator)

242627545-46cfd98d-fe30-4d58-8310-2d6cc01bbb4b

It may be a problem caused by UTF8 encoding type ?

How could we fix this and help people who might have the same issue ?

Thank you guys.

Crash when decoding optional UnkeyContainer

I had this issue in the app so I reproduced it on a small scale.

struct Test1: Codable {
    var param1Test1: [Test2]?
}

struct Test2: Codable {
    var parameter: String
}
let payload = Test1(
      param1Test1: [
         Test2(parameter: "Hello")
           ]
     )
        
let responseCBOR = try CodableCBOREncoder().encode(payload)
        
let response = try CodableCBORDecoder().decode(Test1.self, from: responseCBOR)

this will cause the app to crash on the force unwrap inside func decodeNil() throws -> Bool from extension _CBORDecoder.UnkeyedContainer: UnkeyedDecodingContainer, I added PR with the change I made.
#89

Issues decoding hex string

Hello,
I'm trying to start encoding/decoding CBOR messages and I'm running into issues trying to decode.

I have the following hex data - 9f1b00000164a8e830608b1905340069302e332e302d64657669302e332e302d64657665342e302e326f38363631393130333136343936343373383933353130313831313534323038323534376b4d393546415230324130387641584e5f322e33325f333333375f313530313038303103008719057608f6f6f6f6f6831905600701ff

which is not decoding correctly according to cbor.me. Here is my code. I guess I'm converting it wrong?
Can you provide some help? thank you

       let info3:[UInt8] = Array(Data(hex: "9f1b00000164a8e830608b1905340069302e332e302d64657669302e332e302d64657665342e302e326f38363631393130333136343936343373383933353130313831313534323038323534376b4d393546415230324130387641584e5f322e33325f333333375f313530313038303103008719057608f6f6f6f6f6831905600701ff"))
        
        
        let decoded = try! CBOR.decode(info3)
        print(decoded)

Please follow Semantic Versioning

It would be really appreciated if semantic version was followed in this repo. Both 0.4.5 and 0.4.6 included breaking changes and it is cumbersome to have to manually update this library and keep track of what it is doing instead of consuming what should have been non breaking changes.

Extracting/accessing tags from a cbor,

I am extracting a CWT - and would like to access the tagged elements; but fail to understand the right syntax for this.

        // CBOR decode this (COSE wrapper; RFC8392--CWT)
        let cbor : [UInt8] = decompressed.getBytes(at: 0, length: decompressedBytesWritten) ?? []
        // let cose = try! CBOR.decode(cbor)!
        let cose = try CBORDecoder(input: cbor).decodeItem()
        dump(cose);

which nicely yields below:

▿ Optional(SwiftCBOR.CBOR.tagged(SwiftCBOR.CBOR.Tag(rawValue: 18), SwiftCBOR.CBOR.array([SwiftCBOR.CBOR.byteString([162. _snipped_..1]), SwiftCBOR.CBOR.map([:]), SwiftCBOR.CBOR.byteString([161,... _snipped_.....49, 49, 65, 5])])))
  ▿ some: SwiftCBOR.CBOR.tagged
    ▿ tagged: (2 elements)
      ▿ .0: SwiftCBOR.CBOR.Tag
        - rawValue: 18
      ▿ .1: SwiftCBOR.CBOR.array
        ▿ array: 4 elements
          ▿ SwiftCBOR.CBOR.byteString
            ▿ byteString: 13 elements
              - 162 ..... _snipped_
     ▿ SwiftCBOR.CBOR.map
            - map: 0 key/value pairs
          ▿ SwiftCBOR.CBOR.byteString
            ▿ byteString: 9 elements
              - 161 ... _snipped_
          ▿ SwiftCBOR.CBOR.byteString
            ▿ byteString: 64 elements
              - 201 ..... _snipped_

What is the proper method for accessing specific tags ? (I had naively expect something such as 'someting as? tag)'.

Thanks !

CBOR decode as dictionary

Hi,
I'm trying to decode a CBOR encoded dictionary and getting result as CBOR object. Now how to decode that directly to map / dictionary ?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.