Expand description
§OSAL-RS-Serde - Serialization/Deserialization Framework
A lightweight, extensible serialization framework inspired by Serde, designed for embedded systems and no-std environments.
§Overview
This library provides a flexible serialization/deserialization framework that:
- No-std compatible: Works perfectly in bare-metal embedded environments
- Memory-efficient: Optimized for resource-constrained systems
- Derive macro support:
#[derive(Serialize, Deserialize)]for automatic implementation - Extensible: Create custom serializers for any format (binary, JSON, MessagePack, etc.)
- Type-safe: Leverages Rust’s type system for compile-time guarantees
- Standalone: Can be used in any project, not just with osal-rs
§Supported Types
- Primitives:
bool,u8,i8,u16,i16,u32,i32,u64,i64,u128,i128,f32,f64 - Compound types: Arrays
[T; N], tuples(T1, T2, T3)(up to 3 elements),Option<T> - Collections:
Vec<T>, byte slices, strings (withallocfeature) - Custom types: Any struct implementing
Serialize/Deserialize - Nested structs: Full support for struct composition
§Memory Layout
The default ByteSerializer uses little-endian binary format:
- Primitives: Native sizes (1, 2, 4, 8, or 16 bytes)
bool: 1 byte (0 or 1)Option<T>: 1 byte tag + sizeof(T) if Some, 1 byte if None- Arrays
[T; N]: sizeof(T) * N (no length prefix) - Tuples: concatenation of all elements
- Structs: concatenation of all fields in declaration order
§Quick Start
§Using Derive Macros (Recommended)
§Basic Struct Example
ⓘ
use osal_rs_serde::{Serialize, Deserialize, to_bytes, from_bytes};
#[derive(Serialize, Deserialize)]
struct SensorData {
temperature: i16,
humidity: u8,
pressure: u32,
}
fn main() {
let data = SensorData {
temperature: 25,
humidity: 60,
pressure: 1013,
};
// Serialize
let mut buffer = [0u8; 32];
let len = to_bytes(&data, &mut buffer).unwrap();
println!("Serialized {} bytes", len);
// Deserialize
let read_data: SensorData = from_bytes(&buffer[..len]).unwrap();
println!("Temperature: {}", read_data.temperature);
}§Struct with Optional Fields
ⓘ
use osal_rs_serde::{Serialize, Deserialize, to_bytes, from_bytes};
#[derive(Serialize, Deserialize)]
struct Config {
device_id: u32,
name: Option<u8>, // Optional device name code
enabled: bool,
timeout: Option<u16>, // Optional timeout in ms
}
fn main() {
let config = Config {
device_id: 100,
name: Some(42),
enabled: true,
timeout: None,
};
let mut buffer = [0u8; 64];
let len = to_bytes(&config, &mut buffer).unwrap();
let decoded: Config = from_bytes(&buffer[..len]).unwrap();
}§Struct with Arrays and Tuples
ⓘ
use osal_rs_serde::{Serialize, Deserialize, to_bytes, from_bytes};
#[derive(Serialize, Deserialize)]
struct TelemetryPacket {
timestamp: u64,
coordinates: (i32, i32, i32), // x, y, z
samples: [u16; 8], // 8 sensor readings
status: u8,
}
fn main() {
let packet = TelemetryPacket {
timestamp: 1642857600,
coordinates: (100, 200, 50),
samples: [10, 20, 30, 40, 50, 60, 70, 80],
status: 0xFF,
};
let mut buffer = [0u8; 128];
let len = to_bytes(&packet, &mut buffer).unwrap();
println!("Telemetry packet: {} bytes", len);
}§Nested Structs
ⓘ
use osal_rs_serde::{Serialize, Deserialize, to_bytes, from_bytes};
#[derive(Serialize, Deserialize)]
struct Location {
latitude: i32,
longitude: i32,
}
#[derive(Serialize, Deserialize)]
struct Device {
id: u32,
battery: u8,
location: Location,
active: bool,
}
fn main() {
let device = Device {
id: 42,
battery: 85,
location: Location {
latitude: 45500000,
longitude: 9200000,
},
active: true,
};
let mut buffer = [0u8; 64];
let len = to_bytes(&device, &mut buffer).unwrap();
let decoded: Device = from_bytes(&buffer[..len]).unwrap();
println!("Device at {}, {}",
decoded.location.latitude,
decoded.location.longitude);
}§Complex Embedded System Example
ⓘ
use osal_rs_serde::{Serialize, Deserialize, to_bytes, from_bytes};
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct MotorControl {
motor_id: u8,
speed: i16, // -1000 to 1000
direction: bool, // true = forward, false = reverse
current: u16, // mA
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct RobotState {
timestamp: u64,
motors: [MotorControl; 4], // 4 motors
battery_voltage: u16, // mV
temperature: i8, // °C
error_flags: u32,
}
fn main() {
let state = RobotState {
timestamp: 1000000,
motors: [
MotorControl { motor_id: 0, speed: 500, direction: true, current: 1200 },
MotorControl { motor_id: 1, speed: 500, direction: true, current: 1150 },
MotorControl { motor_id: 2, speed: -300, direction: false, current: 800 },
MotorControl { motor_id: 3, speed: -300, direction: false, current: 850 },
],
battery_voltage: 12400, // 12.4V
temperature: 35,
error_flags: 0,
};
let mut buffer = [0u8; 256];
let len = to_bytes(&state, &mut buffer).unwrap();
println!("Robot state serialized: {} bytes", len);
// Deserialize and check
let decoded: RobotState = from_bytes(&buffer[..len]).unwrap();
assert_eq!(state, decoded);
println!("Battery: {}mV, Temp: {}°C",
decoded.battery_voltage,
decoded.temperature);
}§Manual Implementation (For Custom Behavior)
ⓘ
use osal_rs_serde::{Serialize, Deserialize, Serializer, Deserializer};
struct Point {
x: i32,
y: i32,
}
impl Serialize for Point {
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
serializer.serialize_i32("x", self.x)?;
serializer.serialize_i32("y", self.y)?;
Ok(())
}
}
impl Deserialize for Point {
fn deserialize<D: Deserializer>(deserializer: &mut D, _name: &str) -> Result<Self, D::Error> {
Ok(Point {
x: deserializer.deserialize_i32("x")?,
y: deserializer.deserialize_i32("y")?,
})
}
}§Integration with OSAL-RS
Perfect for inter-task communication using queues:
ⓘ
use osal_rs::os::{Queue, QueueFn};
use osal_rs_serde::{Serialize, Deserialize, to_bytes, from_bytes};
#[derive(Serialize, Deserialize)]
struct Message {
command: u8,
data: [u16; 4],
}
fn sender(queue: &Queue) {
let msg = Message { command: 0x42, data: [1, 2, 3, 4] };
let mut buffer = [0u8; 32];
let len = to_bytes(&msg, &mut buffer).unwrap();
queue.post(&buffer[..len], 100).unwrap();
}
fn receiver(queue: &Queue) {
let mut buffer = [0u8; 32];
queue.fetch(&mut buffer, 100).unwrap();
let msg: Message = from_bytes(&buffer).unwrap();
}§Supported Types
- Primitives: All integer types (u8-u128, i8-i128), f32, f64, bool
- Compound: Arrays
[T; N], tuples (up to 3 elements),Option<T> - Collections:
Vec<T>,String(requiresallocfeature) - Custom: Any type implementing
Serialize/Deserialize - Nested: Full support for nested structs
§Creating Custom Serializers
You can create custom serializers for different formats (JSON, MessagePack, CBOR, etc.)
by implementing the Serializer and Deserializer traits:
ⓘ
use osal_rs_serde::{Serializer, Error};
struct JsonSerializer<'a> {
buffer: &'a mut [u8],
position: usize,
}
impl<'a> Serializer for JsonSerializer<'a> {
type Error = Error;
fn serialize_u32(&mut self, name: &str, v: u32) -> Result<(), Self::Error> {
// Write JSON format: "name": value
// Implementation here...
Ok(())
}
// Implement other serialize_* methods...
}See examples/custom_serializer.rs for a complete implementation example.
§Performance & Binary Size
- Zero-copy: Reads/writes directly to/from buffers
- No allocations: Works entirely with stack buffers (or Vec with
alloc) - Predictable: Buffer size calculable at compile time
- Small code size: Minimal overhead, optimized for embedded targets
§Features
default: Includesallocfeaturealloc: Enables Vec, String supportstd: Enables standard library (error traits, etc.)derive: Enables#[derive(Serialize, Deserialize)]macros (recommended)
§Examples
The examples/ directory contains complete working examples:
basic.rs- Simple struct serializationwith_derive.rs- Using derive macrosarrays_tuples.rs- Arrays and tuplesnested_structs.rs- Nested structuresoptional_fields.rs- Optional fields with Optionrobot_control.rs- Complex embedded systemcustom_serializer.rs- Custom serializer implementation
Re-exports§
pub use error::Error;pub use error::Result;pub use ser::Serialize;pub use ser::Serializer;pub use ser::ByteSerializer;pub use de::Deserialize;pub use de::Deserializer;pub use de::ByteDeserializer;
Modules§
- de
- Deserialization traits and implementations.
- error
- Error types for serialization and deserialization operations.
- ser
- Serialization traits and implementations.
Functions§
- from_
bytes - Deserialize a value from a byte buffer.
- to_
bytes - Serialize a value to a byte buffer.
- to_
dyn_ bytes - Serialize a value to a dynamically sized byte vector.