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;