Skip to main content

inout/
inout_buf.rs

1use crate::{
2    InOut,
3    errors::{IntoArrayError, NotEqualError},
4};
5use core::{marker::PhantomData, ptr, slice};
6use hybrid_array::{Array, ArraySize};
7
8/// Custom slice type which references one immutable (input) slice and one
9/// mutable (output) slice of equal length. Input and output slices are
10/// either the same or do not overlap.
11pub struct InOutBuf<'inp, 'out, T> {
12    pub(crate) in_ptr: *const T,
13    pub(crate) out_ptr: *mut T,
14    pub(crate) len: usize,
15    pub(crate) _pd: PhantomData<(&'inp T, &'out mut T)>,
16}
17
18impl<'a, T> From<&'a mut [T]> for InOutBuf<'a, 'a, T> {
19    #[inline(always)]
20    fn from(buf: &'a mut [T]) -> Self {
21        let p = buf.as_mut_ptr();
22        Self {
23            in_ptr: p,
24            out_ptr: p,
25            len: buf.len(),
26            _pd: PhantomData,
27        }
28    }
29}
30
31impl<'a, T> InOutBuf<'a, 'a, T> {
32    /// Create `InOutBuf` from a single mutable reference.
33    #[inline(always)]
34    pub fn from_mut(val: &'a mut T) -> InOutBuf<'a, 'a, T> {
35        let p = ptr::from_mut(val);
36        Self {
37            in_ptr: p,
38            out_ptr: p,
39            len: 1,
40            _pd: PhantomData,
41        }
42    }
43}
44
45impl<'inp, 'out, T> IntoIterator for InOutBuf<'inp, 'out, T> {
46    type Item = InOut<'inp, 'out, T>;
47    type IntoIter = InOutBufIter<'inp, 'out, T>;
48
49    #[inline(always)]
50    fn into_iter(self) -> Self::IntoIter {
51        InOutBufIter { buf: self, pos: 0 }
52    }
53}
54
55impl<'inp, 'out, T> InOutBuf<'inp, 'out, T> {
56    /// Create `InOutBuf` from a pair of immutable and mutable references.
57    #[inline(always)]
58    pub fn from_ref_mut(in_val: &'inp T, out_val: &'out mut T) -> Self {
59        Self {
60            in_ptr: ptr::from_ref::<T>(in_val),
61            out_ptr: ptr::from_mut::<T>(out_val),
62            len: 1,
63            _pd: PhantomData,
64        }
65    }
66
67    /// Create `InOutBuf` from immutable and mutable slices.
68    ///
69    /// # Errors
70    /// If length of slices is not equal to each other.
71    #[inline(always)]
72    pub fn new(in_buf: &'inp [T], out_buf: &'out mut [T]) -> Result<Self, NotEqualError> {
73        if in_buf.len() != out_buf.len() {
74            Err(NotEqualError)
75        } else {
76            Ok(Self {
77                in_ptr: in_buf.as_ptr(),
78                out_ptr: out_buf.as_mut_ptr(),
79                len: in_buf.len(),
80                _pd: Default::default(),
81            })
82        }
83    }
84
85    /// Get length of the inner buffers.
86    #[inline(always)]
87    #[must_use]
88    pub fn len(&self) -> usize {
89        self.len
90    }
91
92    /// Returns `true` if the buffer has a length of 0.
93    #[inline(always)]
94    #[must_use]
95    pub fn is_empty(&self) -> bool {
96        self.len == 0
97    }
98
99    /// Returns `InOut` for given position.
100    ///
101    /// # Panics
102    /// If `pos` greater or equal to buffer length.
103    #[inline(always)]
104    pub fn get(&mut self, pos: usize) -> InOut<'_, '_, T> {
105        assert!(pos < self.len);
106        unsafe {
107            InOut {
108                in_ptr: self.in_ptr.add(pos),
109                out_ptr: self.out_ptr.add(pos),
110                _pd: PhantomData,
111            }
112        }
113    }
114
115    /// Get input slice.
116    #[inline(always)]
117    #[must_use]
118    pub fn get_in(&self) -> &[T] {
119        unsafe { slice::from_raw_parts(self.in_ptr, self.len) }
120    }
121
122    /// Get output slice.
123    #[inline(always)]
124    pub fn get_out(&mut self) -> &mut [T] {
125        unsafe { slice::from_raw_parts_mut(self.out_ptr, self.len) }
126    }
127
128    /// Consume `self` and get the output slice with lifetime `'out` filled with data from
129    /// the input slice.
130    ///
131    /// In the case if the input and output slices point to the same memory, simply returns
132    /// the output slice. Otherwise, copies data from the former to the latter
133    /// before returning the output slice.
134    #[must_use]
135    pub fn into_out_with_copied_in(self) -> &'out mut [T]
136    where
137        T: Copy,
138    {
139        if !ptr::eq(self.in_ptr, self.out_ptr) {
140            unsafe {
141                ptr::copy(self.in_ptr, self.out_ptr, self.len);
142            }
143        }
144        unsafe { slice::from_raw_parts_mut(self.out_ptr, self.len) }
145    }
146
147    /// Consume `self` and get output slice with lifetime `'out`.
148    #[inline(always)]
149    #[must_use]
150    pub fn into_out(self) -> &'out mut [T] {
151        unsafe { slice::from_raw_parts_mut(self.out_ptr, self.len) }
152    }
153
154    /// Get raw input and output pointers.
155    #[inline(always)]
156    #[must_use]
157    pub fn into_raw(self) -> (*const T, *mut T) {
158        (self.in_ptr, self.out_ptr)
159    }
160
161    /// Reborrow `self`.
162    #[inline(always)]
163    pub fn reborrow(&mut self) -> InOutBuf<'_, '_, T> {
164        Self {
165            in_ptr: self.in_ptr,
166            out_ptr: self.out_ptr,
167            len: self.len,
168            _pd: PhantomData,
169        }
170    }
171
172    /// Create [`InOutBuf`] from raw input and output pointers.
173    ///
174    /// # Safety
175    /// Behavior is undefined if any of the following conditions are violated:
176    /// - `in_ptr` must point to a properly initialized value of type `T` and
177    ///   must be valid for reads for `len * mem::size_of::<T>()` many bytes.
178    /// - `out_ptr` must point to a properly initialized value of type `T` and
179    ///   must be valid for both reads and writes for `len * mem::size_of::<T>()`
180    ///   many bytes.
181    /// - `in_ptr` and `out_ptr` must be either equal or non-overlapping.
182    /// - If `in_ptr` and `out_ptr` are equal, then the memory referenced by
183    ///   them must not be accessed through any other pointer (not derived from
184    ///   the return value) for the duration of lifetime 'a. Both read and write
185    ///   accesses are forbidden.
186    /// - If `in_ptr` and `out_ptr` are not equal, then the memory referenced by
187    ///   `out_ptr` must not be accessed through any other pointer (not derived from
188    ///   the return value) for the duration of lifetime 'a. Both read and write
189    ///   accesses are forbidden. The memory referenced by `in_ptr` must not be
190    ///   mutated for the duration of lifetime `'a`, except inside an `UnsafeCell`.
191    /// - The total size `len * mem::size_of::<T>()`  must be no larger than `isize::MAX`.
192    #[inline(always)]
193    pub unsafe fn from_raw(
194        in_ptr: *const T,
195        out_ptr: *mut T,
196        len: usize,
197    ) -> InOutBuf<'inp, 'out, T> {
198        Self {
199            in_ptr,
200            out_ptr,
201            len,
202            _pd: PhantomData,
203        }
204    }
205
206    /// Divides one buffer into two at `mid` index.
207    ///
208    /// The first will contain all indices from `[0, mid)` (excluding
209    /// the index `mid` itself) and the second will contain all
210    /// indices from `[mid, len)` (excluding the index `len` itself).
211    ///
212    /// # Panics
213    ///
214    /// Panics if `mid > len`.
215    #[inline(always)]
216    #[must_use]
217    pub fn split_at(self, mid: usize) -> (InOutBuf<'inp, 'out, T>, InOutBuf<'inp, 'out, T>) {
218        assert!(mid <= self.len);
219        let (tail_in_ptr, tail_out_ptr) = unsafe { (self.in_ptr.add(mid), self.out_ptr.add(mid)) };
220        (
221            InOutBuf {
222                in_ptr: self.in_ptr,
223                out_ptr: self.out_ptr,
224                len: mid,
225                _pd: PhantomData,
226            },
227            InOutBuf {
228                in_ptr: tail_in_ptr,
229                out_ptr: tail_out_ptr,
230                len: self.len() - mid,
231                _pd: PhantomData,
232            },
233        )
234    }
235
236    /// Partition buffer into 2 parts: buffer of arrays and tail.
237    #[inline(always)]
238    #[must_use]
239    pub fn into_chunks<N: ArraySize>(
240        self,
241    ) -> (InOutBuf<'inp, 'out, Array<T, N>>, InOutBuf<'inp, 'out, T>) {
242        let chunks = self.len() / N::USIZE;
243        let tail_pos = N::USIZE * chunks;
244        let tail_len = self.len() - tail_pos;
245        unsafe {
246            let chunks = InOutBuf {
247                in_ptr: self.in_ptr.cast::<Array<T, N>>(),
248                out_ptr: self.out_ptr.cast::<Array<T, N>>(),
249                len: chunks,
250                _pd: PhantomData,
251            };
252            let tail = InOutBuf {
253                in_ptr: self.in_ptr.add(tail_pos),
254                out_ptr: self.out_ptr.add(tail_pos),
255                len: tail_len,
256                _pd: PhantomData,
257            };
258            (chunks, tail)
259        }
260    }
261}
262
263impl InOutBuf<'_, '_, u8> {
264    /// XORs `data` with values behind the input slice and write
265    /// result to the output slice.
266    ///
267    /// # Panics
268    /// If `data` length is not equal to the buffer length.
269    #[inline(always)]
270    pub fn xor_in2out(&mut self, data: &[u8]) {
271        assert_eq!(self.len(), data.len());
272        unsafe {
273            for i in 0..data.len() {
274                let in_ptr = self.in_ptr.add(i);
275                let out_ptr = self.out_ptr.add(i);
276                *out_ptr = *in_ptr ^ data[i];
277            }
278        }
279    }
280}
281
282impl<'inp, 'out, T, N> TryInto<InOut<'inp, 'out, Array<T, N>>> for InOutBuf<'inp, 'out, T>
283where
284    N: ArraySize,
285{
286    type Error = IntoArrayError;
287
288    #[inline(always)]
289    fn try_into(self) -> Result<InOut<'inp, 'out, Array<T, N>>, Self::Error> {
290        if self.len() == N::USIZE {
291            Ok(InOut {
292                in_ptr: self.in_ptr.cast(),
293                out_ptr: self.out_ptr.cast(),
294                _pd: PhantomData,
295            })
296        } else {
297            Err(IntoArrayError)
298        }
299    }
300}
301
302/// Iterator over [`InOutBuf`].
303pub struct InOutBufIter<'inp, 'out, T> {
304    buf: InOutBuf<'inp, 'out, T>,
305    pos: usize,
306}
307
308impl<'inp, 'out, T> Iterator for InOutBufIter<'inp, 'out, T> {
309    type Item = InOut<'inp, 'out, T>;
310
311    #[inline(always)]
312    fn next(&mut self) -> Option<Self::Item> {
313        if self.buf.len() == self.pos {
314            return None;
315        }
316        let res = unsafe {
317            InOut {
318                in_ptr: self.buf.in_ptr.add(self.pos),
319                out_ptr: self.buf.out_ptr.add(self.pos),
320                _pd: PhantomData,
321            }
322        };
323        self.pos += 1;
324        Some(res)
325    }
326}