Crates.io | fastxdr |
lib.rs | fastxdr |
version | 1.0.2 |
source | src |
created_at | 2020-08-09 21:04:46.197765 |
updated_at | 2020-08-16 19:13:40.424663 |
description | Generate Rust types from XDR specs with fast, zero-copy deserialisation |
homepage | |
repository | https://github.com/domodwyer/fastxdr |
max_upload_size | |
id | 274797 |
size | 181,953 |
Transpiles XDR specifications into Rust code.
Error
variants for malformed databuild.rs
or generate with a standalone binaryrfc1014
/ rfc1832
/ rfc4506
Types containing opaque
bytes are generic over AsRef<[u8]>
implementations,
and all types have TryFrom<Bytes>
implemented for idiomatic, zero-copy
deserialisation (see Bytes
).
Deserialising the wire protocol is very fast, usually under 1 microsecond.
The examples below are NFS messages captured from a production network, of typical size and complexity for the protocol:
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]
By avoiding the need to copy opaque bytes entirely, even XDR messages containing large amounts of data typically deserialise in 1us or less on a modern CPU in O(n) time and space.
This crate can be used as a library to implement custom code generation, or build XDR linters, etc.
The library tokenises the XDR specs using the Pest crate, constructs an
abstract syntax tree, indexes and resolves type aliases and constants and
generates code to calculate the on-wire size of the XDR serialised types - this
information is exposed to users through the index
and ast
modules.
The generated code has dependencies that must be added to your Cargo.toml
:
[dependencies]
thiserror = "1.0"
bytes = "0.5"
Then either generate the code as part of a build script (preferred), or manually using the CLI.
To view the generated types, either export the generated types in your
application and use cargo doc
, or use the CLI to produce the generated code
directly for reading.
To use it as part of a build.rs
build script, first add fastxdr
to the build
dependencies:
[build-dependencies]
fastxdr = "1.0"
Then create a build.rs
file at the crate root (not in 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();
}
And include the generated content somewhere in your application:
// Where out.rs is the filename from above
include!(concat!(env!("OUT_DIR"), "/out.rs"));
use xdr::*;
The generated content is within a module named xdr
which you may choose to
re-export if needed.
You can also generate the code with the CLI:
cargo install fastxdr
fastxdr ./path/to/spec.x > generated.rs
This can get confusing if the spec is modified and the code is not regenerated,
or the spec is not checked into source control and typically a build.rs
script
is the best way to go.
Because of the orphan rule it is not possible to implement TryFrom
for types
defined outside of generated code such as u32
, etc. This is normally fine,
except for relatively uncommon typedefs of variable length arrays containing
primitive types.
Therefore any typedefs to arrays of primitive types must be modified slightly - this is not a breaking change and does not affect the serialised on-wire format:
typedef uint32_t bitmap4<>;
As the orphan rule prevents a TryFrom
implementation being added to the u32
typedef target, wrap it in a typedef to generate a new type:
typedef uint32_t bitmap_inner;
typedef bitmap_inner bitmap4<>;
The array now contains the bitmap_inner
type that can have TryFrom
implemented for it.