Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

28 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

multi-trait

Common traits for multiformats types in Rust. The crate gives encoding, decoding, and null value traits with zero-copy decoding and no_std support.

Features

  • Varint encoding with minimal allocations.
  • Zero-copy decoding. TryDecodeFrom returns the remaining bytes.
  • Zero-allocation encoding. EncodeIntoBuffer reuses an existing buffer.
  • Stack-based encoding. EncodeIntoArray for no_std and embedded systems.
  • no_std support with alloc.
  • Validated newtype. EncodedBytes gives type-level guarantees.
  • #![deny(unsafe_code)] set at the crate root.
  • All types are Send + Sync.
  • 155 tests: unit, property-based, security, concurrency, and round-trip.

Install

Add this to your Cargo.toml:

[dependencies]
multi-trait = "1.1"

For no_std environments:

[dependencies]
multi-trait = { version = "1.1", default-features = false }

MSRV: Rust 1.85 (Edition 2024).

Quick Start

use multi_trait::{EncodeInto, TryDecodeFrom};

// Encode a value to varint bytes
let value = 42u32;
let encoded = value.encode_into();
println!("Encoded {} as {:?}", value, encoded);

// Decode the bytes back to the value
let (decoded, remaining) = u32::try_decode_from(&encoded).unwrap();
assert_eq!(decoded, value);
assert!(remaining.is_empty());

Core Traits

Encoding Traits

EncodeInto

Encode a value into a varint Vec<u8>. Use this for one-off encoding.

use multi_trait::EncodeInto;

let value = 1000u16;
let bytes = value.encode_into(); // Allocates a new Vec<u8>

EncodeIntoBuffer

Encode values into an existing buffer with no allocation. Use this in hot paths or when you encode multiple values.

use multi_trait::EncodeIntoBuffer;

let mut buffer = Vec::with_capacity(100);

42u8.encode_into_buffer(&mut buffer);
1000u16.encode_into_buffer(&mut buffer);
100_000u32.encode_into_buffer(&mut buffer);

println!("Encoded {} bytes total", buffer.len());

EncodeIntoArray

Encode a value into a stack-allocated array. Use this in no_std or real-time systems.

use multi_trait::EncodeIntoArray;

let (array, len) = 42u8.encode_into_array();
assert_eq!(&array[..len], &[42]);

assert_eq!(<u32 as EncodeIntoArray>::MAX_ENCODED_SIZE, 5);

Decoding Trait

TryDecodeFrom

Decode a value from a byte slice. Returns the value and the remaining bytes. No allocation occurs.

use multi_trait::TryDecodeFrom;

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());

// Decode multiple values from one buffer
let bytes = vec![0x01, 0x02, 0x03];
let (first, rest) = u8::try_decode_from(&bytes).unwrap();
let (second, rest) = u8::try_decode_from(rest).unwrap();
let (third, rest) = u8::try_decode_from(rest).unwrap();
assert_eq!((first, second, third), (1, 2, 3));

Null Value Traits

Null

Define and check for a null or sentinel value.

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());

TryNull

Fallible version of Null. Use it for types that need validation.

use multi_trait::TryNull;

struct ValidatedId(u64);

impl TryNull for ValidatedId {
    type Error = &'static str;

    fn try_null() -> Result<Self, Self::Error> {
        Ok(ValidatedId(0))
    }

    fn is_null(&self) -> bool {
        self.0 == 0
    }
}

Validated Types

EncodedBytes

A validated newtype for varint-encoded byte sequences. Construction checks that the bytes are a valid encoding.

use multi_trait::EncodedBytes;

let valid = vec![42u8];
let encoded = EncodedBytes::try_from(valid).unwrap();

let invalid = vec![0x80]; // Truncated varint
assert!(EncodedBytes::try_from(invalid).is_err());

fn process_encoded(data: EncodedBytes) {
    // The type guarantees the data is valid
    println!("Processing {} bytes", data.len());
}

Error Handling

All decode operations return 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 { .. }) => {
        eprintln!("Decode failed");
    }
    Err(e) => eprintln!("Other error: {}", e),
}

Error Types

The Error enum is #[non_exhaustive]. It has these variants:

  • UnsignedVarintDecode: Varint decoding failed. The cause can be truncated data or an invalid encoding.
  • InsufficientData: The input slice does not have enough bytes to decode the requested type.
  • InvalidEncoding: The data is structurally invalid. This variant is for future use and custom validation.

All errors give source chains for debugging. Backtraces are available when the std feature is on.

Performance Guide

Encoding Performance

Pick the encoding strategy for your use case:

  1. EncodeInto — One allocation per call. Use this for one-off encodings.
  2. EncodeIntoBuffer — Zero allocations when the buffer has capacity. Use this in hot paths or when you encode multiple values.
  3. EncodeIntoArray — Zero heap allocations. Use this in no_std or real-time systems.

Decoding Performance

  • Zero allocations. Returns slice references to the input data.
  • No data copy during decode.
  • Efficient varint format checking.

Encoded Sizes

Varint encoding uses 1 to 10 bytes for integers. The size depends on the value.

  • Values 0 to 127: 1 byte.
  • Values 128 to 16,383: 2 bytes.
  • Values 16,384 to 2,097,151: 3 bytes.

Maximum encoded sizes by type:

  • u8, bool: 2 bytes.
  • u16: 3 bytes.
  • u32: 5 bytes.
  • u64, usize (64-bit): 10 bytes.
  • u128: 19 bytes.

Thread Safety

All traits and types in this crate are Send + Sync. You can use them in concurrent contexts.

All operations are lock-free. No mutable state is shared.

no_std Support

The crate works in no_std environments with alloc:

[dependencies]
multi-trait = { version = "1.1", default-features = false }

Use EncodeIntoArray for heap-free encoding in embedded systems:

#![no_std]

extern crate alloc;
use alloc::vec::Vec;
use multi_trait::EncodeIntoArray;

let (array, len) = 42u8.encode_into_array();

let vec = Vec::from(&array[..len]);

Feature Flags

  • std (default). Enables standard library support. It enables std::error::Error implementation and backtrace support in errors. Disable it for no_std with default-features = false. The crate needs alloc when std is off.

Supported Types

All traits are implemented for:

  • bool: Encoded as 0 (false) or 1 (true).
  • u8, u16, u32, u64, u128: Variable-length varint encoding.
  • usize: Platform-dependent (32-bit or 64-bit).
  • [u8; N]: Fixed-length byte arrays. EncodeInto encodes the raw bytes without a varint prefix. TryDecodeFrom reads exactly N bytes. Use this for BLS share identifiers and other fixed-size binary data.

EncodeIntoBuffer and EncodeIntoArray are implemented for bool, u8, u16, u32, u64, u128, and usize. They do not support [u8; N].

Examples

See the examples/ directory for complete examples:

  • basic.rs — Basic encoding and decoding.
  • error_handling.rs — Error handling patterns.
  • custom_type.rs — Implement the traits for custom types.
  • no_std.rs — Use the crate in no_std environments.

Run an example:

cargo run --example basic

Testing

The crate has 155 tests: unit, property-based, concurrency, security, and edge case tests.

Run all tests:

cargo test

Documentation

Generate and view the API documentation:

cargo doc --open

License

Licensed under Apache-2.0. See LICENSE for details.

Contributing

Contributions are welcome. Before you submit a change, make sure:

  • All tests pass (cargo test).
  • The code is formatted (cargo fmt).
  • No clippy warnings (cargo clippy).
  • New features include tests and documentation.

Links

About

Traits useful for multicodec based type crates

Resources

Security policy

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages