inout/reserved.rs
1use crate::{InOutBuf, errors::OutIsTooSmallError};
2use core::{marker::PhantomData, slice};
3
4#[cfg(feature = "block-padding")]
5use {
6 crate::{InOut, errors::PadError},
7 block_padding::Padding,
8 hybrid_array::{Array, ArraySize},
9};
10
11/// Custom slice type which references one immutable (input) slice and one
12/// mutable (output) slice. Input and output slices are either the same or
13/// do not overlap. Length of the output slice is always equal or bigger than
14/// length of the input slice.
15pub struct InOutBufReserved<'inp, 'out, T> {
16 in_ptr: *const T,
17 out_ptr: *mut T,
18 in_len: usize,
19 out_len: usize,
20 _pd: PhantomData<(&'inp T, &'out mut T)>,
21}
22
23impl<'a, T> InOutBufReserved<'a, 'a, T> {
24 /// Crate [`InOutBufReserved`] from a single mutable slice.
25 ///
26 /// # Errors
27 /// If `out` is too small.
28 pub fn from_mut_slice(buf: &'a mut [T], msg_len: usize) -> Result<Self, OutIsTooSmallError> {
29 if msg_len > buf.len() {
30 return Err(OutIsTooSmallError);
31 }
32 let p = buf.as_mut_ptr();
33 let out_len = buf.len();
34 Ok(Self {
35 in_ptr: p,
36 out_ptr: p,
37 in_len: msg_len,
38 out_len,
39 _pd: PhantomData,
40 })
41 }
42}
43
44impl<T> InOutBufReserved<'_, '_, T> {
45 /// Create [`InOutBufReserved`] from raw input and output pointers.
46 ///
47 /// # Safety
48 /// Behavior is undefined if any of the following conditions are violated:
49 /// - `in_ptr` must point to a properly initialized value of type `T` and
50 /// must be valid for reads for `in_len * mem::size_of::<T>()` many bytes.
51 /// - `out_ptr` must point to a properly initialized value of type `T` and
52 /// must be valid for both reads and writes for `out_len * mem::size_of::<T>()`
53 /// many bytes.
54 /// - `in_ptr` and `out_ptr` must be either equal or non-overlapping.
55 /// - `in_len` must be less than or equal to `out_len` (i.e. `in_len <= out_len`).
56 /// - If `in_ptr` and `out_ptr` are equal, then the memory referenced by
57 /// them must not be accessed through any other pointer (not derived from
58 /// the return value) for the duration of lifetime 'a. Both read and write
59 /// accesses are forbidden.
60 /// - If `in_ptr` and `out_ptr` are not equal, then the memory referenced by
61 /// `out_ptr` must not be accessed through any other pointer (not derived from
62 /// the return value) for the duration of lifetime 'a. Both read and write
63 /// accesses are forbidden. The memory referenced by `in_ptr` must not be
64 /// mutated for the duration of lifetime `'a`, except inside an `UnsafeCell`.
65 /// - The total size `in_len * mem::size_of::<T>()` and
66 /// `out_len * mem::size_of::<T>()` must be no larger than `isize::MAX`.
67 #[inline(always)]
68 pub unsafe fn from_raw(
69 in_ptr: *const T,
70 in_len: usize,
71 out_ptr: *mut T,
72 out_len: usize,
73 ) -> Self {
74 Self {
75 in_ptr,
76 out_ptr,
77 in_len,
78 out_len,
79 _pd: PhantomData,
80 }
81 }
82
83 /// Get raw input and output pointers.
84 #[inline(always)]
85 #[must_use]
86 pub fn into_raw(self) -> (*const T, *mut T) {
87 (self.in_ptr, self.out_ptr)
88 }
89
90 /// Get input buffer length.
91 #[inline(always)]
92 #[must_use]
93 pub fn get_in_len(&self) -> usize {
94 self.in_len
95 }
96
97 /// Get output buffer length.
98 #[inline(always)]
99 #[must_use]
100 pub fn get_out_len(&self) -> usize {
101 self.out_len
102 }
103
104 /// Split buffer into `InOutBuf` with input length and mutable slice pointing to
105 /// the remaining reserved suffix.
106 pub fn split_reserved(&mut self) -> (InOutBuf<'_, '_, T>, &mut [T]) {
107 let in_len = self.get_in_len();
108 let out_len = self.get_out_len();
109 let in_ptr = self.get_in().as_ptr();
110 let out_ptr = self.get_out().as_mut_ptr();
111 // This never underflows because the type ensures that `out_len` is
112 // bigger or equal to `in_len`.
113 let tail_len = out_len - in_len;
114 unsafe {
115 let body = InOutBuf::from_raw(in_ptr, out_ptr, in_len);
116 let tail = slice::from_raw_parts_mut(out_ptr.add(in_len), tail_len);
117 (body, tail)
118 }
119 }
120}
121
122impl<'inp, 'out, T> InOutBufReserved<'inp, 'out, T> {
123 /// Crate [`InOutBufReserved`] from two separate slices.
124 ///
125 /// # Errors
126 /// If `out` is too small.
127 pub fn from_slices(
128 in_buf: &'inp [T],
129 out_buf: &'out mut [T],
130 ) -> Result<Self, OutIsTooSmallError> {
131 if in_buf.len() > out_buf.len() {
132 return Err(OutIsTooSmallError);
133 }
134 Ok(Self {
135 in_ptr: in_buf.as_ptr(),
136 out_ptr: out_buf.as_mut_ptr(),
137 in_len: in_buf.len(),
138 out_len: out_buf.len(),
139 _pd: PhantomData,
140 })
141 }
142
143 /// Get input slice.
144 #[inline(always)]
145 #[must_use]
146 pub fn get_in(&self) -> &[T] {
147 unsafe { slice::from_raw_parts(self.in_ptr, self.in_len) }
148 }
149
150 /// Get output slice.
151 #[inline(always)]
152 pub fn get_out(&mut self) -> &mut [T] {
153 unsafe { slice::from_raw_parts_mut(self.out_ptr, self.out_len) }
154 }
155
156 /// Consume `self` and get output slice with lifetime `'out`.
157 #[inline(always)]
158 #[must_use]
159 pub fn into_out(self) -> &'out mut [T] {
160 unsafe { slice::from_raw_parts_mut(self.out_ptr, self.out_len) }
161 }
162}
163
164#[cfg(feature = "block-padding")]
165impl<'inp, 'out> InOutBufReserved<'inp, 'out, u8> {
166 /// Transform buffer into [`PaddedInOutBuf`] using padding algorithm `P`.
167 ///
168 /// # Errors
169 /// If the padding is invalid
170 #[inline(always)]
171 pub fn into_padded_blocks<P, BS>(self) -> Result<PaddedInOutBuf<'inp, 'out, BS>, PadError>
172 where
173 P: Padding,
174 BS: ArraySize,
175 {
176 use block_padding::PaddedData;
177 let out_blocks_len = self.out_len / BS::USIZE;
178
179 let (blocks, tail_block) = match P::pad_detached(self.get_in()) {
180 PaddedData::Pad { blocks, tail_block } => {
181 // If tail block is present, the output buffer must contain
182 // at least `blocks.len() + 1` full blocks
183 if out_blocks_len <= blocks.len() {
184 return Err(PadError);
185 }
186 (blocks, Some(tail_block))
187 }
188 PaddedData::NoPad { blocks } => {
189 // If tail block is not present, the output buffer must contain
190 // at least `blocks.len()` full blocks
191 if out_blocks_len < blocks.len() {
192 return Err(PadError);
193 }
194 (blocks, None)
195 }
196 PaddedData::Error => return Err(PadError),
197 };
198
199 // These invariants must hold for all valid padding implementations.
200 if blocks.as_ptr().cast() != self.in_ptr {
201 return Err(PadError);
202 }
203 if blocks.len() != self.in_len / BS::USIZE {
204 return Err(PadError);
205 }
206
207 Ok(PaddedInOutBuf {
208 in_ptr: blocks.as_ptr(),
209 out_ptr: self.out_ptr.cast(),
210 blocks_len: blocks.len(),
211 tail_block,
212 _pd: PhantomData,
213 })
214 }
215}
216
217/// Variant of [`InOutBuf`] with optional padded tail block.
218#[cfg(feature = "block-padding")]
219pub struct PaddedInOutBuf<'inp, 'out, BS: ArraySize> {
220 /// Pointer to the input blocks.
221 in_ptr: *const Array<u8, BS>,
222 /// Pointer to the output blocks (including tail block if present).
223 out_ptr: *mut Array<u8, BS>,
224 /// Number of full blocks (i.e. excluding the tail block if present).
225 blocks_len: usize,
226 /// Padded input tail.
227 tail_block: Option<Array<u8, BS>>,
228 _pd: PhantomData<(&'inp u8, &'out mut u8)>,
229}
230
231#[cfg(feature = "block-padding")]
232impl<'out, BS: ArraySize> PaddedInOutBuf<'_, 'out, BS> {
233 /// Get full blocks.
234 #[inline(always)]
235 pub fn get_blocks(&mut self) -> InOutBuf<'_, '_, Array<u8, BS>> {
236 unsafe { InOutBuf::from_raw(self.in_ptr, self.out_ptr, self.blocks_len) }
237 }
238
239 /// Get padded tail block.
240 ///
241 /// Most padding implementations always return `Some`.
242 #[inline(always)]
243 pub fn get_tail_block(&mut self) -> Option<InOut<'_, '_, Array<u8, BS>>> {
244 // SAFETY: if `tail` is `Some`, then `out_ptr` points to a buffer with length equal
245 // to at least `blocks_len + 1` blocks, so it satisfies the safety conditions
246 // required by `InOut::from_raw`.
247 self.tail_block.as_ref().map(|in_block| unsafe {
248 let tail_out_ptr = self.out_ptr.add(self.blocks_len);
249 InOut::from_raw(in_block, tail_out_ptr)
250 })
251 }
252
253 /// Convert buffer into output slice.
254 #[inline(always)]
255 pub fn into_out(self) -> &'out [u8] {
256 let mut bytes_len = BS::USIZE * self.blocks_len;
257 // If tail is present, add its length to the resulting buffer length.
258 if self.tail_block.is_some() {
259 bytes_len += BS::USIZE;
260 }
261 // SAFETY: `out_ptr` points to a valid memory with size of `bytes_len`
262 unsafe { slice::from_raw_parts(self.out_ptr.cast(), bytes_len) }
263 }
264}