redoubt_codec_core/collections/
option.rs1use redoubt_zero::{FastZeroizable, ZeroizeMetadata};
6
7use crate::codec_buffer::RedoubtCodecBuffer;
8use crate::error::{DecodeError, EncodeError, OverflowError};
9use crate::traits::{BytesRequired, Decode, Encode, TryDecode, TryEncode};
10use crate::zeroizing::Zeroizing;
11
12use super::helpers::{header_size, process_header, write_header};
13
14#[cfg(feature = "zeroize")]
16#[cold]
17#[inline(never)]
18fn cleanup_encode_error<T: FastZeroizable + ZeroizeMetadata>(
19 opt: &mut Option<T>,
20 buf: &mut RedoubtCodecBuffer,
21) {
22 opt.fast_zeroize();
23 buf.fast_zeroize();
24}
25
26#[cfg(feature = "zeroize")]
28#[cold]
29#[inline(never)]
30fn cleanup_decode_error<T: FastZeroizable + ZeroizeMetadata>(
31 opt: &mut Option<T>,
32 buf: &mut &mut [u8],
33) {
34 opt.fast_zeroize();
35 buf.fast_zeroize();
36}
37
38impl<T> BytesRequired for Option<T>
39where
40 T: BytesRequired,
41{
42 fn encode_bytes_required(&self) -> Result<usize, OverflowError> {
43 let header = header_size();
44
45 match self {
46 None => Ok(header),
47 Some(inner) => {
48 let inner_bytes = inner.encode_bytes_required()?;
49 let total = header.wrapping_add(inner_bytes);
50
51 if total < header {
52 return Err(OverflowError {
53 reason: "Option::encode_bytes_required overflow".into(),
54 });
55 }
56
57 Ok(total)
58 }
59 }
60 }
61}
62
63impl<T> TryEncode for Option<T>
64where
65 T: Encode + BytesRequired + FastZeroizable + ZeroizeMetadata,
66{
67 fn try_encode_into(&mut self, buf: &mut RedoubtCodecBuffer) -> Result<(), EncodeError> {
68 let mut bytes_required = Zeroizing::from(&mut self.encode_bytes_required()?);
69
70 match self {
71 None => {
72 let mut size = Zeroizing::from(&mut 0usize);
74 write_header(buf, &mut size, &mut bytes_required)?;
75 }
76 Some(inner) => {
77 let mut size = Zeroizing::from(&mut 1usize);
79 write_header(buf, &mut size, &mut bytes_required)?;
80
81 inner.encode_into(buf)?;
82 }
83 }
84
85 Ok(())
86 }
87}
88
89impl<T> Encode for Option<T>
90where
91 T: Encode + BytesRequired + FastZeroizable + ZeroizeMetadata,
92{
93 #[inline(always)]
94 fn encode_into(&mut self, buf: &mut RedoubtCodecBuffer) -> Result<(), EncodeError> {
95 let result = self.try_encode_into(buf);
96
97 #[cfg(feature = "zeroize")]
98 if result.is_err() {
99 cleanup_encode_error(self, buf);
100 } else {
101 self.fast_zeroize();
102 }
103
104 result
105 }
106}
107
108impl<T> TryDecode for Option<T>
109where
110 T: Decode + Default + FastZeroizable + ZeroizeMetadata,
111{
112 #[inline(always)]
113 fn try_decode_from(&mut self, buf: &mut &mut [u8]) -> Result<(), DecodeError> {
114 let mut size = Zeroizing::from(&mut 0);
115
116 process_header(buf, &mut size)?;
117
118 match *size {
119 0 => {
120 *self = None;
122 }
123 1 => {
124 let mut inner = T::default();
126 inner.decode_from(buf)?;
127 *self = Some(inner);
128 }
129 _ => {
130 return Err(DecodeError::PreconditionViolated);
131 }
132 }
133
134 Ok(())
135 }
136}
137
138impl<T> Decode for Option<T>
139where
140 T: Decode + Default + FastZeroizable + ZeroizeMetadata,
141{
142 #[inline(always)]
143 fn decode_from(&mut self, buf: &mut &mut [u8]) -> Result<(), DecodeError> {
144 let result = self.try_decode_from(buf);
145
146 #[cfg(feature = "zeroize")]
147 if result.is_err() {
148 cleanup_decode_error(self, buf);
149 }
150
151 result
152 }
153}