Skip to main content

multi_trait/
enc_into.rs

1// SPDX-License-Identifier: Apache-2.0
2#[cfg(not(feature = "std"))]
3use alloc::{vec, vec::Vec};
4
5use unsigned_varint::encode;
6
7/// Trait for encoding values into compact varint byte representation.
8///
9/// This trait provides efficient encoding of numeric values into unsigned varint
10/// format, which uses fewer bytes for smaller values. The encoding is deterministic
11/// and reversible via [`TryDecodeFrom`](crate::TryDecodeFrom).
12///
13/// # Varint Format
14///
15/// The varint (variable-length integer) format uses the most significant bit (MSB)
16/// of each byte as a continuation bit:
17/// - If MSB is 1, more bytes follow
18/// - If MSB is 0, this is the last byte
19///
20/// This allows small values to use fewer bytes:
21/// - Values 0-127: 1 byte
22/// - Values 128-16,383: 2 bytes
23/// - And so on...
24///
25/// # Performance
26///
27/// Encoding is optimized for performance:
28/// - Single heap allocation per call
29/// - O(1) length calculation
30/// - No byte-by-byte copying
31///
32/// # Thread Safety
33///
34/// This trait is `Send + Sync` safe. All implementations are stateless and can
35/// be called concurrently from multiple threads.
36///
37/// # Examples
38///
39/// ```rust
40/// use multi_trait::EncodeInto;
41///
42/// // Small values use minimal space
43/// let small = 42u8;
44/// let encoded = small.encode_into();
45/// assert_eq!(encoded.len(), 1);
46///
47/// // Larger values use more bytes as needed
48/// let large = 256u16;
49/// let encoded = large.encode_into();
50/// assert!(encoded.len() > 1);
51///
52/// // Boolean encoding
53/// assert_eq!(true.encode_into(), vec![1]);
54/// assert_eq!(false.encode_into(), vec![0]);
55/// ```
56///
57/// # Implemented For
58///
59/// - `bool`: Encoded as 0 (false) or 1 (true)
60/// - `u8`, `u16`, `u32`, `u64`, `u128`: Variable-length encoding
61/// - `usize`: Platform-dependent (32-bit or 64-bit)
62pub trait EncodeInto {
63    /// Encode this value into a compact varint `Vec<u8>`.
64    ///
65    /// This method allocates a new `Vec` containing the encoded bytes.
66    /// The resulting vector's length depends on the value's magnitude.
67    ///
68    /// # Returns
69    ///
70    /// A `Vec<u8>` containing the varint-encoded representation of this value.
71    ///
72    /// # Examples
73    ///
74    /// ```rust
75    /// use multi_trait::EncodeInto;
76    ///
77    /// let value = 300u16;
78    /// let bytes = value.encode_into();
79    /// // The exact bytes depend on varint encoding rules
80    /// assert!(!bytes.is_empty());
81    /// ```
82    fn encode_into(&self) -> Vec<u8>;
83}
84
85/// Macro to implement `EncodeInto` for unsigned integer types using varint encoding.
86///
87/// This macro eliminates code duplication by generating identical implementations
88/// for different numeric types. Each implementation:
89/// 1. Creates an appropriate buffer for the type
90/// 2. Encodes the value into the buffer
91/// 3. Finds the encoded length by locating the last byte marker
92/// 4. Returns a Vec containing only the encoded bytes
93///
94/// # Usage
95///
96/// ```text
97/// impl_encode_into! {
98///     u8 => u8_buffer, u8;
99///     u16 => u16_buffer, u16;
100/// }
101/// ```
102///
103/// # Hygiene
104///
105/// This macro uses fully qualified paths to ensure proper hygiene and avoid
106/// namespace collisions with user code.
107macro_rules! impl_encode_into {
108    ($($type:ty => $buffer_fn:ident, $encode_fn:ident);+ $(;)?) => {
109        $(
110            #[doc = concat!("Encode a ", stringify!($type), " into a compact varuint `Vec<u8>`")]
111            impl EncodeInto for $type {
112                #[inline]
113                fn encode_into(&self) -> Vec<u8> {
114                    // Create appropriate buffer for this type
115                    let mut buf = encode::$buffer_fn();
116
117                    // Encode value into buffer
118                    let encoded = encode::$encode_fn(*self, &mut buf);
119
120                    // Single allocation: slice and convert to Vec
121                    encoded.to_vec()
122                }
123            }
124        )+
125    };
126}
127
128/// Encode a bool into a compact varuint `Vec<u8>`
129impl EncodeInto for bool {
130    #[inline]
131    fn encode_into(&self) -> Vec<u8> {
132        if *self { vec![1u8] } else { vec![0u8] }
133    }
134}
135
136// Implement EncodeInto for all unsigned integer types using the macro
137impl_encode_into! {
138    u8 => u8_buffer, u8;
139    u16 => u16_buffer, u16;
140    u32 => u32_buffer, u32;
141    u64 => u64_buffer, u64;
142    u128 => u128_buffer, u128;
143    usize => usize_buffer, usize;
144}
145
146/// Encode a fixed-length byte array as raw bytes (used for BLS share identifiers).
147impl<const N: usize> EncodeInto for [u8; N] {
148    #[inline]
149    fn encode_into(&self) -> Vec<u8> {
150        self.as_slice().to_vec()
151    }
152}