pas/
shared_impl.rs

1use std::{marker::PhantomData, ptr::null};
2
3/// Slice error
4///
5/// An error is raised during when creating a slice via [`crate::Slice::new`],
6/// or [`crate::SliceMut::new`].
7#[derive(Copy, Clone, PartialEq)]
8pub enum SliceError {
9    /// Provided offset is out of bounds regarding the slice size.
10    ///
11    /// ## Example
12    ///
13    /// ```rust,should_panic
14    /// use pas::{Slice};
15    ///
16    /// let data: Vec<u32> = Vec::new();
17    /// // Panics, since the slice doesn't have a size of at least 16 bytes.
18    /// let slice: Slice<u32> = Slice::new(&data, 16);
19    /// ```
20    OffsetOutOfBounds {
21        /// Slice size, in **bytes**
22        size: usize,
23        /// Byte offset
24        offset: usize,
25    },
26    /// Sliced attribute byte size is bigger than the stride.
27    ///
28    /// ## Example
29    ///
30    /// ```rust,should_panic
31    /// use pas::{Slice};
32    ///
33    /// let data: Vec<u16> = vec!(0_u16, 1, 2);
34    /// // Panics, since the slice have a stride of 1 * std::mem::size_of::<u16>(),
35    /// // but the requested attribute has size std::mem::size_of::<u32>().
36    /// let slice: Slice<u32> = Slice::new(&data, 16);
37    /// ```
38    AttributeLargerThanStride {
39        /// Type name of the attribute read by the slice
40        type_name: &'static str,
41        /// Attribute size, in **bytes**
42        attr: usize,
43        /// Slice stride, in **bytes**
44        stride: usize,
45    },
46    /// Attribute is not aligned to the request offset in the slice.
47    ///
48    /// ## Example
49    ///
50    /// ```rust,should_panic
51    /// use pas::{Slice};
52    ///
53    /// let data: Vec<u8> = vec!(0_u8, 1, 2);
54    /// // Panics, since the offset will be unaligned
55    /// let slice: Slice<u32> = Slice::new(&data, 1);
56    /// ```
57    AlignmentFault {
58        /// Type name of the attribute read by the slice
59        type_name: &'static str,
60        /// Byte offset
61        offset: usize,
62    },
63}
64
65impl std::fmt::Debug for SliceError {
66    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67        match self {
68            Self::OffsetOutOfBounds { size, offset } => {
69                write!(
70                    f,
71                    "Byte offset is {}, but slice has a size of {} bytes",
72                    offset, size
73                )
74            }
75            Self::AttributeLargerThanStride {
76                type_name,
77                attr,
78                stride,
79            } => {
80                write!(
81                    f,
82                    "Attribute '{:?}' with size {} bytes, larger than stride with size {}",
83                    type_name, attr, stride
84                )
85            }
86            Self::AlignmentFault { type_name, offset } => write!(
87                f,
88                "Attribute '{:?}' isn't aligned to the byte offset {}",
89                type_name, offset
90            ),
91        }
92    }
93}
94
95/// Slice base implementation.
96///
97/// Do not use this type directly, instead:
98/// - Use the [`crate::slice_attr!`], [`crate::slice_attr_mut!`], [`crate::slice!`], or [`crate::slice_mut!`] macros
99/// - Use the [`crate::Slice`] or [`crate::SliceMut`] types
100#[derive(Clone, Copy)]
101pub struct SliceBase<Attr: Sized + 'static> {
102    /// Start pointer, pointing on the first byte of the slice.
103    pub(crate) start: *const u8,
104    /// End pointer, pointing one byte **after** the end of the slice.
105    pub(crate) end: *const u8,
106    /// Stride, in **bytes**
107    stride: usize,
108    _phantom: PhantomData<Attr>,
109}
110
111impl<Attr: Sized> SliceBase<Attr> {
112    pub(crate) fn new_typed<V: Pod>(
113        data: &[V],
114        offset: usize,
115        elt_count: usize,
116    ) -> Result<Self, SliceError> {
117        let stride = std::mem::size_of::<V>() * elt_count;
118        let bytes = std::mem::size_of_val(data);
119        let ptr = data.as_ptr_range();
120        Self::new(
121            ptr.start as *const u8..ptr.end as *const u8,
122            offset,
123            stride,
124            bytes,
125        )
126    }
127
128    pub(crate) fn new(
129        ptr_range: std::ops::Range<*const u8>,
130        offset: usize,
131        stride: usize,
132        bytes: usize,
133    ) -> Result<Self, SliceError> {
134        let ptr: *const u8 = unsafe { ptr_range.start.add(offset) };
135        // Empty slice are allowed, but we need to ensure that
136        // the offset and stride are valid.
137        if std::mem::size_of::<Attr>() > stride {
138            Err(SliceError::AttributeLargerThanStride {
139                type_name: std::any::type_name::<Attr>(),
140                attr: std::mem::size_of::<Attr>(),
141                stride,
142            })
143        } else if offset > 0 && offset >= bytes {
144            Err(SliceError::OffsetOutOfBounds {
145                size: bytes,
146                offset,
147            })
148        } else if ptr.align_offset(std::mem::align_of::<Attr>()) != 0 {
149            Err(SliceError::AlignmentFault {
150                type_name: std::any::type_name::<Attr>(),
151                offset,
152            })
153        } else {
154            Ok(Self {
155                start: ptr,
156                end: ptr_range.end,
157                stride,
158                _phantom: PhantomData,
159            })
160        }
161    }
162
163    /// Get the reference at index.
164    ///
165    /// ## Example
166    ///
167    /// ```rust
168    /// # use pas::Slice;
169    ///
170    /// let data = [1, 2, 3, 4];
171    /// let slice: Slice<u32> = Slice::new(&data, 0);
172    /// println!("{}", slice[0]); // Prints `1`
173    /// println!("{}", slice[3]); // Prints `3`
174    /// ```
175    pub fn get(&self, index: usize) -> Option<&Attr> {
176        self.get_ptr(index)
177            .map(|ptr| unsafe { &*ptr.cast::<Attr>() })
178    }
179
180    /// Number of elements in the slice.
181    pub fn len(&self) -> usize {
182        (self.end as usize)
183            .checked_sub(self.start as usize)
184            .unwrap()
185            .div_ceil(self.stride)
186    }
187
188    /// `true` if the slice has size `0`, `false` otherwise
189    pub fn is_empty(&self) -> bool {
190        self.len() == 0
191    }
192
193    /// Pointer to the first byte in the slice.
194    pub fn as_ptr(&self) -> *const u8 {
195        self.start
196    }
197
198    /// Get a pointer to the element at index `index`
199    pub(crate) fn get_ptr(&self, index: usize) -> Option<*const u8> {
200        if index < self.len() {
201            let start = self.stride * index;
202            Some(unsafe { self.start.add(start) })
203        } else {
204            None
205        }
206    }
207
208    /// Slice stride.
209    ///
210    /// <div class="warning">The stride is not in **elements count**, but in **bytes**.</div>
211    pub fn stride(&self) -> usize {
212        self.stride
213    }
214}
215
216///
217/// Traits implementation
218///
219
220impl<Attr: Sized + 'static> Default for SliceBase<Attr> {
221    fn default() -> Self {
222        Self {
223            start: null(),
224            end: null(),
225            stride: 0,
226            _phantom: PhantomData,
227        }
228    }
229}
230
231/// Implement [`Iterator`] and related traits for [`SliceIterator`]/[`SliceIteratorMut`].
232macro_rules! impl_iterator {
233    ($name: ident -> $elem: ty) => {
234        impl<'a, T: Pod> Iterator for $name<'a, T> {
235            type Item = $elem;
236
237            fn next(&mut self) -> Option<$elem> {
238                // `end` is exclusive and points one byte after the end of the slice.
239                if self.start >= self.end {
240                    return None;
241                }
242                unsafe {
243                    // Using `transmute` here because `self.start` is always a pointer.
244                    let ret = Some(std::mem::transmute::<_, $elem>(self.start));
245                    self.start = self.start.add(self.stride);
246                    ret
247                }
248            }
249
250            fn nth(&mut self, i: usize) -> Option<$elem> {
251                self.start = unsafe { self.start.add(i * self.stride) };
252                if self.start >= self.end {
253                    return None;
254                }
255                let ret = unsafe {
256                    // Using `transmute` here because `self.start` is always a pointer.
257                    Some(std::mem::transmute::<_, $elem>(self.start))
258                };
259                ret
260            }
261        }
262
263        impl<'a, T: Pod + Debug> std::fmt::Debug for $name<'a, T> {
264            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
265                f.debug_list().entries(self.into_iter()).finish()
266            }
267        }
268    };
269}
270
271use bytemuck::Pod;
272pub(super) use impl_iterator;