#xdr #codec #encode #rpc #onc

bin+lib fastxdr

使用快速、零拷贝反序列化从XDR规范生成Rust类型

3个稳定版本

1.0.2 2020年8月16日
1.0.1 2020年8月10日
1.0.0 2020年8月9日

解析实现中排名1883

BSD-3-Clause

175KB
6K SLoC

fastxdr

将XDR规范转换为Rust代码。

  • 生成具有快速、零拷贝反序列化的Rust类型
  • 为生成的类型自定义derives
  • 不会引发panic - 对于格式不正确的数据返回生成的Error变体
  • 作为build.rs的一部分使用,或者使用独立的二进制文件生成
  • 将XDR联合映射到Rust枚举1-to-1以方便使用
  • XDR typedefs生成不同的Rust类型(不是类型别名)
  • 符合rfc1014 / rfc1832 / rfc4506

包含opaque字节的类型是针对AsRef<[u8]>实现泛型的,并且所有类型都实现了TryFrom<Bytes>,以实现惯用的、零拷贝反序列化(见Bytes)。

速度

反序列化线协议非常快,通常在1微秒以下。

下面的示例是从生产网络捕获的NFS消息,具有协议的典型大小和复杂性

setclientid             time:   [719.93 ns 724.15 ns 728.76 ns]
mount                   time:   [661.15 ns 663.54 ns 665.85 ns]
lookup                  time:   [819.69 ns 823.80 ns 828.04 ns]

通过避免完全复制不透明字节的需求,即使是包含大量数据的XDR消息,在现代CPU上通常在1us或更少的时间内以O(n)时间和空间反序列化。

库功能

此crate可以用作库来实现自定义代码生成,或构建XDR linters等。

该库使用Pest crate对XDR规范进行分词,构建抽象语法树,索引和解析类型别名和常量,并生成代码来计算XDR序列化类型的链路大小——此信息通过indexast模块暴露给用户。

用法

生成的代码有依赖项,必须添加到您的Cargo.toml

[dependencies]
thiserror = "1.0"
bytes = "0.5"

然后,要么在构建脚本中生成代码(首选),要么手动使用CLI。

要查看生成的类型,您可以在应用程序中导出生成的类型并使用cargo doc,或者使用CLI直接生成可读的生成代码。

构建脚本

要将它作为build.rs构建脚本的一部分使用,首先将fastxdr添加到构建依赖项中

[build-dependencies]
fastxdr = "1.0"

然后在crate根目录下创建一个build.rs文件(不在src目录中)

fn main() {
    // Tell Cargo to regenerate the types if the XDR spec changes
    println!("cargo:rerun-if-changed=src/xdr_spec.x");

    // Read from xdr_spec.x, writing the generated code to out.rs
    std::fs::write(
        std::path::Path::new(std::env::var("OUT_DIR").unwrap().as_str()).join("out.rs"),
        fastxdr::Generator::default()
            .generate(include_str!("src/xdr_spec.x"))
            .unwrap(),
    )
    .unwrap();
}

并在您的应用程序中包含生成的内容

// Where out.rs is the filename from above
include!(concat!(env!("OUT_DIR"), "/out.rs"));
use xdr::*;

生成的内容位于名为xdr的模块中,如果需要,您可以将其重新导出。

CLI

您也可以使用CLI生成代码

cargo install fastxdr
fastxdr ./path/to/spec.x > generated.rs

如果规范被修改但代码未重新生成,或者规范未提交到源代码控制,这可能会造成混淆,通常使用build.rs脚本是最好的选择。

孤儿规则

由于孤儿规则,无法为定义在生成代码之外的类型实现TryFrom,例如u32等。这通常是正常的,除了相对不常见的包含原始类型的可变长度数组类型定义。

因此,任何对原始类型数组的类型定义都必须稍作修改——这不是一个破坏性更改,也不会影响序列化的网络格式

typedef uint32_t        bitmap4<>;

由于孤儿规则阻止向u32类型定义添加TryFrom实现,因此请将其包裹在类型定义中以生成新类型

typedef uint32_t        bitmap_inner;
typedef bitmap_inner    bitmap4<>;

现在数组包含了一个可以为其实现TryFrombitmap_inner类型。

依赖项

~3MB
~62K SLoC