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 fn encode_into(&self) -> Vec<u8> {
113 // Create appropriate buffer for this type
114 let mut buf = encode::$buffer_fn();
115
116 // Encode value into buffer
117 let encoded = encode::$encode_fn(*self, &mut buf);
118
119 // Single allocation: slice and convert to Vec
120 encoded.to_vec()
121 }
122 }
123 )+
124 };
125}
126
127/// Encode a bool into a compact varuint `Vec<u8>`
128impl EncodeInto for bool {
129 fn encode_into(&self) -> Vec<u8> {
130 match *self {
131 true => vec![1u8],
132 false => vec![0u8],
133 }
134 }
135}
136
137// Implement EncodeInto for all unsigned integer types using the macro
138impl_encode_into! {
139 u8 => u8_buffer, u8;
140 u16 => u16_buffer, u16;
141 u32 => u32_buffer, u32;
142 u64 => u64_buffer, u64;
143 u128 => u128_buffer, u128;
144 usize => usize_buffer, usize;
145}
146
147/// Encode a fixed-length byte array as raw bytes (used for BLS share identifiers).
148impl<const N: usize> EncodeInto for [u8; N] {
149 fn encode_into(&self) -> Vec<u8> {
150 self.as_slice().to_vec()
151 }
152}