A binary codec library for efficient encoding and decoding of Rust data structures.
⚠️ Early Development: This library is in active development and the API may change.
- 🚀 Derive macros for automatic codec implementation (
Encode,Decode,Measure) - 🔢 Primitive types with custom byte ordering (BE/LE) and variable-length encoding
- 📦 Collections support:
Vec, arrays, slices with configurable length prefixes - 🔤 String handling: UTF-8 strings, C strings (
CStr), and byte slices - 💾 Zero-copy decoding with borrowed data (
'encodedlifetime) - 🎯 Type-safe enums with discriminant encoding
- 🔧 Flexible attribute-based customization with inline codec syntax
- 🎨 Nested structures including boxed types for recursive data
Add to your Cargo.toml:
[dependencies]
byten = "0.0"use byten::{DefaultCodec, DecodeOwned, Encode, Measure, EncodeToVec as _};
#[derive(Debug, DefaultCodec, Encode, Measure, DecodeOwned, PartialEq)]
struct Person {
#[byten($be)]
id: u32,
age: u8,
#[byten($bytes[u8] $utf8 $own)]
name: String,
}
fn main() {
let person = Person {
id: 12345,
age: 30,
name: "Alice".to_string(),
};
// Encode to Vec
let encoded = person.encode_to_vec().unwrap();
// Decode from slice
let mut offset = 0;
let decoded = Person::decode(&encoded, &mut offset).unwrap();
assert_eq!(person, decoded);
}use byten::{DefaultCodec, Decode, Encode, Measure, EncodeToVec as _};
use std::ffi::CStr;
#[derive(Debug, DefaultCodec, Encode, Decode, Measure)]
pub struct Person<'encoded> {
pub first_name: &'encoded CStr,
pub last_name: &'encoded CStr,
#[byten($bytes[u16 $be] $utf8)]
pub address: &'encoded str,
#[byten($bytes[u32 $uvarbe])]
pub avatar_image: &'encoded [u8],
#[byten(.. $utf8)]
pub extra_data: &'encoded str,
}use byten::{DefaultCodec, DecodeOwned, Encode, Measure};
#[derive(Debug, DefaultCodec, DecodeOwned, Encode, Measure, PartialEq)]
#[repr(u16)]
#[byten($le)]
enum Color {
Red = 1,
Green = 2,
Blue = 3,
Grayscale(#[byten($be)] u16) = 4,
RGBa {
red: u8,
green: u8,
blue: u8,
#[byten($be)]
alpha: u16,
} = 5,
Gradient(Box<Color>, Box<Color>) = 6,
}use byten::{DefaultCodec, DecodeOwned, Encode, Measure};
use std::ffi::CString;
#[derive(Debug, DefaultCodec, Encode, Measure, DecodeOwned)]
pub struct Directory {
#[byten(CStr $own)]
pub name: CString,
#[byten(Entry box for[u16 $be])]
pub entries: Vec<Box<Entry>>,
}
#[derive(Debug, DefaultCodec, Encode, Measure, DecodeOwned)]
pub struct File {
#[byten(CStr $own)]
pub name: CString,
#[byten($bytes[u16 $be] $own)]
pub content: Vec<u8>,
#[byten(u32 $be ?)]
pub assigned_application_id: Option<u32>,
}
#[derive(Debug, DefaultCodec, Encode, Measure, DecodeOwned)]
#[repr(u8)]
pub enum Entry {
File(File) = 1,
Directory(Directory) = 2,
}use byten::{byten, Decoder, EncoderToVec as _};
fn main() {
// Define codec inline without derive macros
let codec = byten!( $bytes[u32 $be] $utf8 );
let original_str = "Hello, Byten!";
let encoded = codec.encode_to_vec(original_str).unwrap();
let decoded_str = codec.decode(&encoded, &mut 0).unwrap();
}The #[byten(...)] attribute supports a flexible syntax for customizing encoding:
- Endianness:
$be(big-endian),$le(little-endian) - Variable-length:
$uvarbe(variable-length unsigned, big-endian) - Collections:
T for[Length],T [] - Bytes:
$bytes[Length]for raw byte slices - Strings:
$utf8for UTF-8 strings,CStrfor C strings - Ownership:
$ownto decode into owned data (e.g.,String,Vec) - Optional:
?forOption<T>types with presence byte - Boxing:
boxforBox<T>types - Phantom:
= exprfor constant values with zero bytes - Remaining:
..to consume rest of input - Tuples:
(a, b, ...N)as built-in codecs for N sized tuples - Custom:
{ expr }for custom codec expressions
std(default): Enable standard library support. Disable forno_stdenvironments withdefault-features = falsealloc(default viastd): Enable types that require allocation (Vec,Box,String). Can be used inno_stdwith an allocator.anyhow(default): Integration with theanyhowerror handling crate (requiresstd)derive(default): Enable derive macros for self-coded traits. Works in all modes (core-only, alloc, std).
For embedded systems without an allocator, use only core types:
[dependencies]
byten = { version = "0.0", default-features = false }This provides support for:
- Primitive types (
u8,i8,bool, etc.) - Arrays and slices
- Borrowed data (
&str,&[u8],&CStr) - Endian conversion
- Fixed-size types
With derive macros:
[dependencies]
byten = { version = "0.0", default-features = false, features = ["derive"] }Note: When using derive macros in core-only mode, avoid using Vec, Box, String, or other allocation-dependent types in your structs.
For no_std environments with an allocator:
[dependencies]
byten = { version = "0.0", default-features = false, features = ["alloc"] }This adds support for:
Vec<T>collectionsBox<T>heap allocation- Owned strings (
String) - Variable-length encoding (
UVarBECodec)
With derive macros (recommended for most use cases):
[dependencies]
byten = { version = "0.0", default-features = false, features = ["alloc", "derive"] }Note: The derive feature works in all modes (core-only, alloc, and std). The generated code will only use features that are enabled.
The byten/examples directory contains several complete examples:
array.rs: Encoding arrays with variable-length integersborrowed.rs: Zero-copy decoding with borrowed data and lifetimesarchive.rs: Recursive structures (file system directory tree)icmp.rs: Network packet encoding (ICMP header)nostd.rs: Simple FS structures forno_stdenvironmentsinline.rs: Using the inlinebyten!()macro for ad-hoc codecs
Run examples with:
cargo run --example borrowedLicensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contributions are welcome! Please read our Contributing Guidelines for details on how to submit pull requests, report issues, and contribute to the project.
This project adheres to the Contributor Covenant Code of Conduct. By participating, you are expected to uphold this code.