beve-rs
Rust implementation of the BEVE (Binary Efficient Versatile Encoding) specification with serde support. The crate targets cross-language interoperability, predictable layout, and zero-copy fast paths for scientific and analytics workloads.
Getting Started
Grab the crate from crates.io and add it to your project with cargo add beve or by editing Cargo.toml:
[]
= "0.6"
The library only depends on serde and requires Rust 1.88 or newer. Half-precision floats via half::f16 are supported alongside the standard numeric types.
Encode & Decode with Serde
Use beve::to_vec and beve::from_slice for idiomatic serde round-trips:
use ;
You can stream to files or sockets with beve::to_writer and read everything back using beve::from_reader:
For hot paths that reuse a Vec<u8>, encode directly into an existing buffer:
let mut buf = Vecwith_capacity;
to_vec_into?;
Zero-Copy Deserialization
from_slice supports borrowing directly from the input buffer for string types. Structs with &str fields avoid allocation entirely — the deserialized strings point straight into the BEVE byte slice:
use Deserialize;
This works for &str fields, Vec<&str>, BTreeMap<&str, V> keys, and &[u8] fields (with #[serde(borrow)]). BEVE typed u8 arrays are contiguous bytes with alignment 1, so &[u8] borrows directly from the buffer without copying. Zero-copy borrowing is not available for wider numeric arrays (e.g. &[f64]) since BEVE does not guarantee alignment.
from_reader continues to require DeserializeOwned since it reads into an internal buffer that cannot outlive the call.
Validate Without Deserializing
Use validate_slice or validate_reader when you only need to check that input is valid BEVE, without parsing into a Rust type.
Validation is strict: the payload must contain exactly one well-formed BEVE value with no trailing bytes.
use Cursor;
JSON Interoperability
Convert between JSON payloads and BEVE without allocating an intermediate serde_json::Value. These helpers stream bytes on both sides, so large documents never build an in-memory tree and typed arrays stay in their native BEVE representation.
let json = r#"{"name":"delta","values":[1,2,3]}"#;
let beve_bytes = json_str_to_beve?;
let json_back = beve_slice_to_json_string?;
assert_eq!;
JSON arrays are always encoded as BEVE generic arrays (we do not attempt to detect homogeneous typed arrays), which avoids backtracking mid-stream. Non-finite floating-point literals (NaN, Infinity) are rejected because standard JSON cannot express them.
Dynamic Value Type
When you need to deserialize BEVE data without knowing the schema at compile time, use beve::Value:
use Value;
Value supports all BEVE types: Null, Bool, Number, String, Array, and Object. Numbers preserve their original representation (signed, unsigned, or float) at full precision (up to 128-bit integers).
Converting Value to Concrete Types
Once you have a Value, convert it directly to a concrete type without re-encoding:
use Deserialize;
use ;
Use from_value_ref when you need to keep the original Value around:
use from_value_ref;
Object Keys
BEVE objects support string and integer keys. The Key enum handles both:
use ;
use BTreeMap;
Typed Array Fast Paths
BEVE bakes in typed arrays for numeric, boolean, and string sequences. Skip serde overhead by calling the dedicated helpers:
let floats = ;
let bytes = to_vec_typed_slice;
let flags = ;
let packed = to_vec_bool_slice;
let names = ;
let encoded = to_vec_str_slice;
The resulting payloads match serde output, so beve::from_slice::<Vec<T>> continues to work.
Typed Arrays Inside Structs
Struct fields automatically use the packed typed-array fast paths, so you get compact encodings without custom serializers:
use ;
Integer Map Keys
Maps with integer keys serialize deterministically and read back into ordered maps:
use BTreeMap;
Complex Numbers and Matrices
The crate exposes helpers for common scientific types:
use ;
Matrix and MatrixOwned<T> use the BEVE matrix extension for supported element types (bool, numeric scalars, and Complex<f32/f64>). For unsupported element types, serialization falls back to a { layout, extents, value } map.
MATLAB / .mat Export
Enable the optional mat feature to convert BEVE payloads directly into MATLAB v7.3 MAT files:
[]
= { = "0.6", = ["mat"] }
The MAT feature uses a pure-Rust HDF5 writer (hdf5-pure) and requires no system libraries.
Use RootBinding::NamedVariable when one BEVE value should become one MATLAB variable, or RootBinding::WorkspaceObject when a string-keyed BEVE object should expand into multiple top-level workspace variables:
use ;
For in-memory conversion (useful in WASM or when you already have the bytes), use beve_slice_to_mat_v73_bytes:
let mat_bytes = beve_slice_to_mat_v73_bytes?;
Current mappings:
- numeric, logical, and complex scalars/arrays
- UTF-8 strings and typed string arrays as MATLAB
stringobjects - generic BEVE arrays as MATLAB cell arrays
- string-keyed BEVE objects as MATLAB structs
- BEVE matrix extensions, including row-major to column-major reorder when needed
nullasstruct([])by default
Important limits:
- only MATLAB v7.3 is supported
- non-string object keys cannot map to MATLAB structs/workspace variables
i128,u128,bf16, andf16require explicit fallback policies when MATLAB has no direct native representation- the MATIO-based oracle used in tests does not decode MATLAB
stringobjects semantically, so string coverage is validated structurally against MATLAB-generated fixtures
Examples
cargo run --example emit_boolwrites a short boolean stream to stdout so you can inspect the raw bytes.cargo run --example emit_colordemonstrates encoding a struct with enums and typed arrays.
Enum Configuration
By default enums emit numeric discriminants for compatibility with the reference C++ encoder. Switch to string variants when coordinating with serde-first consumers:
use Serialize;
use ;
let opts = SerializerOptions ;
let bytes = to_vec_with_options?;
Supported Data Model
- Scalars: signed/unsigned integers up to 128-bit, f32/f64, null, bool, and UTF-8 strings
- Collections: typed arrays (numeric, bool, string), generic sequences, maps with string or integer keys, and nested structs/enums
- Streaming:
to_writer,to_writer_with_options, andfrom_readerfor IO-bound workflows - Interop: payloads align with
reference/glazeandreference/BEVE.jl; spec resides inreference/beve/README.mdand the upstream BEVE specification
Half & BFloat16 Scalars
Half-precision (f16) and bfloat16 (bf16) values round-trip like any other scalar:
use ;
Checking Your Work
Run the usual cargo commands before sending a change: