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
- Byte Conversion Traits: A modular trait system for byte array conversion:
AssociatedByteArray: Associates a type with its byte array representationIntoByteArray: Converts values into byte arraysFromByteArray: Constructs values from byte arraysTryIntoByteArray&TryFromByteArray: Fallible conversion variants
ReadByteable&WriteByteableTraits: Extension traits forstd::io::Readandstd::io::Write, enabling convenient reading and writing of byteable types.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(Byteable)](withderivefeature): A procedural macro that automatically implements the byte conversion traits for structs, significantly simplifying boilerplate. For advanced use cases,#[derive(UnsafeByteableTransmute)]is also available for manual transmute-based implementations.
§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, ReadByteable, WriteByteable};
use std::fs::File;
#[derive(Byteable, Debug, PartialEq, Clone, Copy)]
struct Packet {
id: u8,
#[byteable(little_endian)]
length: u16,
data: [u8; 4],
}
// Create a packet
let packet = Packet {
id: 42,
length: 1024,
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;
#[derive(Byteable, Debug, Clone, Copy)]
struct NetworkHeader {
#[byteable(big_endian)]
magic: u32, // Network byte order (big-endian)
version: u8,
flags: u8,
#[byteable(little_endian)]
payload_len: u16, // Little-endian for payload
}§Example: Working with TCP Streams
use byteable::{Byteable, ReadByteable, WriteByteable};
use std::net::TcpStream;
#[derive(Byteable, Debug, Clone, Copy)]
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, Debug, Clone, Copy)]
struct AsyncPacket {
#[byteable(little_endian)]
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, IntoByteArray};
// Create endian-aware values
let big = BigEndian::new(0x12345678u32);
let little = LittleEndian::new(0x12345678u32);
// Get raw bytes (in specified endianness)
assert_eq!(big.into_byte_array(), [0x12, 0x34, 0x56, 0x78]);
assert_eq!(little.into_byte_array(), [0x78, 0x56, 0x34, 0x12]);
// Convert back to native value
assert_eq!(big.get(), 0x12345678u32);
assert_eq!(little.get(), 0x12345678u32);§Enum Support
The #[derive(Byteable)] macro supports C-like enums with explicit discriminants:
use byteable::{Byteable, IntoByteArray, TryFromByteArray};
#[derive(Byteable, Debug, Clone, Copy, PartialEq)]
#[repr(u8)]
enum Status {
Idle = 0,
Running = 1,
Completed = 2,
Failed = 3,
}
let status = Status::Running;
let bytes = status.into_byte_array();
assert_eq!(bytes, [1]);
// Use TryFromByteArray for fallible conversion
let restored = Status::try_from_byte_array(bytes)?;
assert_eq!(restored, Status::Running);
// Invalid discriminants return an error
let invalid = Status::try_from_byte_array([255]);
assert!(invalid.is_err());§Enum with Endianness
Enums support type-level endianness attributes for multi-byte discriminants:
use byteable::Byteable;
// Little-endian (common for file formats)
#[derive(Byteable, Debug, Clone, Copy, PartialEq)]
#[repr(u16)]
#[byteable(little_endian)]
enum Command {
Start = 0x1000,
Stop = 0x2000,
Pause = 0x3000,
}
// Big-endian (common for network protocols)
#[derive(Byteable, Debug, Clone, Copy, PartialEq)]
#[repr(u32)]
#[byteable(big_endian)]
enum HttpStatus {
Ok = 200,
NotFound = 404,
InternalError = 500,
}§Enum Requirements
When deriving Byteable for enums:
- Explicit repr type required:
#[repr(u8)],#[repr(u16)],#[repr(u32)],#[repr(u64)],#[repr(i8)],#[repr(i16)],#[repr(i32)], or#[repr(i64)] - Unit variants only: All variants must be unit variants (no fields)
- Explicit discriminants: All variants must have explicit values
- Fallible conversion: Use
TryFromByteArray(notFromByteArray) because invalid discriminants returnEnumFromBytesError
§Nested Enums in Structs
Use the #[byteable(try_transparent)] attribute for enum fields in structs:
use byteable::Byteable;
#[derive(Byteable, Debug, Clone, Copy, PartialEq)]
#[repr(u8)]
enum MessageType {
Data = 1,
Control = 2,
ErrorMsg = 3,
}
#[derive(Byteable, Clone, Copy)]
struct Message {
#[byteable(try_transparent)]
msg_type: MessageType,
#[byteable(big_endian)]
sequence: u32,
payload: [u8; 16],
}§Safety Considerations
The #[derive(Byteable)] macro uses core::mem::transmute internally, which is unsafe.
When using this macro, ensure that:
- All fields are primitive types or have endianness attributes (
#[byteable(big_endian)],#[byteable(little_endian)]) - The struct doesn’t contain types with invalid bit patterns (e.g.,
bool,char) - C-like enums with explicit discriminants are safe (supported via derive)
- Complex enums with fields are not safe
For types with complex invariants (like String, Vec, references, etc.), do not use
the Byteable derive macro. Use only with plain old data (POD) types.
§Advanced Usage
§Custom Byteable Implementation
The #[derive(Byteable)] macro handles most use cases automatically, including
endianness conversion via attributes:
#![cfg(feature = "derive")]
use byteable::{Byteable, IntoByteArray, FromByteArray};
#[derive(Byteable, Debug, PartialEq, Clone, Copy)]
struct Point {
#[byteable(little_endian)]
x: i32,
#[byteable(little_endian)]
y: i32,
}
let point = Point { x: 10, y: 20 };
let bytes = point.into_byte_array();
let restored = Point::from_byte_array(bytes);
assert_eq!(point, restored);For advanced cases, you can still use the impl_byteable_via! macro with manual
implementations. See the trait documentation for details.
§Feature Flags
derive: Enables the#[derive(Byteable)]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
into_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.
- BoolRaw
- CharRaw
- Enum
From Bytes Error - Error type for converting bytes to an enum with an invalid discriminant.
- Little
Endian - A wrapper type that stores a value in little-endian byte order.
Enums§
- Discriminant
- Represents a discriminant value that can be of various integer types.
- TryByteable
Error - Error type for fallible byteable I/O operations.
Traits§
- Associated
Byte Array - Associates a type with its byte array representation.
- Byte
Array - A trait for types that can be used as byte array representations.
- Endian
Convert - A trait for types that can be converted between different byte orders (endianness).
- From
Byte Array - Constructs a value from its byte array representation.
- HasRaw
Type - A trait for types that have a corresponding raw representation type.
- Into
Byte Array - Converts a value into its byte array representation.
- Read
Byteable - Extension trait for
Readthat adds methods for readingByteabletypes. - Read
TryByteable - Extension trait for
Readthat adds methods for reading types with fallible conversion. - TryFrom
Byte Array - Attempts to construct a value from its byte array representation, potentially failing.
- TryHas
RawType - A trait for types that have a corresponding raw representation type with fallible conversion.
- TryInto
Byte Array - Attempts to convert a value into its byte array representation, potentially failing.
- Valid
Bytecast Marker - Marker trait for types that are safe to transmute to and from byte arrays.
- Write
Byteable - Extension trait for
Writethat adds methods for writingByteabletypes. - Write
TryByteable - Extension trait for
Writethat adds methods for writing types with fallible conversion.
Derive Macros§
- Byteable
- Derives a delegate pattern for
Byteableby generating a raw struct with endianness markers. - Unsafe
Byteable Transmute - Derives byte conversion traits for a struct using
transmute.