Binary serialization in Rust just got a sovereign, modern foundation.
Today we released OxiCode 0.1.0 — a compact, fast binary encoder/decoder for Rust, built from the ground up as the spiritual successor to bincode while keeping 100% binary compatibility with it.
No C. No Fortran. No Python pickle. No language-specific serialization runtime.
Just a clean, memory-safe, zero-fluff binary codec that compiles to a single static binary (or WASM) and runs everywhere Rust runs.
Why OxiCode 0.1.0 matters
For years, the practical choice for compact binary serialization in Rust was bincode. It is a fine library and the community owes it a lot — but the ecosystem has moved on, and a few things have nagged for a long time:
- No first-class zero-copy decoding path
- No built-in SIMD, compression, or integrity-checking story
- Limited schema-evolution and streaming support
- A maintenance pace that doesn’t always match downstream needs
OxiCode 0.1.0 is our answer. The core goal for this first release was simple and strict: be a drop-in replacement for bincode 2.0 that you can adopt without a flag day. Data encoded with bincode decodes with OxiCode, and vice versa — verified by 18/18 byte-for-byte binary-compatibility tests.
// Before (bincode 2.0)
let bytes = bincode::encode_to_vec(&value, config::standard())?;
// After (OxiCode) — same shape, same bytes
let bytes = oxicode::encode_to_vec(&value)?;
On top of that baseline, 0.1.0 already ships the features we wanted bincode to have, behind opt-in flags so the default build stays tiny.
Technical Deep Dive: a layered, no_std-first codec
The workspace is intentionally small and honest for a first release — a core crate plus a derive crate plus a compatibility test crate:
-
Core layer (
oxicode)
Varint and fixed-width integer encoding, theEncode/Decodetraits, a flexibleconfigsystem (config::standard()for little-endian + varint,config::legacy()for bincode-1.0-compatible fixed-int), configurable size limits, and comprehensive typed errors. 112+ types implementEncode/Decodeout of the box — every integer and float,bool,char,Option,Result,Box,Rc,Arc,Cow,PhantomData, the full standard collection family (Vec,HashMap,BTreeMap,VecDeque,LinkedList,BinaryHeap, …),NonZero*, tuples up to 16, and arrays of any size. -
Derive layer (
oxicode_derive)
#[derive(Encode, Decode)]generates the trait impls for your own structs and enums, with informative compile-time errors. -
no_std from the start
default = ["std", "derive"], but the core works underno_std. Turn on justallocfor heap types without pulling in fullstd— embedded and WASM targets are first-class, not an afterthought. -
Opt-in enhancements (the “150% beyond bincode” set)
- SIMD (
features = ["simd"]): auto-detects SSE2 / AVX2 / AVX-512 and accelerates largei32/u32/i64/u64/f32/f64arrays (2–4× on supported CPUs), with a scalar fallback everywhere else. - Compression (
compression-lz4,compression-zstd):CompressedEncoder/CompressedDecoderfor network and storage payloads. - Schema evolution: a
Versiontype, embedded version tags, compatibility checking, andVersionedEncoder/VersionedDecoder. - Streaming: chunked sync encoders/decoders, plus async via
async-tokio. - Validation middleware: constraint-based
Validator<T>(MaxLength,Range,NonEmpty,AsciiOnly, …) for decoding untrusted data safely.
- SIMD (
A non-negotiable throughout: zero unwrap() in the codebase. Every fallible path returns a typed error you can match on.
Getting Started
cargo add oxicode
A minimal encode/decode round-trip with the derive macros:
use oxicode::{Encode, Decode};
#[derive(Encode, Decode, PartialEq, Debug)]
struct Point {
x: f32,
y: f32,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let point = Point { x: 1.0, y: 2.0 };
// Encode to bytes
let encoded = oxicode::encode_to_vec(&point)?;
// Decode from bytes
let (decoded, _): (Point, _) = oxicode::decode_from_slice(&encoded)?;
assert_eq!(point, decoded);
Ok(())
}
Prefer to keep your existing serde derives? Enable the optional serde feature and serialize through it:
let encoded = oxicode::serde::encode_to_vec(&person, oxicode::config::standard())?;
let (decoded, _): (Person, _) =
oxicode::serde::decode_from_slice(&encoded, oxicode::config::standard())?;
What’s inside
- 100% bincode 2.0 binary compatibility — mix libraries freely during migration; 18/18 compatibility tests passing
- 112+ types with
Encode/Decode, including all primitives, the standard collections, and special types - Derive macros for
EncodeandDecode - Flexible
configsystem —standard(),legacy(), and a builder for endianness / int-encoding / size limits - no_std +
allocsupport for embedded and WASM - Optional SIMD array encoding (2–4× speedup on supported CPUs)
- Optional LZ4 and Zstd compression
- Schema versioning & migration, sync + async streaming, validation middleware
- Strict no-unwrap policy, comprehensive typed errors, configurable size limits to resist DoS
- 10,860 lines of pure Rust across 61 files, 211 tests passing, zero compiler/clippy warnings, every file under 2,000 lines
Tips
- Migrating from bincode? Swap the crate and keep your byte streams. Where you need the classic fixed-int wire format, use
config::legacy()instead ofconfig::standard()so the bytes match bincode 1.0 exactly. - Targeting embedded or WASM? Drop
default-featuresand enable onlyalloc(andderiveif you want the macros). The core codec works withoutstd. - Bulk numeric data? Turn on
features = ["simd"]— largef32/f64/integer arrays get the SSE2/AVX2/AVX-512 fast path automatically, with a transparent scalar fallback. - Sending over the wire? Enable
compression-lz4(fast) orcompression-zstd(smaller) and reach forCompressedEncoder/CompressedDecoder. - Decoding untrusted input? Set a size limit on your
configand wrap decoded values in aValidator<T>withMaxLength/Range/NonEmptyconstraints. - Planning for change? Use
VersionedEncoder/VersionedDecodernow so future schema changes stay backward-compatible.
This is the foundation
OxiCode is meant to be a quiet, dependable layer that other Pure Rust projects can serialize against — simulation state for Spintronics, tensors for TenRSo and TensorLogic, audio buffers for VoiRS, and numeric arrays alongside OxiBLAS. As the COOLJAPAN stack grows, OxiCode aims to be the binary codec underneath it.
Repository: https://github.com/cool-japan/oxicode
Star the repo if you want a modern, drop-in successor to bincode without adding a single C dependency.
Pure Rust binary serialization is here — fast, compatible, and sovereign.
— KitaSan at COOLJAPAN OÜ December 28, 2025