一个用于 Swift 的 CBOR 实现
Swift 中的 CBOR (RFC 7049 Concise Binary Object Representation,精简二进制对象表示) 解码器和编码器。可以直接从 Swift 类型进行编码,或使用包装器对象。解码为 CBOR 值类型,可以使用原生 Swift 下标进行访问,并使用等效的字面量表示。
- 一个完全跨平台的 Swift 5.x 包!
- 支持
Codable
! - 负整数被解码为
NegativeInt(UInt)
,实际数字为-1 - i
(CBOR 的负整数可以大于 64 位有符号整数)。 - 标签被解码,但不被处理。带标签的值被编码,但不进行类型检查。自己处理吧 :-)
- 字面量可转换类型已为
CBOR
类型定义! - 还有
subscript
。因此您可以像这样访问 CBOR 映射和数组:myDecodedObject["numbers"][1]
。 - 如果您想从流中解码,请在您的流上实现
CBORInputStream
协议,并像这样创建解码器:CBORDecoder(stream: yourStream)
。 - 半精度浮点数可以解码为 Float,甚至可能正确。不支持编码 Float16(Swift 中不存在)。
- 编码的内存效率需要调整。(编码通常不是就地完成的。)
- 支持编码不定长度数据,但您需要显式地将开始和结束信息添加到您的流数据中。
- 推荐使用 cbor.me 来查看您的 CBOR 编码数据。
有很多方法:Swift Package Manager、CocoaPods、git submodule...
CocoaPod 由贡献者提交,更新可能会有所延迟。
Swift Package Manager 是推荐的依赖管理器。
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")])
要解包解码后的 CBOR
值,请使用 模式匹配!!
编码一个值会返回一个字节数组,[UInt8]
。您可以使用 CBOR.encode(myValue)
或 myValue.encode()
进行编码。任何符合 CBOREncodable
协议的类型都可以被编码。您可以为您的类型实现 CBOREncodable
协议,然后像往常一样进行编码。
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)
由于 Swift 不完整的泛型系统,您不能调用 someArray.encode()
或 someDictionary.encode()
,但只要您的数组项或映射的键和值类型符合 CBOREncodable
,您就可以简单地使用 CBOR.encode(someArrayOrMap)
。
在某些情况下,可能需要在编码之前创建一个 CBOR
中间表示。例如,如果您想编码包含异构类型的数组或字典(这在类似 JSON 的对象中很常见),您还不能使用原生 Swift 映射。 您可以在您的类型上实现 CBOREncodable
,这将构建一个 CBOR
值并对其进行编码,或者在没有 CBOREncodable
的情况下进行 CBOR
值处理。
CBOR
枚举可以 使用字面量表示,但请注意变量不是字面量,因此您可能需要手动调用构造函数。
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
encode
函数不必一定是那样的。如果您想做一些自定义的事情,比如保持映射键的顺序,您可以手动构建 [UInt8]
。可以参考 Encoder 函数 获取灵感。
当前的通用 API 如下所示。当您需要对要编码的类型进行细粒度控制时,请使用以下方法。
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]
要编码不定长度的数组、映射、字符串和字节字符串,请显式使用 open- 和 close-stream CBOR 值。在这两个值之间,使用编码后的数组和映射块,通过 CBOR.encodeArrayChunk
和 CBOR.encodeMapChunk
。 不定长度的字符串和字节字符串可以像正常编码一样编码(即,它们不需要自己的“块”函数)。
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]
最后,关于在使用通用数组编码器 CBOR.encode(..)
时编码字节字符串的技术说明。 如果函数参数 asByteString
为真,那么除了 UInt8 之外的所有类型的数组,如果计算机是小端字节序(CBOR 使用大端字节序或网络字节序),则每个项目的原始字节将被反转(但不会反转项目本身的顺序)。 UInt8 的数组被认为已经处于网络字节序。
通过参与此项目,您同意遵守贡献者行为准则。
This is free and unencumbered software released into the public domain. For more information, please refer to the UNLICENSE
file or unlicense.org.