head/
slice.rs

1use core::{
2    mem::{self, MaybeUninit},
3    ptr, slice,
4};
5
6#[cfg(feature = "alloc")]
7use alloc::boxed::Box;
8
9/// A dynamically-sized view into a contiguous header and trailing sequence.
10#[repr(C)]
11#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
12pub struct HeaderSlice<H, T> {
13    header: H,
14    slice: [T],
15}
16
17#[repr(C)]
18struct HeaderSliceDummy<H, T> {
19    header: MaybeUninit<H>,
20    slice: MaybeUninit<[T; 0]>,
21}
22
23#[repr(C)]
24struct HeaderSliceHeader<H, T> {
25    header: H,
26    // Using `MaybeUninit` to avoid dropck for `T`.
27    slice: MaybeUninit<[T; 0]>,
28}
29
30impl<H, T> HeaderSliceHeader<H, T> {
31    #[inline]
32    fn as_header_slice(&self) -> &HeaderSlice<H, T> {
33        // SAFETY: `header` satisfies slice alignment requirement of `T`.
34        unsafe { HeaderSlice::from_header_unchecked(&self.header) }
35    }
36
37    #[inline]
38    fn as_header_slice_mut(&mut self) -> &mut HeaderSlice<H, T> {
39        // SAFETY: `header` satisfies slice alignment requirement of `T`.
40        unsafe { HeaderSlice::from_header_unchecked_mut(&mut self.header) }
41    }
42}
43
44/// Convenience functions for handling raw memory.
45#[allow(dead_code)]
46impl<H, T> HeaderSlice<H, T> {
47    /// Returns the alignment for header-slice allocations.
48    #[inline]
49    pub(crate) fn align() -> usize {
50        mem::align_of::<HeaderSliceDummy<H, T>>()
51    }
52
53    /// Returns the offset from the base address of a header-slice to the slice.
54    #[inline]
55    pub(crate) fn slice_offset() -> usize {
56        let dummy = HeaderSliceDummy::<H, T> {
57            header: MaybeUninit::uninit(),
58            slice: MaybeUninit::uninit(),
59        };
60
61        let base_addr = &dummy as *const _ as usize;
62        let slice_addr = &dummy.slice as *const _ as usize;
63
64        slice_addr - base_addr
65    }
66}
67
68// TODO: `From<Arc<H>>` for `Arc<HeaderSlice<H, H>>`
69// TODO: `From<Rc<H>>`  for `Rc<HeaderSlice<H, H>>`
70
71// TODO: `Clone` for `Box<HeaderSlice<H, T>>`
72
73impl<'a, H> From<&'a H> for &'a HeaderSlice<H, H> {
74    #[inline]
75    fn from(header: &'a H) -> Self {
76        // SAFETY: `H` satisfies slice alignment requirement.
77        unsafe { HeaderSlice::from_header_unchecked(header) }
78    }
79}
80
81impl<'a, H> From<&'a mut H> for &'a mut HeaderSlice<H, H> {
82    #[inline]
83    fn from(header: &'a mut H) -> Self {
84        // SAFETY: `H` satisfies slice alignment requirement.
85        unsafe { HeaderSlice::from_header_unchecked_mut(header) }
86    }
87}
88
89#[cfg(feature = "alloc")]
90impl<H> From<Box<H>> for Box<HeaderSlice<H, H>> {
91    #[inline]
92    fn from(header: Box<H>) -> Self {
93        // SAFETY: `H` satisfies slice alignment requirement.
94        unsafe { HeaderSlice::from_boxed_header_unchecked(header) }
95    }
96}
97
98#[cfg(feature = "alloc")]
99impl<H> From<Box<HeaderSlice<H, H>>> for Box<[H]> {
100    #[inline]
101    fn from(hs: Box<HeaderSlice<H, H>>) -> Self {
102        hs.into_full_boxed_slice()
103    }
104}
105
106/// Returns `true` if `header` is aligned to the slice element type `T`.
107#[inline]
108fn is_header_slice_aligned<H, T>(header: *const H) -> bool {
109    header as usize % mem::align_of::<T>() == 0
110}
111
112impl<H, T> HeaderSlice<H, T> {
113    /// Returns the result of calling `f` on a shared header-slice starting with
114    /// `header`.
115    #[inline]
116    pub fn with_header<F, R>(header: H, f: F) -> R
117    where
118        F: for<'a> FnOnce(&'a Self) -> R,
119    {
120        let hs = HeaderSliceHeader::<H, T> {
121            header,
122            slice: MaybeUninit::uninit(),
123        };
124
125        f(hs.as_header_slice())
126    }
127
128    /// Returns the result of calling `f` on a mutable header-slice starting
129    /// with `header`.
130    #[inline]
131    pub fn with_header_mut<F, R>(header: H, f: F) -> R
132    where
133        F: for<'a> FnOnce(&'a mut Self) -> R,
134    {
135        let mut hs = HeaderSliceHeader::<H, T> {
136            header,
137            slice: MaybeUninit::uninit(),
138        };
139
140        f(hs.as_header_slice_mut())
141    }
142
143    /// Attempts to create a shared header-slice from just `header`.
144    ///
145    /// The address of `header` must be at least aligned to `T` because the
146    /// empty slice component must be properly aligned.
147    ///
148    /// If `T` has equal or greater alignment than `H`, unwrapping the returned
149    /// value is a no-op.
150    #[inline]
151    pub fn from_header(header: &H) -> Option<&Self> {
152        if is_header_slice_aligned::<H, T>(header) {
153            // SAFETY: `header` satisfies slice alignment requirement.
154            Some(unsafe { Self::from_header_unchecked(header) })
155        } else {
156            None
157        }
158    }
159
160    /// Attempts to create a mutable header-slice from just `header`.
161    ///
162    /// The address of `header` must be at least aligned to `T` because the
163    /// empty slice component must be properly aligned.
164    ///
165    /// If `T` has equal or greater alignment than `H`, unwrapping the returned
166    /// value is a no-op.
167    #[inline]
168    pub fn from_header_mut(header: &mut H) -> Option<&mut Self> {
169        if is_header_slice_aligned::<H, T>(header) {
170            // SAFETY: `header` satisfies slice alignment requirement.
171            Some(unsafe { Self::from_header_unchecked_mut(header) })
172        } else {
173            None
174        }
175    }
176
177    /// Attempts to create a boxed header-slice from just `header`.
178    ///
179    /// The address of `header` must be at least aligned to `T` because the
180    /// empty slice component must be properly aligned.
181    ///
182    /// If `T` has equal or greater alignment than `H`, unwrapping the returned
183    /// value is a no-op.
184    #[cfg(feature = "alloc")]
185    #[inline]
186    pub fn from_boxed_header(header: Box<H>) -> Result<Box<Self>, Box<H>> {
187        if is_header_slice_aligned::<H, T>(&*header) {
188            // SAFETY: `header` satisfies slice alignment requirement.
189            Ok(unsafe { Self::from_boxed_header_unchecked(header) })
190        } else {
191            Err(header)
192        }
193    }
194
195    /// Create a shared header-slice from just `header`, without checking its
196    /// alignment.
197    ///
198    /// # Safety
199    ///
200    /// The address of `header` must be at least aligned to `T` because the
201    /// empty slice component must be properly aligned.
202    #[inline]
203    pub unsafe fn from_header_unchecked(header: &H) -> &Self {
204        Self::from_raw_parts(header, 0)
205    }
206
207    /// Create a mutable header-slice from just `header`, without checking its
208    /// alignment.
209    ///
210    /// # Safety
211    ///
212    /// The address of `header` must be at least aligned to `T` because the
213    /// empty slice component must be properly aligned.
214    #[inline]
215    pub unsafe fn from_header_unchecked_mut(header: &mut H) -> &mut Self {
216        Self::from_raw_parts_mut(header, 0)
217    }
218
219    /// Create a boxed header-slice from just `header`, without checking its
220    /// alignment.
221    ///
222    /// # Safety
223    ///
224    /// The address of `header` must be at least aligned to `T` because the
225    /// empty slice component must be properly aligned.
226    #[cfg(feature = "alloc")]
227    #[inline]
228    pub unsafe fn from_boxed_header_unchecked(header: Box<H>) -> Box<Self> {
229        Self::boxed_from_raw_parts(Box::into_raw(header), 0)
230    }
231
232    /// Forms a shared header-slice from a pointer and a length.
233    ///
234    /// # Safety
235    ///
236    /// Behavior is undefined if any of the following conditions are violated:
237    ///
238    /// - `header` and any slice following it must be [valid].
239    ///
240    ///   - `header` must be non-null and aligned to the greater alignment
241    ///     between `H` and `T`.
242    ///
243    ///   - If `len` is non-zero, the slice following `header` must be aligned
244    ///     to `T`.
245    ///
246    ///   - The entire memory range of spanning from the start of `header` to
247    ///     the end of the trailing slice must be contained within a single
248    ///     allocated object! Header-slices can never span across multiple
249    ///     allocated objects.
250    ///
251    /// - The memory referenced by the returned header-slice must not be mutated
252    ///   for the duration of lifetime `'a`, except inside an [`UnsafeCell`].
253    ///
254    /// - The total size of the resulting header-slice must be no larger than
255    ///   `isize::MAX`.
256    ///
257    /// # Caveat
258    ///
259    /// The lifetime for the returned slice is inferred from its usage. To
260    /// prevent accidental misuse, it's suggested to tie the lifetime to
261    /// whichever source lifetime is safe in the context, such as by providing a
262    /// helper function taking the lifetime of a host value for the slice, or by
263    /// explicit annotation.
264    ///
265    /// [valid]: https://doc.rust-lang.org/std/ptr/index.html#safety
266    /// [`UnsafeCell`]: https://doc.rust-lang.org/std/cell/struct.UnsafeCell.html
267    #[inline]
268    pub unsafe fn from_raw_parts<'a>(header: *const H, len: usize) -> &'a Self {
269        // We never create `&[H]` because data past `header` may refer to
270        // invalid instances of `H`. So instead we strictly use a raw slice
271        // pointer.
272        &*(ptr::slice_from_raw_parts(header, len) as *const Self)
273    }
274
275    /// Forms a mutable header-slice from a pointer and a length.
276    ///
277    /// # Safety
278    ///
279    /// Behavior is undefined if any of the following conditions are violated:
280    ///
281    /// - `header` and any slice following it must be [valid].
282    ///
283    ///   - `header` must be non-null and aligned to the greater alignment
284    ///     between `H` and `T`.
285    ///
286    ///   - If `len` is non-zero, the slice following `header` must be aligned
287    ///     to `T`.
288    ///
289    ///   - The entire memory range of spanning from the start of `header` to
290    ///     the end of the trailing slice must be contained within a single
291    ///     allocated object! Header-slices can never span across multiple
292    ///     allocated objects.
293    ///
294    /// - The memory referenced by the returned header-slice must not be
295    ///   accessed through any other pointer (not derived from the return value)
296    ///   for the duration of lifetime `'a`. Both read and write accesses are
297    ///   forbidden.
298    ///
299    /// - The total size of the resulting header-slice must be no larger than
300    ///   `isize::MAX`.
301    ///
302    /// # Caveat
303    ///
304    /// The lifetime for the returned slice is inferred from its usage. To
305    /// prevent accidental misuse, it's suggested to tie the lifetime to
306    /// whichever source lifetime is safe in the context, such as by providing a
307    /// helper function taking the lifetime of a host value for the slice, or by
308    /// explicit annotation.
309    ///
310    /// [valid]: https://doc.rust-lang.org/std/ptr/index.html#safety
311    #[inline]
312    pub unsafe fn from_raw_parts_mut<'a>(header: *mut H, len: usize) -> &'a mut Self {
313        // We never create `&mut [H]` because data past `header` may refer to
314        // invalid instances of `H`. So instead we strictly use a raw slice
315        // pointer.
316        &mut *(ptr::slice_from_raw_parts_mut(header, len) as *mut Self)
317    }
318
319    /// Forms a boxed header-slice from a pointer and a length.
320    ///
321    /// # Safety
322    ///
323    /// `header` must point to a header-slice with a slice of `len` items that
324    /// has been allocated by the global allocator.
325    ///
326    /// Improper use can lead to:
327    ///
328    /// - A double-free if the function is called twice on the same raw pointer.
329    ///
330    /// - Mutable aliasing, which causes undefined behavior.
331    ///
332    /// [valid]: https://doc.rust-lang.org/std/ptr/index.html#safety
333    #[cfg(feature = "alloc")]
334    #[inline]
335    pub unsafe fn boxed_from_raw_parts(header: *mut H, len: usize) -> Box<Self> {
336        // We never create `&mut [H]` because data past `header` may refer to
337        // invalid instances of `H`. So instead we strictly use a raw slice
338        // pointer.
339        Box::from_raw(ptr::slice_from_raw_parts_mut(header, len) as *mut Self)
340    }
341
342    /// Returns a shared reference to the value preceding a slice of `T` in
343    /// memory.
344    #[inline]
345    pub fn as_header(&self) -> &H {
346        &self.header
347    }
348
349    /// Returns a mutable reference to the value preceding a slice of `T` in
350    /// memory.
351    #[inline]
352    pub fn as_header_mut(&mut self) -> &mut H {
353        &mut self.header
354    }
355
356    /// Returns a shared reference to the trailing contiguous sequence of
357    /// values.
358    #[inline]
359    pub fn as_slice(&self) -> &[T] {
360        &self.slice
361    }
362
363    /// Returns a mutable reference to the trailing contiguous sequence of
364    /// values.
365    #[inline]
366    pub fn as_slice_mut(&mut self) -> &mut [T] {
367        &mut self.slice
368    }
369
370    /// Returns shared references to the header and its proceeded slice of `T`.
371    #[inline]
372    pub fn as_header_and_slice(&self) -> (&H, &[T]) {
373        (&self.header, &self.slice)
374    }
375
376    /// Returns mutable references to the header and its proceeded slice of `T`.
377    #[inline]
378    pub fn as_header_and_slice_mut(&mut self) -> (&mut H, &mut [T]) {
379        (&mut self.header, &mut self.slice)
380    }
381}
382
383impl<H> HeaderSlice<H, H> {
384    /// Attempts to create a shared header-slice from `slice`, using the first
385    /// element as the header.
386    ///
387    /// Returns `None` if `slice` is empty.
388    #[inline]
389    pub fn from_full_slice(slice: &[H]) -> Option<&Self> {
390        if slice.is_empty() {
391            None
392        } else {
393            // SAFETY: `slice` has an element for a header.
394            Some(unsafe { Self::from_full_slice_unchecked(slice) })
395        }
396    }
397
398    /// Attempts to create a mutable header-slice from `slice`, using the first
399    /// element as the header.
400    ///
401    /// Returns `None` if `slice` is empty.
402    #[inline]
403    pub fn from_full_slice_mut(slice: &mut [H]) -> Option<&mut Self> {
404        if slice.is_empty() {
405            None
406        } else {
407            // SAFETY: `slice` has an element for a header.
408            Some(unsafe { Self::from_full_slice_unchecked_mut(slice) })
409        }
410    }
411
412    /// Attempts to create a boxed header-slice from `slice`, using the first
413    /// element as the header.
414    ///
415    /// Returns `None` if `slice` is empty.
416    #[cfg(feature = "alloc")]
417    #[inline]
418    pub fn from_full_boxed_slice(slice: Box<[H]>) -> Option<Box<Self>> {
419        if slice.is_empty() {
420            None
421        } else {
422            // SAFETY: `slice` has an element for a header.
423            Some(unsafe { Self::from_full_boxed_slice_unchecked(slice) })
424        }
425    }
426
427    /// Creates a shared header-slice from `slice`, using the first element as
428    /// the header without checking if it exists.
429    ///
430    /// # Safety
431    ///
432    /// `slice` must be non-empty.
433    #[inline]
434    pub unsafe fn from_full_slice_unchecked(slice: &[H]) -> &Self {
435        Self::from_raw_parts(slice.as_ptr(), slice.len().wrapping_sub(1))
436    }
437
438    /// Creates a mutable header-slice from `slice`, using the first element as
439    /// the header without checking if it exists.
440    ///
441    /// # Safety
442    ///
443    /// `slice` must be non-empty.
444    #[inline]
445    pub unsafe fn from_full_slice_unchecked_mut(slice: &mut [H]) -> &mut Self {
446        Self::from_raw_parts_mut(slice.as_mut_ptr(), slice.len().wrapping_sub(1))
447    }
448
449    /// Creates a boxed header-slice from `slice`, using the first element as
450    /// the header without checking if it exists.
451    ///
452    /// # Safety
453    ///
454    /// `slice` must be non-empty.
455    #[cfg(feature = "alloc")]
456    #[inline]
457    pub unsafe fn from_full_boxed_slice_unchecked(slice: Box<[H]>) -> Box<Self> {
458        let new_len = slice.len().wrapping_sub(1);
459        let header = Box::into_raw(slice) as *mut H;
460
461        Self::boxed_from_raw_parts(header, new_len)
462    }
463
464    /// Returns the full range of `self` as a single shared slice.
465    #[inline]
466    pub fn as_full_slice(&self) -> &[H] {
467        let data = self as *const Self as *const H;
468        let len = self.slice.len() + 1;
469
470        unsafe { slice::from_raw_parts(data, len) }
471    }
472
473    /// Returns the full range of `self` as a single mutable slice.
474    #[inline]
475    pub fn as_full_slice_mut(&mut self) -> &mut [H] {
476        let data = self as *mut Self as *mut H;
477        let len = self.slice.len() + 1;
478
479        unsafe { slice::from_raw_parts_mut(data, len) }
480    }
481
482    /// Returns the full range of `self` as a single boxed slice.
483    #[cfg(feature = "alloc")]
484    #[inline]
485    pub fn into_full_boxed_slice(self: Box<Self>) -> Box<[H]> {
486        let len = self.slice.len() + 1;
487        let data = Box::into_raw(self) as *mut H;
488
489        unsafe { Box::from_raw(slice::from_raw_parts_mut(data, len)) }
490    }
491}