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.1"
The library only depends on serde and requires Rust 1.87 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:
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 ;
Matrices serialize as { layout, extents, value } maps for easy consumption by other languages.
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: