Skip to main content

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}