Expand description
§Multitrait
A lightweight, high-performance library providing common traits for implementing multiformats types in Rust.
§Overview
This crate provides core traits that standardize encoding, decoding, and null value handling across multiformats implementations:
§Encoding Traits
EncodeInto: Encode values into compact varintVec<u8>formatEncodeIntoBuffer: Zero-allocation encoding into existing buffersEncodeIntoArray: Stack-based encoding forno_stdenvironments
§Decoding Traits
TryDecodeFrom: Fallibly decode values from byte slices with remainder tracking
§Null Value Traits
Null: Define and check for null/default valuesTryNull: Fallible version ofNullfor types requiring validation
§Validated Types
EncodedBytes: Validated newtype for varint-encoded byte sequences
§Features
- Zero-copy decoding:
TryDecodeFromreturns remaining bytes without allocation - Zero-allocation encoding:
EncodeIntoBufferreuses existing buffers - Stack-based encoding:
EncodeIntoArrayforembedded/no_stdcontexts - Optimized encoding: Single-allocation encoding with efficient varint compression
no_stdsupport: Works in embedded and constrained environments (withalloc)- Type-safe errors: Structured error types with proper error chains
- Thread-safe: All traits are
Send + Syncsafe
§Quick Start
use multi_trait::{EncodeInto, TryDecodeFrom};
// Encoding: Convert a value to compact varint bytes
let value = 42u32;
let encoded = value.encode_into();
println!("Encoded {} as {:?}", value, encoded);
// Decoding: Parse bytes back to original value
let (decoded, remaining) = u32::try_decode_from(&encoded).unwrap();
assert_eq!(decoded, value);
assert!(remaining.is_empty());§Encoding Example
The EncodeInto trait provides efficient varint encoding:
use multi_trait::EncodeInto;
// Small values use fewer bytes
assert_eq!(0u8.encode_into(), vec![0]);
assert_eq!(127u8.encode_into(), vec![127]);
assert_eq!(128u8.encode_into(), vec![128, 1]); // Requires 2 bytes
// Works with all unsigned integer types
let large_value = 0xFFFF_FFFF_u32;
let encoded = large_value.encode_into();
println!("Encoded 0x{:X} in {} bytes", large_value, encoded.len());§Decoding Example
The TryDecodeFrom trait enables zero-copy parsing with error handling:
use multi_trait::TryDecodeFrom;
// Decode from byte slice
let bytes = vec![0xFF, 0xFF, 0x03]; // Varint encoding of 65535
let (value, remaining) = u16::try_decode_from(&bytes).unwrap();
assert_eq!(value, 65535);
assert!(remaining.is_empty());
// Handle errors gracefully
let empty: &[u8] = &[];
let result = u8::try_decode_from(empty);
assert!(result.is_err());§Null Value Handling
Define sentinel/null values for custom types:
use multi_trait::Null;
struct MyId(u64);
impl Null for MyId {
fn null() -> Self {
MyId(0)
}
fn is_null(&self) -> bool {
self.0 == 0
}
}
let null_id = MyId::null();
assert!(null_id.is_null());
let valid_id = MyId(12345);
assert!(!valid_id.is_null());§Error Handling
All decode operations return a Result with a structured Error type:
use multi_trait::{TryDecodeFrom, Error};
let truncated = vec![0xFF]; // Incomplete varint
match u16::try_decode_from(&truncated) {
Ok((value, _)) => println!("Decoded: {}", value),
Err(Error::UnsignedVarintDecode { source }) => {
eprintln!("Decode failed: {}", source);
}
Err(e) => eprintln!("Other error: {}", e),
}§Buffer-Based Encoding (Zero Allocation)
The EncodeIntoBuffer trait enables encoding without allocations:
use multi_trait::EncodeIntoBuffer;
// Create a reusable buffer
let mut buffer = Vec::with_capacity(100);
// Encode multiple values with no additional allocations
42u8.encode_into_buffer(&mut buffer);
1000u16.encode_into_buffer(&mut buffer);
100000u32.encode_into_buffer(&mut buffer);
// All three values encoded in one buffer
println!("Encoded {} bytes", buffer.len());§Stack-Based Encoding (No Heap)
The EncodeIntoArray trait provides stack-only encoding for embedded systems:
use multi_trait::EncodeIntoArray;
// Encode to stack-allocated array (no heap)
let (array, len) = 42u8.encode_into_array();
assert_eq!(&array[..len], &[42]);
// Maximum sizes known at compile time
assert_eq!(<u32 as EncodeIntoArray>::MAX_ENCODED_SIZE, 5);§Type Safety with Validated Newtypes
The EncodedBytes newtype provides compile-time guarantees that bytes
represent valid varint encodings:
use multi_trait::EncodedBytes;
// Validation happens at construction
let valid = vec![42u8];
let encoded = EncodedBytes::try_from(valid).unwrap();
// Invalid data is rejected
let invalid = vec![0x80]; // Truncated varint
assert!(EncodedBytes::try_from(invalid).is_err());
// Type system ensures valid data
fn process_encoded(data: EncodedBytes) {
// No need to validate - type guarantees validity
println!("Processing {} bytes", data.len());
}§Performance Characteristics
EncodeInto: Single allocation, O(1) complexity for finding varint lengthEncodeIntoBuffer: Zero allocations (reuses buffer capacity), ideal for hot pathsEncodeIntoArray: Zero heap allocations (stack only), deterministic performanceTryDecodeFrom: Zero allocations, returns slice references- Varint format: Compact representation, 1-10 bytes per integer depending on value
§Thread Safety
All traits and types in this crate are Send + Sync, making them safe to use
in concurrent contexts. This section documents the thread-safety guarantees.
§Trait Implementations
All trait implementations (EncodeInto, TryDecodeFrom, Null, TryNull)
are stateless and immutable, providing these guarantees:
Send: Values can be transferred between threadsSync: References can be shared between threads- No locks required: All operations are lock-free
- No data races: No mutable state is shared
§Type Safety
The EncodedBytes newtype is explicitly marked as Send + Sync:
use multi_trait::EncodedBytes;
use std::sync::Arc;
use std::thread;
let encoded = EncodedBytes::new(&[42]).unwrap();
let shared = Arc::new(encoded);
// Can be shared across threads safely
let handles: Vec<_> = (0..4)
.map(|_| {
let data = Arc::clone(&shared);
thread::spawn(move || {
assert_eq!(&data[..], &[42]);
})
})
.collect();
for handle in handles {
handle.join().unwrap();
}§Concurrency Patterns
Common patterns that work safely:
- Parallel encoding: Multiple threads can encode different values simultaneously
- Shared decoding: Multiple threads can decode from the same source data
- Pipeline processing: Encode in one thread, decode in another
- Work stealing: Tasks can move between threads freely
§Feature Flags
std(default): Enables standard library support- Disable for
no_stdenvironments:default-features = false - Requires
allocwhen disabled (forVec<u8>support)
- Disable for
§no_std Support
This crate works in no_std environments with alloc:
[dependencies]
multitrait = { version = "1.0", default-features = false }§Implementation Details
The crate uses production-quality declarative macros to eliminate code duplication while maintaining zero runtime overhead. All encoding/decoding implementations are generated at compile time with full type safety.
Re-exports§
pub use error::Error;pub use enc_into::EncodeInto;pub use enc_into_buffer::EncodeIntoBuffer;pub use enc_into_array::EncodeIntoArray;pub use null::Null;pub use null::TryNull;pub use try_decode_from::TryDecodeFrom;pub use encoded_bytes::EncodedBytes;
Modules§
- enc_
into EncodeIntotrait- enc_
into_ array - EncodeIntoArray trait for stack-based encoding Stack-based encoding trait for no-allocation varint encoding.
- enc_
into_ buffer - EncodeIntoBuffer trait for zero-allocation encoding Buffer-based encoding trait for zero-allocation varint encoding.
- encoded_
bytes - Validated newtype for encoded bytes Validated newtype for varint-encoded byte sequences.
- error
- Errors generated from the implementations
- null
- Null and
TryNulltraits - prelude
- one-stop shop for all exported symbols
- try_
decode_ from TryDecodeFromtrait