Skip to content

victorstewart/rustery

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Rustery

Rustery is a Rust implementation of the Bitsery serialization format.

The goal is wire-format compatibility with C++ Bitsery, exposed through a Rust-native API: typed encoders/decoders, borrowed zero-copy views, and generic container/extension support where the Bitsery model maps cleanly to Rust.

Status

Rustery is pinned to this Bitsery target:

  • Repository: https://github.com/victorstewart/bitsery.git
  • Branch: zero-copy-read
  • Revision: 93dc81684f4e7a8ee8c4087876d5417bae6968ca
  • Typed-wire reader: bitsery/typed_wire.h, bitsery::tw::makeTypedWireView

The pin is also recorded in Cargo.toml under package.metadata.bitsery.

Features

  • Bitsery-compatible little-endian scalar payloads.
  • Bitsery variable-length size prefixes.
  • Strings, bytes, arrays, Rust containers, maps/sets, options, tuples, tagged unions, fixed bitsets, compact integers, ranges, entropy defaults, and growable sections.
  • Bit packing with alignment validation.
  • C++ interop tests against the pinned Bitsery branch.
  • #[derive(BitseryView)] borrowed views for Rust zero-copy reads, including nested views, scalar arrays, and generic view parameters.

Capability coverage is tracked in audits/bitsery_capability_matrix.md.

Bitsery Parity

Rustery is production-ready for the documented Rust-native Bitsery payload API, typed-wire encoding, borrowed views, and covered extension set. It does not yet claim byte-for-byte feature parity for every C++ Bitsery surface.

Known parity gaps:

  • Stream adapters: buffer/cursor adapters are implemented; streaming I/O adapters are pending.
  • Big-endian configuration: current payload support is little-endian.
  • Low-level data operations: core bit packing and alignment are implemented; broader C++ adapter operation parity is still being expanded.
  • Pointer and identity graphs: direct C++ pointer/RTTI/inheritance semantics do not map one-to-one to Rust. Rustery will model these through explicit IDs, tagged unions, and ownership-safe schema APIs instead of raw pointer cloning.

Encoding And Decoding

use rustery::{Result, TypedWireDecode, TypedWireEncode, Writer, Reader};

struct Ping {
    version: u32,
    nonce: u64,
}

impl TypedWireEncode for Ping {
    fn encode_typed_wire(&self, writer: &mut Writer) -> Result<()> {
        writer.write_u32(self.version);
        writer.write_u64(self.nonce);
        Ok(())
    }
}

impl<'a> TypedWireDecode<'a> for Ping {
    fn decode_typed_wire(reader: &mut Reader<'a>) -> Result<Self> {
        Ok(Self {
            version: reader.read_u32()?,
            nonce: reader.read_u64()?,
        })
    }
}

let bytes = Ping { version: 1, nonce: 42 }.to_typed_wire()?;
let ping = Ping::from_typed_wire(&bytes)?;

Zero-Copy Views

Rust does not have C++26-style reflection, so Rustery uses compile-time derive generation instead. Safe views validate the Bitsery payload once, copy scalar values, and borrow dynamic fields directly from the original buffer.

use rustery::{BitseryView, Result, TypedWireEncode, Writer};

#[derive(BitseryView)]
struct Message {
    id: u32,
    title: String,
    payload: Vec<u8>,
}

impl TypedWireEncode for Message {
    fn encode_typed_wire(&self, writer: &mut Writer) -> Result<()> {
        writer.write_u32(self.id);
        writer.write_str(&self.title)?;
        writer.write_sized_bytes(&self.payload)
    }
}

let bytes = Message {
    id: 7,
    title: "hello".to_string(),
    payload: vec![1, 2, 3],
}.to_typed_wire()?;

let view = MessageView::new(&bytes)?;
assert_eq!(view.id(), 7);
assert_eq!(view.title(), "hello");      // borrowed &'a str
assert_eq!(view.payload(), &[1, 2, 3]); // borrowed &'a [u8]

This is intentionally not a &Message transmute. Bitsery wire layout is not Rust struct layout.

The derive supports concrete named structs, nested derived structs, Vec<u8> as borrowed bytes, scalar vectors, fixed scalar arrays, and generic parameters that represent nested view types. Generated views also expose unsafe new_trusted for payloads that are already known to be valid; that path skips validation such as UTF-8 checks and is the closest apples-to-apples match for Bitsery's C++ typed byte views.

Benchmarks

benches/typed_wire_perf.rs mirrors the pinned Bitsery branch's active tests/typed_wire_perf.cpp cases: application-state, activity, versioned-state, static-sample, dynamic-sample, and kitchen-sink.

Latest local comparison artifact: benchmarks/typed_wire_perf_exact_comparison_latest.tsv.

Ratio columns below are Bitsery time / Rustery time; values above 1.0x mean Rustery was faster in this local run.

case bytes fresh/no-hint serialize size-hint serialize reusable serialize owned decode typed read safe view trusted view
application-state 1 1.52x 2.57x 4.49x 1.98x 1.92x 1.93x 1.93x
activity 24 0.24x 1.68x 1.29x 1.34x 1.84x 1.67x 1.68x
versioned-state 8 1.29x 1.86x 1.57x 2.24x 1.60x 1.60x 1.60x
static-sample 40 0.35x 1.66x 1.27x 1.89x 1.66x 1.26x 1.76x
dynamic-sample 236 0.30x 1.66x 1.35x 1.22x 1.44x 0.64x 1.44x
kitchen-sink 418 0.59x 1.17x 0.62x 1.05x 3.16x 1.35x 2.30x

Benchmark Environment

These are local microbenchmark results, not a broad claim that Rustery is always faster than Bitsery.

  • Host CPU: AMD Ryzen Threadripper PRO 9965WX 24-Cores
  • OS/kernel: Linux 6.19.10-1-cachyos-server
  • Rust: rustc 1.94.1, LLVM 21.1.8
  • C++: g++-16 16.0.1 20260425, -O3 -DNDEBUG -std=c++26 -freflection
  • Bitsery revision: 93dc81684f4e7a8ee8c4087876d5417bae6968ca
  • Rounds: 21
  • CPU affinity: taskset -c 0

Transparent reading of the results:

  • Rustery typed reads and owned decodes were faster in all six mirrored cases on this host.
  • Rustery trusted derived views were faster than Bitsery typed reads in all six cases on this host.
  • Rustery safe derived views were faster than Bitsery typed reads in five of six cases. dynamic-sample is slower because safe Rust views validate UTF-8 while Bitsery's typed view exposes text as bytes.
  • Rustery size-hinted serialization was faster in all six cases.
  • Exact fresh/no-hint serialization was not uniformly faster; Rustery was slower in four of six cases.
  • Reusable serialization was not uniformly faster; Rustery was slower on kitchen-sink.

Raw samples and environment metadata are checked in under benchmarks/. Regenerate them on the publication host before using the numbers as headline claims.

Verification

cargo test --workspace --jobs $(nproc)
cargo fmt --all --check
cargo clippy --workspace --all-targets -- -D warnings
cargo doc --workspace --no-deps --jobs $(nproc)
cargo package --workspace
cargo bench --bench typed_wire_perf -- \
  --rounds 21 \
  --out benchmarks/rustery_typed_wire_perf_latest.tsv \
  --samples-out benchmarks/rustery_typed_wire_perf_samples_latest.tsv

C++ interop tests run automatically when Bitsery headers are available at /root/bitsery/include, or when RUSTERY_BITSERY_INCLUDE points to them. RUSTERY_GXX16 and RUSTERY_CLANGXX can override compiler paths.

License

Rustery is licensed under the Apache License, Version 2.0.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors