diff --git a/CHANGELOG.md b/CHANGELOG.md index 08cdba1..df82e41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.0.0] - 2026-07-13 + +### Changed +- Synced from bettersign workspace (bs-multitrait 0.7.0) +- Renamed crate from `bs-multitrait` to `multi-trait` +- Initial published release on crates.io as `multi-trait` + ## [1.0.1] - 2025-01-08 ### Fixed diff --git a/Cargo.toml b/Cargo.toml index f5ff82b..e941248 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,14 @@ [package] -name = "multitrait" +name = "multi-trait" version = "1.0.1" edition = "2021" authors = ["Dave Grantham "] description = "Common traits for multiformats types" -repository = "https://github.com/cryptidtech/multitrait.git" +repository = "https://github.com/cryptidtech/multi-trait.git" readme = "README.md" license = "Apache-2.0" +keywords = ["multiformats", "encoding", "multicodec", "multibase", "no_std"] +categories = ["encoding", "data-structures"] [features] default = ["std"] @@ -18,8 +20,25 @@ unsigned-varint = { version = "0.8", default-features = false } [dev-dependencies] proptest = "1.4" -criterion = "0.5" +criterion = { version = "0.5", features = ["html_reports"] } [[bench]] name = "encoding" harness = false +path = "benches/encoding.rs" + +[[example]] +name = "basic" +path = "examples/basic.rs" + +[[example]] +name = "custom_type" +path = "examples/custom_type.rs" + +[[example]] +name = "error_handling" +path = "examples/error_handling.rs" + +[[example]] +name = "no_std" +path = "examples/no_std.rs" diff --git a/benches/encoding.rs b/benches/encoding.rs index 7db53d3..269a8fa 100644 --- a/benches/encoding.rs +++ b/benches/encoding.rs @@ -3,8 +3,10 @@ //! //! Run with: `cargo bench` -use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; -use multitrait::{EncodeInto, EncodeIntoArray, EncodeIntoBuffer, TryDecodeFrom}; +use std::hint::black_box; + +use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; +use multi_trait::{EncodeInto, EncodeIntoArray, EncodeIntoBuffer, TryDecodeFrom}; /// Benchmark encoding operations for various integer types fn bench_encoding(c: &mut Criterion) { diff --git a/examples/basic.rs b/examples/basic.rs index e84aab3..9ab244e 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -7,7 +7,7 @@ //! - Sequential encoding and decoding //! - Using different encoding strategies -use multitrait::{EncodeInto, EncodeIntoArray, EncodeIntoBuffer, TryDecodeFrom}; +use multi_trait::{EncodeInto, EncodeIntoArray, EncodeIntoBuffer, TryDecodeFrom}; fn main() { println!("=== Multitrait Basic Example ===\n"); diff --git a/examples/custom_type.rs b/examples/custom_type.rs index 5c2cb95..a1422c5 100644 --- a/examples/custom_type.rs +++ b/examples/custom_type.rs @@ -8,7 +8,7 @@ //! - Implementing Null and TryNull for custom types //! - Creating composable encoding/decoding for complex structures -use multitrait::{EncodeInto, EncodeIntoBuffer, Null, TryDecodeFrom, TryNull}; +use multi_trait::{EncodeInto, EncodeIntoBuffer, Null, TryDecodeFrom, TryNull}; fn main() { println!("=== Multitrait Custom Type Example ===\n"); @@ -47,7 +47,7 @@ fn simple_newtype() { // Implement TryDecodeFrom impl<'a> TryDecodeFrom<'a> for UserId { - type Error = multitrait::Error; + type Error = multi_trait::Error; fn try_decode_from(bytes: &'a [u8]) -> Result<(Self, &'a [u8]), Self::Error> { let (id, remaining) = u64::try_decode_from(bytes)?; @@ -96,7 +96,7 @@ fn multi_field_struct() { // Implement TryDecodeFrom - decode fields sequentially impl<'a> TryDecodeFrom<'a> for Person { - type Error = multitrait::Error; + type Error = multi_trait::Error; fn try_decode_from(bytes: &'a [u8]) -> Result<(Self, &'a [u8]), Self::Error> { let (id, remaining) = u32::try_decode_from(bytes)?; @@ -244,7 +244,7 @@ fn nested_structures() { // Implement TryDecodeFrom for Metadata impl<'a> TryDecodeFrom<'a> for Metadata { - type Error = multitrait::Error; + type Error = multi_trait::Error; fn try_decode_from(bytes: &'a [u8]) -> Result<(Self, &'a [u8]), Self::Error> { let (version, remaining) = u8::try_decode_from(bytes)?; @@ -267,7 +267,7 @@ fn nested_structures() { // Implement TryDecodeFrom for Message impl<'a> TryDecodeFrom<'a> for Message { - type Error = multitrait::Error; + type Error = multi_trait::Error; fn try_decode_from(bytes: &'a [u8]) -> Result<(Self, &'a [u8]), Self::Error> { let (metadata, remaining) = Metadata::try_decode_from(bytes)?; diff --git a/examples/error_handling.rs b/examples/error_handling.rs index a6a7d70..d83cf59 100644 --- a/examples/error_handling.rs +++ b/examples/error_handling.rs @@ -8,7 +8,7 @@ //! - Validating data with EncodedBytes //! - Recovering from errors -use multitrait::{EncodedBytes, Error, TryDecodeFrom}; +use multi_trait::{EncodedBytes, Error, TryDecodeFrom}; fn main() { println!("=== Multitrait Error Handling Example ===\n"); @@ -215,7 +215,7 @@ fn inspect_error_source() { /// Helper function showing how to wrap multitrait errors in application-specific errors #[allow(dead_code)] mod application_errors { - use multitrait::Error as MultitraitError; + use multi_trait::Error as MultitraitError; #[derive(Debug)] pub enum AppError { diff --git a/examples/no_std.rs b/examples/no_std.rs index bf2eed0..c0760c3 100644 --- a/examples/no_std.rs +++ b/examples/no_std.rs @@ -24,7 +24,7 @@ // #![no_std] // extern crate alloc; -use multitrait::{EncodeInto, EncodeIntoArray, EncodeIntoBuffer, TryDecodeFrom}; +use multi_trait::{EncodeInto, EncodeIntoArray, EncodeIntoBuffer, TryDecodeFrom}; fn main() { println!("=== Multitrait no_std Usage Patterns ===\n"); @@ -207,7 +207,7 @@ fn zero_allocation_decoding() { mod no_std_custom_types { // In actual no_std code: // use alloc::vec::Vec; - use multitrait::{EncodeIntoArray, EncodeIntoBuffer, TryDecodeFrom}; + use multi_trait::{EncodeIntoArray, EncodeIntoBuffer, TryDecodeFrom}; /// A custom type that uses only stack encoding #[derive(Debug, PartialEq)] @@ -230,7 +230,7 @@ mod no_std_custom_types { } /// Decode from bytes (zero allocation) - pub fn decode_from(bytes: &[u8]) -> Result<(Self, &[u8]), multitrait::Error> { + pub fn decode_from(bytes: &[u8]) -> Result<(Self, &[u8]), multi_trait::Error> { let (sensor_id, remaining) = u8::try_decode_from(bytes)?; let (value, remaining) = u16::try_decode_from(remaining)?; Ok((SensorReading { sensor_id, value }, remaining)) diff --git a/src/enc_into.rs b/src/enc_into.rs index 8ed2a99..f84d19a 100644 --- a/src/enc_into.rs +++ b/src/enc_into.rs @@ -2,7 +2,7 @@ #[cfg(not(feature = "std"))] use alloc::{vec, vec::Vec}; -use unsigned_varint::{decode, encode}; +use unsigned_varint::encode; /// Trait for encoding values into compact varint byte representation. /// @@ -37,7 +37,7 @@ use unsigned_varint::{decode, encode}; /// # Examples /// /// ```rust -/// use multitrait::EncodeInto; +/// use multi_trait::EncodeInto; /// /// // Small values use minimal space /// let small = 42u8; @@ -72,7 +72,7 @@ pub trait EncodeInto { /// # Examples /// /// ```rust - /// use multitrait::EncodeInto; + /// use multi_trait::EncodeInto; /// /// let value = 300u16; /// let bytes = value.encode_into(); @@ -114,16 +114,10 @@ macro_rules! impl_encode_into { let mut buf = encode::$buffer_fn(); // Encode value into buffer - encode::$encode_fn(*self, &mut buf); - - // Find the length efficiently by locating the last byte marker - let len = buf - .iter() - .position(|&b| decode::is_last(b)) - .map_or(buf.len(), |pos| pos + 1); + let encoded = encode::$encode_fn(*self, &mut buf); // Single allocation: slice and convert to Vec - buf[..len].to_vec() + encoded.to_vec() } } )+ @@ -149,3 +143,10 @@ impl_encode_into! { u128 => u128_buffer, u128; usize => usize_buffer, usize; } + +/// Encode a fixed-length byte array as raw bytes (used for BLS share identifiers). +impl EncodeInto for [u8; N] { + fn encode_into(&self) -> Vec { + self.as_slice().to_vec() + } +} diff --git a/src/enc_into_array.rs b/src/enc_into_array.rs index 4c017bd..df2a6ac 100644 --- a/src/enc_into_array.rs +++ b/src/enc_into_array.rs @@ -26,7 +26,7 @@ //! ## Basic stack encoding //! //! ```rust -//! use multitrait::EncodeIntoArray; +//! use multi_trait::EncodeIntoArray; //! //! let (array, len) = 42u8.encode_into_array(); //! assert_eq!(&array[..len], &[42]); @@ -35,7 +35,7 @@ //! ## Working with larger values //! //! ```rust -//! use multitrait::EncodeIntoArray; +//! use multi_trait::EncodeIntoArray; //! //! let (array, len) = 1000u16.encode_into_array(); //! // Varint encoding of 1000 takes 2 bytes @@ -48,7 +48,7 @@ //! Each type has a compile-time known maximum encoded size: //! //! ```rust -//! use multitrait::EncodeIntoArray; +//! use multi_trait::EncodeIntoArray; //! //! // u8 values fit in 2 bytes max //! assert_eq!(::MAX_ENCODED_SIZE, 2); @@ -97,7 +97,7 @@ pub const MAX_VARINT_SIZE: usize = 19; /// # Examples /// /// ```rust -/// use multitrait::EncodeIntoArray; +/// use multi_trait::EncodeIntoArray; /// /// // Encode a value to a stack array /// let (array, len) = 42u8.encode_into_array(); @@ -132,7 +132,7 @@ pub trait EncodeIntoArray { /// # Examples /// /// ```rust - /// use multitrait::EncodeIntoArray; + /// use multi_trait::EncodeIntoArray; /// /// let (array, len) = 42u8.encode_into_array(); /// assert_eq!(len, 1); diff --git a/src/enc_into_buffer.rs b/src/enc_into_buffer.rs index 2e98979..cf075ee 100644 --- a/src/enc_into_buffer.rs +++ b/src/enc_into_buffer.rs @@ -25,7 +25,7 @@ //! ## Single value encoding //! //! ```rust -//! use multitrait::EncodeIntoBuffer; +//! use multi_trait::EncodeIntoBuffer; //! //! let mut buffer = Vec::new(); //! 42u8.encode_into_buffer(&mut buffer); @@ -35,7 +35,7 @@ //! ## Sequential encoding (multiple values) //! //! ```rust -//! use multitrait::EncodeIntoBuffer; +//! use multi_trait::EncodeIntoBuffer; //! //! let mut buffer = Vec::new(); //! 42u8.encode_into_buffer(&mut buffer); @@ -49,7 +49,7 @@ //! ## Buffer reuse //! //! ```rust -//! use multitrait::EncodeIntoBuffer; +//! use multi_trait::EncodeIntoBuffer; //! //! let mut buffer = Vec::with_capacity(100); //! for i in 0u8..10 { @@ -88,7 +88,7 @@ use unsigned_varint::encode; /// # Examples /// /// ```rust -/// use multitrait::EncodeIntoBuffer; +/// use multi_trait::EncodeIntoBuffer; /// /// // Create a reusable buffer /// let mut buffer = Vec::with_capacity(64); @@ -121,7 +121,7 @@ pub trait EncodeIntoBuffer { /// # Examples /// /// ```rust - /// use multitrait::EncodeIntoBuffer; + /// use multi_trait::EncodeIntoBuffer; /// /// let mut buffer = Vec::new(); /// 42u8.encode_into_buffer(&mut buffer); @@ -167,16 +167,10 @@ macro_rules! impl_encode_into_buffer { let mut buf = encode::$buffer_fn(); // Encode value into temporary buffer - encode::$encode_fn(*self, &mut buf); - - // Find the length efficiently by locating the last byte marker - let len = buf - .iter() - .position(|&b| unsigned_varint::decode::is_last(b)) - .map_or(buf.len(), |pos| pos + 1); + let encoded = encode::$encode_fn(*self, &mut buf); // Extend the target buffer with the encoded bytes - buffer.extend_from_slice(&buf[..len]); + buffer.extend_from_slice(encoded); } } )+ diff --git a/src/encoded_bytes.rs b/src/encoded_bytes.rs index a581f5e..c8430a9 100644 --- a/src/encoded_bytes.rs +++ b/src/encoded_bytes.rs @@ -38,7 +38,7 @@ use unsigned_varint::decode; /// ## Creating from valid data /// /// ```rust -/// use multitrait::EncodedBytes; +/// use multi_trait::EncodedBytes; /// /// // Valid varint encoding of 42 /// let bytes = vec![42u8]; @@ -49,7 +49,7 @@ use unsigned_varint::decode; /// ## Validation catches invalid data /// /// ```rust -/// use multitrait::EncodedBytes; +/// use multi_trait::EncodedBytes; /// /// // Invalid: continuation bit set but no following byte /// let invalid = vec![0x80]; @@ -60,7 +60,7 @@ use unsigned_varint::decode; /// ## Zero-cost unwrapping /// /// ```rust -/// use multitrait::EncodedBytes; +/// use multi_trait::EncodedBytes; /// /// let bytes = vec![42u8]; /// let encoded = EncodedBytes::try_from(bytes).unwrap(); @@ -85,7 +85,7 @@ impl EncodedBytes { /// # Examples /// /// ```rust - /// use multitrait::EncodedBytes; + /// use multi_trait::EncodedBytes; /// /// let encoded = EncodedBytes::new(&[42]).unwrap(); /// assert_eq!(encoded.as_ref(), &[42]); @@ -99,7 +99,7 @@ impl EncodedBytes { /// # Examples /// /// ```rust - /// use multitrait::EncodedBytes; + /// use multi_trait::EncodedBytes; /// /// let encoded = EncodedBytes::new(&[42]).unwrap(); /// assert_eq!(encoded.len(), 1); @@ -118,7 +118,7 @@ impl EncodedBytes { /// # Examples /// /// ```rust - /// use multitrait::EncodedBytes; + /// use multi_trait::EncodedBytes; /// /// let encoded = EncodedBytes::new(&[42]).unwrap(); /// assert!(!encoded.is_empty()); @@ -133,7 +133,7 @@ impl EncodedBytes { /// # Examples /// /// ```rust - /// use multitrait::EncodedBytes; + /// use multi_trait::EncodedBytes; /// /// let encoded = EncodedBytes::new(&[42]).unwrap(); /// assert_eq!(encoded.as_bytes(), &[42]); @@ -150,7 +150,7 @@ impl EncodedBytes { /// # Examples /// /// ```rust - /// use multitrait::EncodedBytes; + /// use multi_trait::EncodedBytes; /// /// let encoded = EncodedBytes::new(&[42]).unwrap(); /// let bytes = encoded.into_vec(); @@ -183,7 +183,7 @@ impl TryFrom> for EncodedBytes { /// # Examples /// /// ```rust - /// use multitrait::EncodedBytes; + /// use multi_trait::EncodedBytes; /// /// // Valid single-byte encoding /// let valid = vec![42]; @@ -208,9 +208,15 @@ impl TryFrom> for EncodedBytes { Ok((_, remaining)) => remaining, Err(source) => { #[cfg(feature = "std")] - { return Err(Error::UnsignedVarintDecode { source }); } + { + return Err(Error::UnsignedVarintDecode { source }); + } #[cfg(not(feature = "std"))] - { return Err(Error::UnsignedVarintDecode { message: format!("{:?}", source) }); } + { + return Err(Error::UnsignedVarintDecode { + message: format!("{:?}", source), + }); + } } }; @@ -236,7 +242,7 @@ impl From for Vec { /// # Examples /// /// ```rust - /// use multitrait::EncodedBytes; + /// use multi_trait::EncodedBytes; /// /// let encoded = EncodedBytes::new(&[42]).unwrap(); /// let bytes: Vec = encoded.into(); @@ -254,7 +260,7 @@ impl AsRef<[u8]> for EncodedBytes { /// # Examples /// /// ```rust - /// use multitrait::EncodedBytes; + /// use multi_trait::EncodedBytes; /// /// let encoded = EncodedBytes::new(&[42]).unwrap(); /// let slice: &[u8] = encoded.as_ref(); @@ -276,7 +282,7 @@ impl Deref for EncodedBytes { /// # Examples /// /// ```rust - /// use multitrait::EncodedBytes; + /// use multi_trait::EncodedBytes; /// /// let encoded = EncodedBytes::new(&[42]).unwrap(); /// assert_eq!(encoded[0], 42); // Deref enables indexing @@ -383,7 +389,7 @@ mod tests { } // Compile-time verification of Send + Sync - #[allow(dead_code)] + #[test] fn assert_send_sync() { fn is_send() {} fn is_sync() {} diff --git a/src/error.rs b/src/error.rs index 3ed80e4..a21f0e4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -19,7 +19,7 @@ use alloc::string::String; /// # Examples /// /// ``` -/// use multitrait::{TryDecodeFrom, Error}; +/// use multi_trait::{TryDecodeFrom, Error}; /// /// // Attempting to decode from empty slice returns an error /// let result = u8::try_decode_from(&[]); @@ -76,7 +76,7 @@ pub enum Error { /// # Examples /// /// ``` - /// use multitrait::{TryDecodeFrom, Error}; + /// use multi_trait::{TryDecodeFrom, Error}; /// /// // Empty slice cannot decode any value /// let result = u16::try_decode_from(&[]); diff --git a/src/lib.rs b/src/lib.rs index 4d68dd5..10a639b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,7 +41,7 @@ //! ## Quick Start //! //! ```rust -//! use multitrait::{EncodeInto, TryDecodeFrom}; +//! use multi_trait::{EncodeInto, TryDecodeFrom}; //! //! // Encoding: Convert a value to compact varint bytes //! let value = 42u32; @@ -59,7 +59,7 @@ //! The [`EncodeInto`] trait provides efficient varint encoding: //! //! ```rust -//! use multitrait::EncodeInto; +//! use multi_trait::EncodeInto; //! //! // Small values use fewer bytes //! assert_eq!(0u8.encode_into(), vec![0]); @@ -77,7 +77,7 @@ //! The [`TryDecodeFrom`] trait enables zero-copy parsing with error handling: //! //! ```rust -//! use multitrait::TryDecodeFrom; +//! use multi_trait::TryDecodeFrom; //! //! // Decode from byte slice //! let bytes = vec![0xFF, 0xFF, 0x03]; // Varint encoding of 65535 @@ -96,7 +96,7 @@ //! Define sentinel/null values for custom types: //! //! ```rust -//! use multitrait::Null; +//! use multi_trait::Null; //! //! struct MyId(u64); //! @@ -122,7 +122,7 @@ //! All decode operations return a [`Result`] with a structured [`Error`] type: //! //! ```rust -//! use multitrait::{TryDecodeFrom, Error}; +//! use multi_trait::{TryDecodeFrom, Error}; //! //! let truncated = vec![0xFF]; // Incomplete varint //! match u16::try_decode_from(&truncated) { @@ -139,7 +139,7 @@ //! The [`EncodeIntoBuffer`] trait enables encoding without allocations: //! //! ```rust -//! use multitrait::EncodeIntoBuffer; +//! use multi_trait::EncodeIntoBuffer; //! //! // Create a reusable buffer //! let mut buffer = Vec::with_capacity(100); @@ -158,7 +158,7 @@ //! The [`EncodeIntoArray`] trait provides stack-only encoding for embedded systems: //! //! ```rust -//! use multitrait::EncodeIntoArray; +//! use multi_trait::EncodeIntoArray; //! //! // Encode to stack-allocated array (no heap) //! let (array, len) = 42u8.encode_into_array(); @@ -174,7 +174,7 @@ //! represent valid varint encodings: //! //! ```rust -//! use multitrait::EncodedBytes; +//! use multi_trait::EncodedBytes; //! //! // Validation happens at construction //! let valid = vec![42u8]; @@ -219,7 +219,7 @@ //! The [`EncodedBytes`] newtype is explicitly marked as `Send + Sync`: //! //! ```rust -//! use multitrait::EncodedBytes; +//! use multi_trait::EncodedBytes; //! use std::sync::Arc; //! use std::thread; //! @@ -783,7 +783,7 @@ mod test { // ======================================================================== /// Compile-time verification that Error is Send + Sync - #[allow(dead_code)] + #[test] fn assert_error_send_sync() { fn is_send() {} fn is_sync() {} @@ -792,7 +792,7 @@ mod test { } /// Compile-time verification that EncodedBytes is Send + Sync - #[allow(dead_code)] + #[test] fn assert_encoded_bytes_send_sync() { fn is_send() {} fn is_sync() {} @@ -1094,7 +1094,7 @@ mod test { // ======================================================================== /// Verify that primitive types used with traits are Send + Sync - #[allow(dead_code)] + #[test] fn assert_primitives_send_sync() { fn is_send() {} fn is_sync() {} @@ -1121,7 +1121,7 @@ mod test { } /// Verify that encoded data types are Send + Sync - #[allow(dead_code)] + #[test] fn assert_encoded_types_send_sync() { fn is_send() {} fn is_sync() {} diff --git a/src/null.rs b/src/null.rs index b72d550..b12a8ed 100644 --- a/src/null.rs +++ b/src/null.rs @@ -20,7 +20,7 @@ /// # Examples /// /// ```rust -/// use multitrait::Null; +/// use multi_trait::Null; /// /// #[derive(Debug, PartialEq)] /// struct UserId(u64); @@ -57,7 +57,7 @@ pub trait Null { /// # Examples /// /// ```rust - /// use multitrait::Null; + /// use multi_trait::Null; /// /// # struct ResourceId(u32); /// # impl Null for ResourceId { @@ -77,7 +77,7 @@ pub trait Null { /// # Examples /// /// ```rust - /// use multitrait::Null; + /// use multi_trait::Null; /// /// # struct Counter(i32); /// # impl Null for Counter { @@ -115,7 +115,7 @@ pub trait Null { /// # Examples /// /// ```rust -/// use multitrait::TryNull; +/// use multi_trait::TryNull; /// /// #[derive(Debug)] /// struct BufferId(Vec); @@ -163,7 +163,7 @@ pub trait TryNull: Sized { /// # Examples /// /// ```rust - /// use multitrait::TryNull; + /// use multi_trait::TryNull; /// /// # struct ValidatedId(String); /// # impl TryNull for ValidatedId { @@ -187,7 +187,7 @@ pub trait TryNull: Sized { /// # Examples /// /// ```rust - /// use multitrait::TryNull; + /// use multi_trait::TryNull; /// /// # struct OptionalData(Option>); /// # impl TryNull for OptionalData { diff --git a/src/try_decode_from.rs b/src/try_decode_from.rs index 81a1772..1c264de 100644 --- a/src/try_decode_from.rs +++ b/src/try_decode_from.rs @@ -40,7 +40,7 @@ use unsigned_varint::decode; /// ## Basic Decoding /// /// ```rust -/// use multitrait::TryDecodeFrom; +/// use multi_trait::TryDecodeFrom; /// /// // Decode a single value /// let bytes = vec![42]; @@ -52,7 +52,7 @@ use unsigned_varint::decode; /// ## Sequential Decoding /// /// ```rust -/// use multitrait::TryDecodeFrom; +/// use multi_trait::TryDecodeFrom; /// /// // Decode multiple values from one buffer /// let bytes = vec![0x01, 0x02, 0x03]; @@ -69,7 +69,7 @@ use unsigned_varint::decode; /// ## Error Handling /// /// ```rust -/// use multitrait::{TryDecodeFrom, Error}; +/// use multi_trait::{TryDecodeFrom, Error}; /// /// // Handle decode errors /// let empty: &[u8] = &[]; @@ -112,7 +112,7 @@ pub trait TryDecodeFrom<'a>: Sized { /// # Examples /// /// ```rust - /// use multitrait::TryDecodeFrom; + /// use multi_trait::TryDecodeFrom; /// /// let bytes = vec![0xFF, 0x01]; // Varint encoding of 255 /// let (value, remaining) = u8::try_decode_from(&bytes).unwrap(); @@ -176,9 +176,15 @@ impl<'a> TryDecodeFrom<'a> for bool { fn try_decode_from(bytes: &'a [u8]) -> Result<(Self, &'a [u8]), Self::Error> { let (v, ptr) = decode::u8(bytes).map_err(|source| { #[cfg(feature = "std")] - { Self::Error::UnsignedVarintDecode { source } } + { + Self::Error::UnsignedVarintDecode { source } + } #[cfg(not(feature = "std"))] - { Self::Error::UnsignedVarintDecode { message: alloc::format!("{:?}", source) } } + { + Self::Error::UnsignedVarintDecode { + message: alloc::format!("{:?}", source), + } + } })?; Ok(((v != 0), ptr)) } @@ -193,3 +199,23 @@ impl_try_decode_from! { u128 => u128; usize => usize; } + +/// Decode a fixed-length byte array (reads N bytes; used for BLS share identifiers). +impl<'a, const N: usize> TryDecodeFrom<'a> for [u8; N] { + type Error = Error; + + fn try_decode_from(bytes: &'a [u8]) -> Result<([u8; N], &'a [u8]), Self::Error> { + if bytes.len() < N { + return Err(Error::InsufficientData { + expected: N, + actual: bytes.len(), + }); + } + let (head, rest) = bytes.split_at(N); + let arr = <[u8; N]>::try_from(head).map_err(|_| Error::InsufficientData { + expected: N, + actual: bytes.len(), + })?; + Ok((arr, rest)) + } +}