multi_trait/enc_into_buffer.rs
1// SPDX-License-Identifier: Apache-2.0
2//! Buffer-based encoding trait for zero-allocation varint encoding.
3//!
4//! This module provides the [`EncodeIntoBuffer`] trait, which allows encoding
5//! values into an existing buffer without allocating. This is useful for
6//! performance-critical code paths where allocations should be minimized.
7//!
8//! # Performance Benefits
9//!
10//! Using `EncodeIntoBuffer` instead of [`EncodeInto`](crate::EncodeInto) provides:
11//! - **Zero allocations**: Reuses existing buffer capacity
12//! - **Better cache locality**: Fewer heap operations
13//! - **Batch encoding**: Encode multiple values into one buffer
14//! - **Predictable performance**: No allocation pauses
15//!
16//! # Use Cases
17//!
18//! - Hot paths in serialization code
19//! - Encoding multiple values sequentially
20//! - Systems with allocation constraints
21//! - Performance-sensitive applications
22//!
23//! # Examples
24//!
25//! ## Single value encoding
26//!
27//! ```rust
28//! use multi_trait::EncodeIntoBuffer;
29//!
30//! let mut buffer = Vec::new();
31//! 42u8.encode_into_buffer(&mut buffer);
32//! assert_eq!(buffer, vec![42]);
33//! ```
34//!
35//! ## Sequential encoding (multiple values)
36//!
37//! ```rust
38//! use multi_trait::EncodeIntoBuffer;
39//!
40//! let mut buffer = Vec::new();
41//! 42u8.encode_into_buffer(&mut buffer);
42//! 1000u16.encode_into_buffer(&mut buffer);
43//! 100000u32.encode_into_buffer(&mut buffer);
44//!
45//! // All three values encoded in one buffer with a single allocation
46//! assert!(buffer.len() > 3);
47//! ```
48//!
49//! ## Buffer reuse
50//!
51//! ```rust
52//! use multi_trait::EncodeIntoBuffer;
53//!
54//! let mut buffer = Vec::with_capacity(100);
55//! for i in 0u8..10 {
56//! i.encode_into_buffer(&mut buffer);
57//! }
58//! // Buffer was reused for all 10 values with no additional allocations
59//! assert_eq!(buffer.len(), 10);
60//! ```
61
62#[cfg(not(feature = "std"))]
63use alloc::vec::Vec;
64
65use unsigned_varint::encode;
66
67/// Trait for encoding values into an existing buffer.
68///
69/// This trait provides zero-allocation encoding by appending to an existing
70/// `Vec<u8>`. This is more efficient than [`EncodeInto`](crate::EncodeInto)
71/// when:
72/// - Encoding multiple values sequentially
73/// - Working in allocation-sensitive contexts
74/// - Reusing buffers across multiple operations
75///
76/// # Performance
77///
78/// This trait is optimized for minimal overhead:
79/// - No heap allocations (unless buffer needs to grow)
80/// - Direct buffer manipulation
81/// - Inline hints for hot paths
82///
83/// # Thread Safety
84///
85/// This trait is `Send + Sync` safe. All implementations are stateless and can
86/// be called concurrently from multiple threads.
87///
88/// # Examples
89///
90/// ```rust
91/// use multi_trait::EncodeIntoBuffer;
92///
93/// // Create a reusable buffer
94/// let mut buffer = Vec::with_capacity(64);
95///
96/// // Encode values without allocation
97/// 42u8.encode_into_buffer(&mut buffer);
98/// 1000u16.encode_into_buffer(&mut buffer);
99///
100/// // Buffer contains both encoded values
101/// assert!(buffer.len() >= 2);
102/// ```
103///
104/// # Implemented For
105///
106/// - `bool`: Encoded as 0 (false) or 1 (true)
107/// - `u8`, `u16`, `u32`, `u64`, `u128`: Variable-length encoding
108/// - `usize`: Platform-dependent (32-bit or 64-bit)
109pub trait EncodeIntoBuffer {
110 /// Encode this value and append it to the given buffer.
111 ///
112 /// The buffer will be extended with the varint-encoded bytes representing
113 /// this value. The buffer's existing contents are preserved.
114 ///
115 /// # Performance
116 ///
117 /// This method is optimized to minimize allocations. If the buffer has
118 /// sufficient capacity, no allocation occurs. Otherwise, the buffer will
119 /// grow according to Vec's growth strategy.
120 ///
121 /// # Examples
122 ///
123 /// ```rust
124 /// use multi_trait::EncodeIntoBuffer;
125 ///
126 /// let mut buffer = Vec::new();
127 /// 42u8.encode_into_buffer(&mut buffer);
128 /// assert_eq!(buffer, vec![42]);
129 ///
130 /// // Append another value
131 /// 100u8.encode_into_buffer(&mut buffer);
132 /// assert_eq!(buffer.len(), 2);
133 /// ```
134 fn encode_into_buffer(&self, buffer: &mut Vec<u8>);
135}
136
137/// Macro to implement `EncodeIntoBuffer` for unsigned integer types using varint encoding.
138///
139/// This macro eliminates code duplication by generating identical implementations
140/// for different numeric types. Each implementation:
141/// 1. Creates an appropriate temporary buffer for the type
142/// 2. Encodes the value into the temporary buffer
143/// 3. Finds the encoded length
144/// 4. Extends the target buffer with the encoded bytes
145///
146/// # Usage
147///
148/// ```text
149/// impl_encode_into_buffer! {
150/// u8 => u8_buffer, u8;
151/// u16 => u16_buffer, u16;
152/// }
153/// ```
154///
155/// # Hygiene
156///
157/// This macro uses fully qualified paths to ensure proper hygiene and avoid
158/// namespace collisions with user code.
159macro_rules! impl_encode_into_buffer {
160 ($($type:ty => $buffer_fn:ident, $encode_fn:ident);+ $(;)?) => {
161 $(
162 #[doc = concat!("Encode a ", stringify!($type), " and append to a buffer")]
163 impl EncodeIntoBuffer for $type {
164 #[inline]
165 fn encode_into_buffer(&self, buffer: &mut Vec<u8>) {
166 // Create temporary buffer for this type
167 let mut buf = encode::$buffer_fn();
168
169 // Encode value into temporary buffer
170 let encoded = encode::$encode_fn(*self, &mut buf);
171
172 // Extend the target buffer with the encoded bytes
173 buffer.extend_from_slice(encoded);
174 }
175 }
176 )+
177 };
178}
179
180/// Encode a bool and append to a buffer
181impl EncodeIntoBuffer for bool {
182 #[inline]
183 fn encode_into_buffer(&self, buffer: &mut Vec<u8>) {
184 buffer.push(u8::from(*self));
185 }
186}
187
188// Implement EncodeIntoBuffer for all unsigned integer types using the macro
189impl_encode_into_buffer! {
190 u8 => u8_buffer, u8;
191 u16 => u16_buffer, u16;
192 u32 => u32_buffer, u32;
193 u64 => u64_buffer, u64;
194 u128 => u128_buffer, u128;
195 usize => usize_buffer, usize;
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201
202 #[test]
203 fn test_buffer_single_value() {
204 let mut buffer = Vec::new();
205 42u8.encode_into_buffer(&mut buffer);
206 assert_eq!(buffer, vec![42]);
207 }
208
209 #[test]
210 fn test_buffer_multiple_values() {
211 let mut buffer = Vec::new();
212 42u8.encode_into_buffer(&mut buffer);
213 100u8.encode_into_buffer(&mut buffer);
214 200u8.encode_into_buffer(&mut buffer);
215 // 42 = 1 byte, 100 = 1 byte, 200 = 2 bytes (>127 requires continuation)
216 assert_eq!(buffer.len(), 4);
217 }
218
219 #[test]
220 fn test_buffer_sequential_encoding() {
221 let mut buffer = Vec::new();
222 42u8.encode_into_buffer(&mut buffer);
223 1000u16.encode_into_buffer(&mut buffer);
224 100_000_u32.encode_into_buffer(&mut buffer);
225 // Verify buffer contains all three values
226 assert!(buffer.len() > 3);
227 }
228
229 #[test]
230 fn test_buffer_preserves_existing_content() {
231 let mut buffer = vec![0xFF, 0xEE];
232 42u8.encode_into_buffer(&mut buffer);
233 assert_eq!(buffer[0], 0xFF);
234 assert_eq!(buffer[1], 0xEE);
235 assert_eq!(buffer[2], 42);
236 }
237
238 #[test]
239 fn test_buffer_bool() {
240 let mut buffer = Vec::new();
241 true.encode_into_buffer(&mut buffer);
242 false.encode_into_buffer(&mut buffer);
243 assert_eq!(buffer, vec![1, 0]);
244 }
245
246 #[test]
247 fn test_buffer_reuse() {
248 let mut buffer = Vec::with_capacity(100);
249 for i in 0u8..10 {
250 i.encode_into_buffer(&mut buffer);
251 }
252 assert_eq!(buffer.len(), 10);
253 // Verify no reallocation occurred
254 assert!(buffer.capacity() >= 100);
255 }
256
257 #[test]
258 fn test_buffer_large_values() {
259 let mut buffer = Vec::new();
260 u8::MAX.encode_into_buffer(&mut buffer);
261 u16::MAX.encode_into_buffer(&mut buffer);
262 u32::MAX.encode_into_buffer(&mut buffer);
263 // Each max value takes multiple bytes
264 assert!(buffer.len() > 3);
265 }
266
267 #[test]
268 fn test_buffer_zero_values() {
269 let mut buffer = Vec::new();
270 0u8.encode_into_buffer(&mut buffer);
271 0u16.encode_into_buffer(&mut buffer);
272 0u32.encode_into_buffer(&mut buffer);
273 // Zero values take 1 byte each
274 assert_eq!(buffer, vec![0, 0, 0]);
275 }
276}