Skip to main content

Crate multi_trait

Crate multi_trait 

Source
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

§Decoding Traits

  • TryDecodeFrom: Fallibly decode values from byte slices with remainder tracking

§Null Value Traits

  • Null: Define and check for null/default values
  • TryNull: Fallible version of Null for types requiring validation

§Validated Types

  • EncodedBytes: Validated newtype for varint-encoded byte sequences

§Features

  • Zero-copy decoding: TryDecodeFrom returns remaining bytes without allocation
  • Zero-allocation encoding: EncodeIntoBuffer reuses existing buffers
  • Stack-based encoding: EncodeIntoArray for embedded/no_std contexts
  • Optimized encoding: Single-allocation encoding with efficient varint compression
  • no_std support: Works in embedded and constrained environments (with alloc)
  • Type-safe errors: Structured error types with proper error chains
  • Thread-safe: All traits are Send + Sync safe

§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 length
  • EncodeIntoBuffer: Zero allocations (reuses buffer capacity), ideal for hot paths
  • EncodeIntoArray: Zero heap allocations (stack only), deterministic performance
  • TryDecodeFrom: 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 threads
  • Sync: 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_std environments: default-features = false
    • Requires alloc when disabled (for Vec<u8> support)

§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
EncodeInto trait
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 TryNull traits
prelude
one-stop shop for all exported symbols
try_decode_from
TryDecodeFrom trait