Byteable
A Rust crate for zero-overhead, zero-copy serialization and deserialization of byte-oriented data.
byteable provides traits and utilities for seamless conversion between data structures and byte arrays, with full support for both synchronous and asynchronous I/O operations, and comprehensive endianness handling.
Features
- Byte Conversion Traits: Modular trait system for byte array conversion:
ByteRepr: Associates a type with its byte array representationIntoByteArray: Converts values into byte arraysFromByteArray: Constructs values from byte arraysTryFromByteArray: Fallible deserialization for types that can fail (e.g.,bool,char, enums)
ReadValue&WriteValue: Extension traits forstd::io::Readandstd::io::WriteAsyncReadValue&AsyncWriteValue: Async I/O support with tokio (optional)- Endianness Support:
BigEndian<T>andLittleEndian<T>wrappers for explicit byte order #[derive(Byteable)]: Procedural macro for automatic trait implementation with endianness support (optional):- Fixed-size structs via zero-copy transmute
#[byteable(io_only)]structs for types containingVec,String,Option, etc.- C-like enums and enums with variant fields
- Standard Collection I/O: Built-in
Readable/WritableforVec,String,Option,HashMap,BTreeMap, and more - Zero Overhead: Fixed-size types compile down to simple memory operations with no runtime cost
Why byteable?
- Binary Protocols: Perfect for implementing network protocols (TCP, UDP, custom formats)
- File I/O: Read/write binary file formats with ease
- Cross-Platform: Consistent behavior across different architectures with endianness control
- Type-Safe: Rust's type system ensures correctness at compile time
- No Dependencies: Core functionality has zero dependencies (tokio is optional)
Installation
Add byteable to your Cargo.toml:
[]
= "0.25" # Or latest version
Optional Features
[]
= { = "0.25", = ["derive", "tokio"] }
derive(default): Enables the#[derive(Byteable)]procedural macrotokio: Enables async I/O traits for use with tokio
Quick Start
Basic File I/O Example
use ;
use File;
Network Protocol Example
use Byteable;
let header = TcpHeader ;
// Convert to bytes for transmission
let bytes = header.into_byte_array;
Async I/O with Tokio
use ;
use TcpStream;
async
Primitive Type Support
bool and char
The crate provides safe support for bool and char types with proper validation via TryFromByteArray. These types have restricted valid byte patterns and will return errors for invalid values.
Boolean Support
use ;
// Valid boolean values
let value = true;
let bytes = value.into_byte_array;
assert_eq!;
let value = false;
let bytes = value.into_byte_array;
assert_eq!;
// Roundtrip conversion
let restored = booltry_from_byte_array.unwrap;
assert_eq!;
// Invalid byte values return errors
let result = booltry_from_byte_array;
assert!; // Only 0 and 1 are valid
Character Support
Rust's char type represents a Unicode scalar value (code points U+0000 to U+10FFFF, excluding surrogates). Characters are stored as little-endian 32-bit integers.
use ;
// ASCII character
let ch = 'A';
let bytes = ch.into_byte_array;
assert_eq!; // Little-endian U+0041
// Unicode emoji
let ch = '🦀';
let bytes = ch.into_byte_array;
assert_eq!; // Little-endian U+1F980
// Roundtrip conversion
let restored = chartry_from_byte_array.unwrap;
assert_eq!;
// Invalid code points return errors
let result = chartry_from_byte_array;
assert!; // Not a valid Unicode scalar value
Using bool and char in Structs
use ;
Important Notes:
- Use
TryFromByteArrayinstead ofFromByteArrayfor types containingboolorchar boolonly accepts0(false) or1(true)charvalidates against Unicode scalar values (excludes surrogates and values > U+10FFFF)- Characters are always stored as little-endian 32-bit values
Enum Support
The #[derive(Byteable)] macro supports two kinds of enums: C-like enums (unit variants only, with fixed-size byte array conversion) and field enums (variants with data, using stream-based I/O).
C-Like Enums
C-like enums (unit variants with explicit discriminants) implement IntoByteArray / TryFromByteArray for zero-copy fixed-size conversion.
use ;
// Required: explicit repr type
Enums with non-sequential discriminants are fully supported:
use Byteable;
// Only the defined discriminants are valid; all others return errors
assert_eq!;
assert!;
Enums with Fields
Enums with variant fields (named or tuple) implement Readable / Writable for stream-based I/O. The discriminant is written first, followed by the variant's fields in order.
use ;
use Cursor;
let original = Data ;
let mut buf = Vecnew;
buf.write_value.unwrap;
assert_eq!; // discriminant + fields
let decoded: Message = new.read_value.unwrap;
assert_eq!;
Discriminants and fields both support endianness annotations:
use Byteable;
// Little-endian u16 discriminant
// Individual fields can have per-field endianness
If #[repr] is omitted, the macro infers the smallest integer type that fits all variants (e.g. u8 for up to 255 variants), and discriminants auto-increment from 0 like ordinary Rust enums.
Enum Endianness (C-like)
C-like enums also support type-level endianness for their fixed-size representation:
use Byteable;
let bytes = Binary.into_byte_array;
assert_eq!; // little-endian, platform-independent
io_only Structs
The standard #[derive(Byteable)] path uses transmute-based zero-copy conversion and requires every field to be a fixed-size, TransmuteSafe type. For structs that contain Vec<T>, String, Option<T>, or other dynamically-sized types, annotate the struct with #[byteable(io_only)] to generate sequential field I/O instead:
use ;
use Cursor;
let original = Packet ;
let mut buf = Vecnew;
buf.write_value.unwrap;
let decoded: Packet = new.read_value.unwrap;
assert_eq!;
io_only structs implement Readable / Writable (not IntoByteArray / FromByteArray), so they always require a reader or writer. Fields are written in declaration order. Field-level endianness attributes still apply:
Tuple structs and unit structs are also supported with #[byteable(io_only)].
Standard Collection I/O
The Readable and Writable traits are implemented for common standard library collection types. All collections are serialized as a little-endian u64 length (number of elements) followed by each element in sequence.
| Type | Notes |
|---|---|
Vec<T> |
Sequential elements |
VecDeque<T> |
Sequential elements |
LinkedList<T> |
Sequential elements |
HashMap<K, V> |
Sequential key-value pairs |
HashSet<T> |
Sequential elements |
BTreeMap<K, V> |
Sequential key-value pairs |
BTreeSet<T> |
Sequential elements |
Option<T> |
0u8 for None, 1u8 + value for Some |
Result<V, E> |
0u8 + value for Ok, 1u8 + error for Err |
String |
UTF-8 bytes prefixed by a little-endian u64 byte-length |
Path / PathBuf |
Same encoding as String (UTF-8 path) |
CStr / CString |
Null-terminated bytes |
These implementations are used automatically by ReadValue::read_value / WriteValue::write_value and are composed transparently within io_only structs and field enums.
Usage Patterns
Working with Different Endianness
use Byteable;
Reading Multiple Values
use ReadValue;
use Cursor;
let data = vec!;
let mut reader = new;
let header: u32 = reader.read_value?;
let length: u16 = reader.read_value?;
let checksum: u32 = reader.read_value?;
Safety Considerations
#[derive(Byteable)] uses two distinct code-generation paths with different safety profiles:
Transmute path (default)
Used for ordinary structs and C-like enums. Internally uses core::mem::transmute, so every field must be a fixed-size, TransmuteSafe type.
Safe to use:
- Primitive numeric types (
u8,i32,f64, etc.) boolandchar(with validation viaTryFromByteArray)BigEndian<T>andLittleEndian<T>wrappers- Arrays of the above
- C-like enums with explicit discriminants
Never use on the transmute path:
String,Vec, or any heap-allocated types — use#[byteable(io_only)]instead- References or pointers (
&T,Box<T>,*const T) - Types with
Dropimplementations NonZero*types or types with invariants
io_only / field-enum path
Used for #[byteable(io_only)] structs and enums with variant fields. No transmute is involved — values are read/written field by field via the Readable/Writable traits. Standard library collection types (Vec, String, Option, HashMap, etc.) are fully supported on this path.
Documentation
The crate includes extensive documentation:
- API Documentation: Every trait, type, and function is documented with examples
- Inline Comments: All implementations include explanatory comments
- Safety Guidelines: Clear warnings about unsafe usage
- Examples: Multiple real-world usage examples in the
examples/directory
Generate and view the documentation locally:
See Also
License
This project is licensed under the MIT License - see the LICENSE file for details.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Acknowledgments
Built with ❤️ for the Rust community.