redoubt_codec_core/collections/
vec.rs

1// Copyright (c) 2025-2026 Federico Hoerth <memparanoid@gmail.com>
2// SPDX-License-Identifier: GPL-3.0-only
3// See LICENSE in the repository root for full license text.
4
5use alloc::vec::Vec;
6
7use redoubt_zero::{FastZeroizable, ZeroizeMetadata};
8
9use crate::codec_buffer::RedoubtCodecBuffer;
10use crate::error::{DecodeError, EncodeError, OverflowError};
11use crate::traits::{
12    BytesRequired, Decode, DecodeSlice, Encode, EncodeSlice, PreAlloc, TryDecode, TryEncode,
13};
14use crate::zeroizing::Zeroizing;
15
16use super::helpers::{header_size, process_header, write_header};
17
18/// Cleanup function for encode errors. Marked #[cold] to keep it out of the hot path.
19#[cfg(feature = "zeroize")]
20#[cold]
21#[inline(never)]
22fn cleanup_encode_error<T: FastZeroizable + ZeroizeMetadata>(
23    vec: &mut Vec<T>,
24    buf: &mut RedoubtCodecBuffer,
25) {
26    vec.fast_zeroize();
27    buf.fast_zeroize();
28}
29
30/// Cleanup function for decode errors. Marked #[cold] to keep it out of the hot path.
31#[cfg(feature = "zeroize")]
32#[cold]
33#[inline(never)]
34fn cleanup_decode_error<T: FastZeroizable + ZeroizeMetadata>(
35    vec: &mut Vec<T>,
36    buf: &mut &mut [u8],
37) {
38    vec.fast_zeroize();
39    buf.fast_zeroize();
40}
41
42impl<T> BytesRequired for Vec<T>
43where
44    T: BytesRequired,
45{
46    fn encode_bytes_required(&self) -> Result<usize, OverflowError> {
47        let mut bytes_required = header_size();
48
49        for elem in self.iter() {
50            let new_bytes_required = bytes_required.wrapping_add(elem.encode_bytes_required()?);
51
52            if new_bytes_required < bytes_required {
53                return Err(OverflowError {
54                    reason: "Vec::encode_bytes_required overflow".into(),
55                });
56            }
57
58            bytes_required = new_bytes_required;
59        }
60
61        Ok(bytes_required)
62    }
63}
64
65impl<T> TryEncode for Vec<T>
66where
67    T: EncodeSlice + BytesRequired + FastZeroizable + ZeroizeMetadata,
68{
69    fn try_encode_into(&mut self, buf: &mut RedoubtCodecBuffer) -> Result<(), EncodeError> {
70        let mut size = Zeroizing::from(&mut self.len());
71        let mut bytes_required = Zeroizing::from(&mut self.encode_bytes_required()?);
72
73        write_header(buf, &mut size, &mut bytes_required)?;
74
75        T::encode_slice_into(self.as_mut_slice(), buf)
76    }
77}
78
79impl<T> Encode for Vec<T>
80where
81    T: EncodeSlice + BytesRequired + FastZeroizable + ZeroizeMetadata,
82{
83    #[inline(always)]
84    fn encode_into(&mut self, buf: &mut RedoubtCodecBuffer) -> Result<(), EncodeError> {
85        let result = self.try_encode_into(buf);
86
87        #[cfg(feature = "zeroize")]
88        if result.is_err() {
89            cleanup_encode_error(self, buf);
90        } else {
91            self.fast_zeroize();
92        }
93
94        result
95    }
96}
97
98impl<T> EncodeSlice for Vec<T>
99where
100    T: EncodeSlice + BytesRequired + FastZeroizable + ZeroizeMetadata,
101{
102    #[inline(always)]
103    fn encode_slice_into(
104        slice: &mut [Self],
105        buf: &mut RedoubtCodecBuffer,
106    ) -> Result<(), EncodeError> {
107        for elem in slice.iter_mut() {
108            elem.encode_into(buf)?;
109        }
110
111        Ok(())
112    }
113}
114
115impl<T> TryDecode for Vec<T>
116where
117    T: DecodeSlice + PreAlloc + FastZeroizable + ZeroizeMetadata,
118{
119    #[inline(always)]
120    fn try_decode_from(&mut self, buf: &mut &mut [u8]) -> Result<(), DecodeError> {
121        let mut size = Zeroizing::from(&mut 0);
122
123        process_header(buf, &mut size)?;
124
125        self.prealloc(*size);
126
127        T::decode_slice_from(self.as_mut_slice(), buf)
128    }
129}
130
131impl<T> Decode for Vec<T>
132where
133    T: DecodeSlice + PreAlloc + FastZeroizable + ZeroizeMetadata,
134{
135    #[inline(always)]
136    fn decode_from(&mut self, buf: &mut &mut [u8]) -> Result<(), DecodeError> {
137        let result = self.try_decode_from(buf);
138
139        #[cfg(feature = "zeroize")]
140        if result.is_err() {
141            cleanup_decode_error(self, buf);
142        }
143
144        result
145    }
146}
147
148impl<T> DecodeSlice for Vec<T>
149where
150    T: DecodeSlice + PreAlloc + FastZeroizable + ZeroizeMetadata,
151{
152    #[inline(always)]
153    fn decode_slice_from(slice: &mut [Self], buf: &mut &mut [u8]) -> Result<(), DecodeError> {
154        for elem in slice.iter_mut() {
155            elem.decode_from(buf)?;
156        }
157
158        Ok(())
159    }
160}
161
162#[inline(always)]
163pub(crate) fn vec_prealloc<T: PreAlloc + FastZeroizable + ZeroizeMetadata>(
164    vec: &mut Vec<T>,
165    size: usize,
166    zero_init: bool,
167) {
168    vec.shrink_to_fit();
169    vec.reserve_exact(size);
170
171    if zero_init {
172        vec.fast_zeroize();
173        unsafe { vec.set_len(size) };
174    } else {
175        vec.resize_with(size, Default::default);
176        vec.fast_zeroize();
177    }
178}
179
180impl<T: PreAlloc + FastZeroizable + ZeroizeMetadata> PreAlloc for Vec<T> {
181    /// Vec can NEVER be zero-initialized (has ptr/len/capacity).
182    const ZERO_INIT: bool = false;
183
184    #[inline(always)]
185    fn prealloc(&mut self, size: usize) {
186        vec_prealloc(self, size, T::ZERO_INIT);
187    }
188}