Expand description
§Byteable
A Rust crate for convenient serialization and deserialization of byte-oriented data.
byteable provides traits and utilities for seamless conversion between data structures
and byte arrays, handling both synchronous and asynchronous I/O operations, and managing
endianness.
§Features
ByteableTrait: The core trait for types that can be converted to and from a byte array.ReadByteable&WriteByteableTraits: Extension traits forstd::io::Readandstd::io::Write, enabling convenient reading and writing ofByteabletypes.AsyncReadByteable&AsyncWriteByteableTraits (withtokiofeature): Asynchronous counterparts toReadByteableandWriteByteable, designed for use withtokio’s async I/O.EndianConvertTrait & Wrappers: Provides methods for converting primitive types between different endianness (little-endian and big-endian), along withBigEndian<T>andLittleEndian<T>wrapper types.#[derive(UnsafeByteable)](withderivefeature): A procedural macro that automatically implements theByteabletrait for structs, significantly simplifying boilerplate.
§Quick Start
Add byteable to your Cargo.toml:
[dependencies]
byteable = { version = "*", features = ["derive"] }§Basic Usage
The core workflow involves:
- Defining a struct with explicit memory layout (
#[repr(C, packed)]) - Deriving or implementing the
Byteabletrait - Using extension traits for reading/writing
§Example: File I/O
use byteable::{Byteable, LittleEndian, ReadByteable, WriteByteable};
use std::fs::File;
#[derive(Debug, PartialEq)]
#[derive(byteable::UnsafeByteable)]
#[repr(C, packed)]
struct Packet {
id: u8,
length: LittleEndian<u16>,
data: [u8; 4],
}
// Create a packet
let packet = Packet {
id: 42,
length: 1024.into(),
data: [0xDE, 0xAD, 0xBE, 0xEF],
};
// Write to file
let mut file = File::create("packet.bin")?;
file.write_byteable(packet)?;
// Read from file
let mut file = File::open("packet.bin")?;
let restored: Packet = file.read_byteable()?;
assert_eq!(packet, restored);§Example: Network Protocol
use byteable::{Byteable, BigEndian, LittleEndian};
use byteable::UnsafeByteable;
#[derive(UnsafeByteable, Debug, Clone, Copy)]
#[repr(C, packed)]
struct NetworkHeader {
magic: BigEndian<u32>, // Network byte order (big-endian)
version: u8,
flags: u8,
payload_len: LittleEndian<u16>, // Little-endian for payload
}§Example: Working with TCP Streams
use byteable::{ReadByteable, WriteByteable, Byteable};
use std::net::TcpStream;
#[derive(byteable::UnsafeByteable, Debug)]
#[repr(C, packed)]
struct Message {
msg_type: u8,
data: [u8; 16],
}
let mut stream = TcpStream::connect("127.0.0.1:8080")?;
// Write message
let msg = Message {
msg_type: 1,
data: [0; 16],
};
stream.write_byteable(msg)?;
// Read response
let response: Message = stream.read_byteable()?;§Async I/O (with tokio feature)
Enable async support in your Cargo.toml:
[dependencies]
byteable = { version = "*", features = ["derive", "tokio"] }
tokio = { version = "1", features = ["full"] }Example usage:
use byteable::{AsyncReadByteable, AsyncWriteByteable, Byteable};
use tokio::net::TcpStream;
#[derive(byteable::UnsafeByteable, Debug)]
#[repr(C, packed)]
struct AsyncPacket {
id: u32,
data: [u8; 8],
}
let mut stream = TcpStream::connect("127.0.0.1:8080").await?;
let packet = AsyncPacket {
id: 123,
data: [1, 2, 3, 4, 5, 6, 7, 8],
};
stream.write_byteable(packet).await?;
let response: AsyncPacket = stream.read_byteable().await?;§Endianness Handling
The crate provides BigEndian<T> and LittleEndian<T> wrappers for handling byte order:
use byteable::{BigEndian, LittleEndian};
// Create endian-aware values
let big = BigEndian::new(0x12345678u32);
let little = LittleEndian::new(0x12345678u32);
// Get raw bytes (in specified endianness)
assert_eq!(big.raw_bytes(), [0x12, 0x34, 0x56, 0x78]);
assert_eq!(little.raw_bytes(), [0x78, 0x56, 0x34, 0x12]);
// Convert back to native value
assert_eq!(big.get(), 0x12345678u32);
assert_eq!(little.get(), 0x12345678u32);§Safety Considerations
The #[derive(UnsafeByteable)] macro uses std::mem::transmute internally, which is unsafe.
When using this macro, ensure that:
- Your struct has
#[repr(C, packed)]or another explicit layout - All fields in the struct implement
Byteable - The struct doesn’t contain padding bytes with undefined values
- Reading arbitrary bytes into your struct won’t violate invariants
For types with complex invariants (like String, Vec, references, etc.), do not use
UnsafeByteable. Use only with plain old data (POD) types.
§Advanced Usage
§Custom Byteable Implementation
You can implement Byteable manually for types that need special handling:
use byteable::{Byteable, impl_byteable_via};
use byteable::{LittleEndian, UnsafeByteable};
// Raw representation suitable for byte conversion
#[derive(UnsafeByteable)]
#[repr(C, packed)]
struct PointRaw {
x: LittleEndian<i32>,
y: LittleEndian<i32>,
}
// User-friendly representation
#[derive(Debug, PartialEq)]
struct Point {
x: i32,
y: i32,
}
impl From<Point> for PointRaw {
fn from(p: Point) -> Self {
Self {
x: p.x.into(),
y: p.y.into(),
}
}
}
impl From<PointRaw> for Point {
fn from(raw: PointRaw) -> Self {
Self {
x: raw.x.get(),
y: raw.y.get(),
}
}
}
// Implement Byteable via the raw type
impl_byteable_via!(Point => PointRaw);§Feature Flags
derive: Enables the#[derive(UnsafeByteable)]procedural macro (default: enabled)tokio: Enables async I/O traits for use with tokio (default: disabled)
§Performance
This crate is designed for zero-copy, zero-overhead serialization. Operations like
as_byte_array and from_byte_array typically compile down to simple memory operations
or even no-ops when possible.
Macros§
- impl_
byteable_ via - Implements
Byteablefor a type by delegating to another type. - unsafe_
byteable_ transmute - Implements
Byteablefor one or more types usingtransmute.
Structs§
- BigEndian
- A wrapper type that stores a value in big-endian (network) byte order.
- Little
Endian - A wrapper type that stores a value in little-endian byte order.
Traits§
- Byte
Array - A trait for types that can be used as byte array representations.
- Byteable
- A trait for types that can be converted to and from a byte array.
- Endian
Convert - A trait for types that can be converted between different byte orders (endianness).
- Read
Byteable - Extension trait for
Readthat adds methods for readingByteabletypes. - Write
Byteable - Extension trait for
Writethat adds methods for writingByteabletypes.
Derive Macros§
- Unsafe
Byteable - Derives the
Byteabletrait for a struct usingtransmute.