Go 语言中的 CBOR 编解码器
fxamacker/cbor 是一个用于编码和解码 CBOR 和 CBOR 序列 的库。
CBOR 是 JSON、MessagePack、Protocol Buffers 等的可信替代品。CBOR 是由 IETF STD 94 (RFC 8949) 定义的互联网标准,设计目标是在未来数十年内保持相关性。
fxamacker/cbor
被 Arm Ltd.、Cisco、EdgeX Foundry、Flow Foundation、Fraunhofer-AISEC、Kubernetes、Let's Encrypt (ISRG)、Linux Foundation、Microsoft、Mozilla、Oasis Protocol、Tailscale、Teleport 等项目使用。
请参阅快速开始和发布版本。🆕 UnmarshalFirst
和 DiagnoseFirst
可以解码 CBOR 序列。cbor.MarshalToBuffer()
和 UserBufferEncMode
接受用户指定的缓冲区。
fxamacker/cbor
fxamacker/cbor
是一个完全符合 IETF STD 94 (RFC 8949) 的 CBOR 编解码器。它还支持 CBOR 序列(RFC 8742)和扩展诊断表示法(RFC 8610 的附录 G)。
特性包括对 CBOR 标签的全面支持、核心确定性编码、重复映射键检测等。
设计平衡了安全性、速度、并发性、编码数据大小、可用性等方面的权衡。
具有可配置设置的安全解码
fxamacker/cbor
具有可配置的限制等,可以防御恶意 CBOR 数据。
相比之下,encoding/gob
不设计用于对抗性输入的加固。
安全解码与可配置设置
fxamacker/cbor
具有可配置的限制等,可以防御恶意 CBOR 数据。
相比之下,encoding/gob
不设计用于对抗性输入的加固。
fxamacker/cbor
在拒绝格式错误的 CBOR 数据方面非常快速。例如,尝试将 10 字节的恶意 CBOR 数据解码为 []byte
(使用默认设置):
编解码器 | 速度 (ns/op) | 内存 | 分配次数 |
---|---|---|---|
fxamacker/cbor 2.5.0 | 44 ± 5% | 32 B/op | 2 allocs/op |
ugorji/go 1.2.11 | 5353261 ± 4% | 67111321 B/op | 13 allocs/op |
最新比较使用: |
- 输入:
[]byte{0x9B, 0x00, 0x00, 0x42, 0xFA, 0x42, 0xFA, 0x42, 0xFA, 0x42}
- go1.19.10, linux/amd64, i5-13600K (禁用所有 e-cores, DDR4 @2933)
- go test -bench=. -benchmem -count=20
先前的比较
编解码器 | 速度 (ns/op) | 内存 | 分配次数 |
---|---|---|---|
fxamacker/cbor 2.5.0-beta2 | 44.33 ± 2% | 32 B/op | 2 allocs/op |
fxamacker/cbor 0.1.0 - 2.4.0 | ~44.68 ± 6% | 32 B/op | 2 allocs/op |
ugorji/go 1.2.10 | 5524792.50 ± 3% | 67110491 B/op | 12 allocs/op |
ugorji/go 1.1.0 - 1.2.6 | 💥 运行时: | 内存不足: | 无法分配 |
- 输入:
[]byte{0x9B, 0x00, 0x00, 0x42, 0xFA, 0x42, 0xFA, 0x42, 0xFA, 0x42}
- go1.19.6, linux/amd64, i5-13600K (DDR4)
- go test -bench=. -benchmem -count=20
使用结构体标签实现更小的编码
结构体标签(toarray
, keyasint
, omitempty
)可以减少结构体的编码大小。
示例:将3层嵌套的Go结构体编码为1字节CBOR
https://go.dev/play/p/YxwvfPdFQG2
// 示例:编码嵌套结构体(使用omitempty标签)
// - encoding/json: 18字节JSON
// - fxamacker/cbor: 1字节CBOR
package main
import (
"encoding/hex"
"encoding/json"
"fmt"
"github.com/fxamacker/cbor/v2"
)
type GrandChild struct {
Quux int `json:",omitempty"`
}
type Child struct {
Baz int `json:",omitempty"`
Qux GrandChild `json:",omitempty"`
}
type Parent struct {
Foo Child `json:",omitempty"`
Bar int `json:",omitempty"`
}
func cb() {
results, _ := cbor.Marshal(Parent{})
fmt.Println("hex(CBOR): " + hex.EncodeToString(results))
text, _ := cbor.Diagnose(results) // 诊断表示法
fmt.Println("DN: " + text)
}
func js() {
results, _ := json.Marshal(Parent{})
fmt.Println("hex(JSON): " + hex.EncodeToString(results))
text := string(results) // JSON
fmt.Println("JSON: " + text)
}
func main() {
cb()
fmt.Println("-------------")
js()
}
输出结果(DN是诊断表示法):
hex(CBOR): a0
DN: {}
-------------
hex(JSON): 7b22466f6f223a7b22517578223a7b7d7d7d
JSON: {"Foo":{"Qux":{}}}
使用多个结构体标签的示例
结构体标签简化了需要使用整数键的CBOR数组或映射的CBOR协议的使用。
CBOR标签
CBOR标签在TagSet
中指定。
可以使用TagSet
创建自定义模式来处理CBOR标签。
em, err := opts.EncMode() // 无CBOR标签
em, err := opts.EncModeWithTags(ts) // 不可变的CBOR标签
em, err := opts.EncModeWithSharedTags(ts) // 可变的共享CBOR标签
TagSet
和使用它的模式对于并发使用是安全的。DecMode
也有等效的API。
使用TagSet和TagOptions的示例
// 使用"解码CWT"示例中定义的signedCWT结构体。
// 创建TagSet(并发安全)。
tags := cbor.NewTagSet()
// 注册标签COSE_Sign1 18和signedCWT类型。
tags.Add(
cbor.TagOptions{EncTag: cbor.EncTagRequired, DecTag: cbor.DecTagRequired},
reflect.TypeOf(signedCWT{}),
18)
// 创建带有不可变标签的DecMode。
dm, _ := cbor.DecOptions{}.DecModeWithTags(tags)
// 解组到带标签支持的signedCWT。
var v signedCWT
if err := dm.Unmarshal(data, &v); err != nil {
return err
}
// 创建带有不可变标签的EncMode。
em, _ := cbor.EncOptions{}.EncModeWithTags(tags)
// 使用标签号组织signedCWT。
if data, err := cbor.Marshal(v); err != nil {
return err
}
函数和接口
函数和接口一览
与encoding/json
具有相同API的常用函数:
Marshal
,Unmarshal
NewEncoder
,(*Encoder).Encode
NewDecoder
,(*Decoder).Decode
注意:如果有剩余字节,Unmarshal
将返回ExtraneousDataError
,因为RFC 8949将有剩余字节的CBOR数据项视为格式错误。
- 💡 使用
UnmarshalFirst
解码第一个CBOR数据项并返回任何剩余字节。
其他有用的函数:
Diagnose
,DiagnoseFirst
从CBOR数据生成人类可读的扩展诊断表示法。UnmarshalFirst
解码第一个CBOR数据项并返回任何剩余字节。Wellformed
如果CBOR数据项格式良好则返回true。
与Go encoding
包相同或类似的接口包括:
Marshaler
, Unmarshaler
, BinaryMarshaler
, 和 BinaryUnmarshaler
。
RawMessage
类型可用于延迟CBOR解码或预先计算CBOR编码。
安全提示
🔒 解码非常大或不定大小的数据时,使用Go的io.LimitReader
来限制大小。
对于处理非常大的数据(例如区块链)的系统,可能需要增加默认限制。
可以使用DecOptions
修改MaxArrayElements
、MaxMapPairs
和MaxNestedLevels
的默认限制。
状态
v2.7.0 (2024年6月23日)增加了一些功能和改进,有助于大型项目(如Kubernetes)将CBOR用作JSON和Protocol Buffers的替代方案。其他改进包括速度提升、改进的内存使用、错误修复、新的序列化选项等。它通过了模糊测试(超过50亿次执行)并且具有生产质量。
有关更多详细信息,请参阅发行说明。
先前版本
v2.6.0 (2024年2月)增加了重要的新功能、优化和错误修复。它对需要在CBOR和JSON之间转换数据的系统特别有用。新的选项和优化改进了对大整数、整数、映射和字符串的处理。
v2.5.0于2023年8月13日(星期日)发布,具有新功能和重要的错误修复。经过长期的beta测试 v2.5.0-beta (2022年12月) -> v2.5.0 (2023年8月),它已经经过模糊测试并具有生产质量。
重要: 👉 从v2.4或更早版本升级之前,请阅读发行说明中强调的显著变化。v2.5.0是一个大版本,对Unmarshal
中额外数据的错误处理等进行了错误修复,在升级之前应该进行审查。
有关新功能、改进和错误修复的列表,请参阅v2.5.0发行说明。
有关版本编号等更多信息,请参阅"版本和API更改"部分。