3 stable releases
1.0.2 | Aug 16, 2020 |
---|---|
1.0.1 | Aug 10, 2020 |
1.0.0 | Aug 9, 2020 |
#1970 in Parser implementations
175KB
6K
SLoC
fastxdr
Transpiles XDR specifications into Rust code.
- Generates Rust types with fast, zero-copy deserialisation
- Customisable derives for generated types
- No panicking - returns generated
Error
variants for malformed data - Use as part of a
build.rs
or generate with a standalone binary - XDR unions mapped to Rust enums 1-to-1 for convince
- XDR typedefs produce distinct Rust types (not type aliases)
- Complies with
rfc1014
/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
).
Speed
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.
Library functionality
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.
Usage
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.
Build Script
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.
CLI
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.
Orphan Rule
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.
Dependencies
~3MB
~60K SLoC