redoubt_codec_core/collections/
allocked_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 redoubt_alloc::AllockedVec;
6use redoubt_zero::{FastZeroizable, ZeroizationProbe, ZeroizeMetadata};
7
8use crate::zeroizing::Zeroizing;
9
10use crate::codec_buffer::RedoubtCodecBuffer;
11use crate::error::{DecodeError, EncodeError, OverflowError};
12use crate::traits::{
13    BytesRequired, Decode, DecodeSlice, Encode, EncodeSlice, PreAlloc, TryDecode, TryEncode,
14};
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#[cold]
20#[inline(never)]
21fn cleanup_encode_error<T>(vec: &mut AllockedVec<T>, buf: &mut RedoubtCodecBuffer)
22where
23    T: FastZeroizable + ZeroizeMetadata + ZeroizationProbe,
24{
25    vec.fast_zeroize();
26    buf.fast_zeroize();
27}
28
29/// Cleanup function for decode errors. Marked #[cold] to keep it out of the hot path.
30#[cold]
31#[inline(never)]
32fn cleanup_decode_error<T>(vec: &mut AllockedVec<T>, buf: &mut &mut [u8])
33where
34    T: FastZeroizable + ZeroizeMetadata + ZeroizationProbe,
35{
36    vec.fast_zeroize();
37    redoubt_util::fast_zeroize_slice(buf);
38}
39
40impl<T> BytesRequired for AllockedVec<T>
41where
42    T: FastZeroizable + ZeroizeMetadata + BytesRequired + ZeroizationProbe,
43{
44    fn encode_bytes_required(&self) -> Result<usize, OverflowError> {
45        let mut bytes_required = header_size();
46
47        for elem in self.as_slice().iter() {
48            let new_bytes_required = bytes_required.wrapping_add(elem.encode_bytes_required()?);
49
50            if new_bytes_required < bytes_required {
51                return Err(OverflowError {
52                    reason: "AllockedVec bytes_required overflow".into(),
53                });
54            }
55
56            bytes_required = new_bytes_required;
57        }
58
59        Ok(bytes_required)
60    }
61}
62
63impl<T> TryEncode for AllockedVec<T>
64where
65    T: FastZeroizable + ZeroizeMetadata + EncodeSlice + BytesRequired + ZeroizationProbe,
66{
67    fn try_encode_into(&mut self, buf: &mut RedoubtCodecBuffer) -> Result<(), EncodeError> {
68        let mut size = Zeroizing::from(&mut self.len());
69        let mut bytes_required = Zeroizing::from(&mut self.encode_bytes_required()?);
70
71        write_header(buf, &mut size, &mut bytes_required)?;
72
73        T::encode_slice_into(self.as_mut_slice(), buf)
74    }
75}
76
77impl<T> Encode for AllockedVec<T>
78where
79    T: FastZeroizable + ZeroizeMetadata + EncodeSlice + BytesRequired + ZeroizationProbe,
80{
81    #[inline(always)]
82    fn encode_into(&mut self, buf: &mut RedoubtCodecBuffer) -> Result<(), EncodeError> {
83        let result = self.try_encode_into(buf);
84
85        if result.is_err() {
86            cleanup_encode_error(self, buf);
87        } else {
88            self.fast_zeroize();
89        }
90
91        result
92    }
93}
94
95impl<T> EncodeSlice for AllockedVec<T>
96where
97    T: FastZeroizable + ZeroizeMetadata + EncodeSlice + BytesRequired + ZeroizationProbe,
98{
99    fn encode_slice_into(
100        slice: &mut [Self],
101        buf: &mut RedoubtCodecBuffer,
102    ) -> Result<(), EncodeError> {
103        for elem in slice.iter_mut() {
104            elem.encode_into(buf)?;
105        }
106
107        Ok(())
108    }
109}
110
111impl<T> TryDecode for AllockedVec<T>
112where
113    T: DecodeSlice + FastZeroizable + ZeroizeMetadata + ZeroizationProbe + Default,
114{
115    #[inline(always)]
116    fn try_decode_from(&mut self, buf: &mut &mut [u8]) -> Result<(), DecodeError> {
117        let mut size = Zeroizing::from(&mut 0usize);
118
119        process_header(buf, &mut size)?;
120
121        self.prealloc(*size);
122
123        T::decode_slice_from(self.as_mut_slice(), buf)
124    }
125}
126
127impl<T> Decode for AllockedVec<T>
128where
129    T: DecodeSlice + FastZeroizable + ZeroizeMetadata + ZeroizationProbe + Default,
130{
131    fn decode_from(&mut self, buf: &mut &mut [u8]) -> Result<(), DecodeError> {
132        let result = self.try_decode_from(buf);
133
134        if result.is_err() {
135            cleanup_decode_error(self, buf);
136        }
137
138        result
139    }
140}
141
142impl<T> DecodeSlice for AllockedVec<T>
143where
144    T: DecodeSlice + FastZeroizable + ZeroizeMetadata + ZeroizationProbe + Default,
145{
146    fn decode_slice_from(slice: &mut [Self], buf: &mut &mut [u8]) -> Result<(), DecodeError> {
147        for elem in slice.iter_mut() {
148            elem.decode_from(buf)?;
149        }
150
151        Ok(())
152    }
153}
154
155impl<T> PreAlloc for AllockedVec<T>
156where
157    T: FastZeroizable + ZeroizeMetadata + ZeroizationProbe + Default,
158{
159    const ZERO_INIT: bool = false;
160
161    fn prealloc(&mut self, size: usize) {
162        self.fast_zeroize();
163        self.realloc_with_capacity(size);
164        self.fill_with_default();
165
166        unsafe {
167            self.set_len(size);
168        }
169
170        debug_assert_eq!(self.len(), size);
171    }
172}