#bson #serialization #mongo-db #serde

rawbson

闪电般的零拷贝 BSON 处理

4 个版本

0.2.1 2021 年 1 月 22 日
0.2.0 2021 年 1 月 17 日
0.1.1 2021 年 1 月 13 日
0.1.0 2021 年 1 月 10 日

#912编码

MIT 许可证

3MB
3K SLoC

rawbson

version license

rawbson 提供了对 BSON 数据的零拷贝操作。

用法

可以从包含原始 BSON 数据的 Vec<u8> 创建一个 rawbson 文档,并通过与 bson-rust crate 中类似的方法访问元素。请注意,rawbson 返回一个 Result<Option>,因为文档中的字节只有在尝试访问包含的数据时才进行完全验证。

use rawbson::{
    DocBuf,
    elem,
};

// \x16\x00\x00\x00                   // total document size
// \x02                               // 0x02 = type String
// hello\x00                          // field name
// \x06\x00\x00\x00world\x00          // field value
// \x00

let doc = DocBuf::new(b"\x16\x00\x00\x00\x02hello\x00\x06\x00\x00\x00world\x00\x00".to_vec())?;
let elem: Option<elem::Element> = doc.get("hello")?;
assert_eq!(
    elem.unwrap().as_str()?,
    "world",
);
# Ok::<(), rawbson::RawError>(())

bson-rust 互操作

该 crate 设计为与 bson crate 顺利互操作。

可以从 DocBuf 创建一个 bson::document::Document。内部,将 Document 序列化为一个 Vec<u8>,然后将这些字节包含在 DocBuf 中。

use bson::doc;
use rawbson::{
    DocBuf,
};

let document = doc!{"goodbye": {"cruel": "world"}};
let raw = DocBuf::from_document(&document);
let value: Option<&str> = raw.get_document("goodbye")?
    .map(|docref| docref.get_str("cruel"))
    .transpose()?
    .flatten();

assert_eq!(
    value,
    Some("world"),
);
# Ok::<(), rawbson::RawError>(())

引用类型

也可以使用 [Doc] 引用类型访问 BSON 文档,这是一个无大小类型,它将 BSON 有效负载表示为 [u8]。这允许在不重新分配的情况下访问嵌套文档。[Doc] 必须始终通过指针类型访问,类似于 [T]str

此类型将至少与已弃用的 [DocRef] 类型共存一个次要版本。

以下示例在一个基于堆栈的数组中构建一个 bson 文档,并从中提取一个 &str,不进行堆分配。

use rawbson::Doc;

let bytes = b"\x13\x00\x00\x00\x02hi\x00\x06\x00\x00\x00y'all\x00\x00";
assert_eq!(Doc::new(bytes)?.get_str("hi")?, Some("y'all"));
# Ok::<(), rawbson::RawError>(())

迭代

《Doc》实现了IntoIterator,也可以通过DocBuf::iter或已弃用的DocRef::into_iter访问。

use bson::doc;
use rawbson::{DocBuf, elem::Element};

let doc = DocBuf::from_document(&doc! {"crate": "rawbson", "license": "MIT"});
let mut dociter = doc.iter();

let (key, value): (&str, Element) = dociter.next().unwrap()?;
assert_eq!(key, "crate");
assert_eq!(value.as_str()?, "rawbson");

let (key, value): (&str, Element) = dociter.next().unwrap()?;
assert_eq!(key, "license");
assert_eq!(value.as_str()?, "MIT");
# Ok::<(), rawbson::RawError>(())

serde支持

还提供了serde反序列化支持。

目前尚未提供serde序列化支持。现在,请使用bson::to_document代替,然后使用bson::Document::to_writerDocBuf::from_document进行序列化。

use serde::Deserialize;
use bson::{doc, Document, oid::ObjectId, DateTime};
use rawbson::{DocBuf, de::from_docbuf};

#[derive(Deserialize)]
#[serde(rename_all="camelCase")]
struct User {
    #[serde(rename = "_id")]
    id: ObjectId,
    first_name: String,
    last_name: String,
    birthdate: Option<chrono::DateTime<chrono::Utc>>,
    #[serde(flatten)]
    extra: Document,
}

let doc = DocBuf::from_document(&doc!{
    "_id": ObjectId::with_string("543254325432543254325432")?,
    "firstName": "John",
    "lastName": "Doe",
    "birthdate": null,
    "luckyNumbers": [3, 60, 2147483647],
    "nickname": "Red",
});

let user: User = from_docbuf(&doc)?;
assert_eq!(user.id.to_hex(), "543254325432543254325432");
assert_eq!(user.first_name, "John");
assert_eq!(user.last_name, "Doe");
assert_eq!(user.extra.get_str("nickname")?, "Red");
assert!(user.birthdate.is_none());
# Ok::<(), Box<dyn std::error::Error>>(())

性能

待办:用更严格的分析替换此部分。

由于rawbson不需要解析BSON负载或为文档中的每个元素分配空间,因此访问文档中的单个元素和按顺序遍历元素比使用bson::Document类型要快得多。

将原始字节反序列化到自定义类型也比使用bson crate提供的反序列化方法要快得多,因为所有这些方法都是首先将它们反序列化到解析后的Bson类型。

另一方面,由于找到特定键需要从文档的开始遍历,创建具有O(1)元素访问的解析bson::Document,当在文档中反复访问随机元素时,访问速度会更快。

此crate提供了一个criterion基准测试套件来支持这些断言。在我的Thinkpad X1 Carbon (Gen 5)上运行这些基准测试的输出可以在./criterion-report目录中找到。

欢迎提出改进这些基准测试质量的建议。

依赖项

~6.5MB
~122K SLoC