devela/data/dst/
helpers.rs

1// helper functions
2
3use crate::{
4    MaybeUninit, MemPod,
5    _core::{ptr, slice},
6};
7
8type BufSlice<T> = [MaybeUninit<T>];
9
10pub(crate) fn decompose_pointer<T: ?Sized>(mut ptr: *const T) -> (*const (), usize, [usize; 3]) {
11    let addr = ptr as *const ();
12    let rv = mem_as_slice(&mut ptr);
13    let mut vals = [0; 3];
14    assert!(rv[0] == addr as usize, "BUG: Pointer layout is not (data_ptr, info...)");
15    vals[..rv.len() - 1].copy_from_slice(&rv[1..]);
16    (addr, rv.len() - 1, vals)
17}
18
19pub(crate) const fn mem_as_slice<T>(ptr: &mut T) -> &mut [usize] {
20    assert!(size_of::<T>() % size_of::<usize>() == 0);
21    assert!(align_of::<T>() % align_of::<usize>() == 0);
22    let words = size_of::<T>() / size_of::<usize>();
23    // SAFETY: Points to valid memory (a raw pointer)
24    unsafe { slice::from_raw_parts_mut(ptr as *mut _ as *mut usize, words) }
25}
26
27/// Re-construct a fat pointer
28pub(crate) unsafe fn make_fat_ptr<T: ?Sized, W: MemPod>(
29    data_ptr: *mut (),
30    meta_vals: &BufSlice<W>,
31) -> *mut T {
32    #[repr(C)]
33    #[derive(Clone, Copy)]
34    struct Raw {
35        ptr: *const (),
36        meta: [usize; 4],
37    }
38    #[repr(C)]
39    union Inner<T: ?Sized> {
40        ptr: *mut T,
41        raw: Raw,
42    }
43    let mut rv = Inner { raw: Raw { ptr: data_ptr, meta: [0; 4] } };
44    assert!(meta_vals.len() * size_of::<W>() % size_of::<usize>() == 0);
45    assert!(meta_vals.len() * size_of::<W>() <= 4 * size_of::<usize>());
46    // SAFETY: caller must ensure safety
47    unsafe {
48        ptr::copy(
49            meta_vals.as_ptr() as *const u8,
50            rv.raw.meta.as_mut_ptr() as *mut u8,
51            meta_vals.len() * size_of::<W>(),
52        );
53    }
54    // SAFETY: FIXME: miri complains
55    // WAIT:FIX: https://github.com/thepowersgang/stack_dst-rs/issues/14
56    let rv = unsafe { rv.ptr };
57    assert_eq!(rv as *const (), data_ptr.cast_const());
58    rv
59}
60
61/// Write metadata (abstraction around `ptr::copy`)
62pub(crate) fn store_metadata<W: MemPod>(dst: &mut BufSlice<W>, meta_words: &[usize]) {
63    let n_bytes = size_of_val(meta_words);
64    assert!(
65        n_bytes <= dst.len() * size_of::<W>(),
66        "nbytes [{}] <= dst.len() [{}] * sizeof [{}]",
67        n_bytes,
68        dst.len(),
69        size_of::<W>()
70    );
71    unsafe {
72        ptr::copy(meta_words.as_ptr() as *const u8, dst.as_mut_ptr() as *mut u8, n_bytes);
73    }
74}
75
76pub(crate) const fn round_to_words<T>(len: usize) -> usize {
77    len.div_ceil(size_of::<T>())
78}
79
80/// Calls a provided function to get a fat pointer version of `v`
81/// (and checks that the returned pointer is sane)
82pub(crate) fn check_fat_pointer<U, T: ?Sized>(v: &U, get_ref: impl FnOnce(&U) -> &T) -> &T {
83    let ptr: &T = get_ref(v);
84    assert_eq!(
85        ptr as *const _ as *const u8, v as *const _ as *const u8,
86        "MISUSE: Closure returned different pointer"
87    );
88    assert_eq!(size_of_val(ptr), size_of::<U>(), "MISUSE: Closure returned a subset pointer");
89    ptr
90}
91
92/// Push items to a list using a generator function to get the items
93/// - `meta`  - Metadata slot (must be 1 usize long)
94/// - `data`  - Data slot, must be at least `count * sizeof(T)` long
95/// - `count` - Number of items to insert
96/// - `make`  - Generator function (is passed the current index)
97/// - `reset_slot` - A slot updated with `reset_value` when a panic happens before push is complete
98/// - `reset_value` - Value used in `reset_slot`
99///
100/// This provides a panic-safe push as long as `reset_slot` and `reset_value` undo the allocation operation
101pub(crate) unsafe fn list_push_gen<T, W: MemPod>(
102    meta: &mut BufSlice<W>,
103    data: &mut BufSlice<W>,
104    count: usize,
105    mut make: impl FnMut(usize) -> T,
106    reset_slot: &mut usize,
107    reset_value: usize,
108) {
109    /// Helper to drop/zero all pushed items, and reset data structure state if there's a panic
110    struct PanicState<'a, T>(*mut T, usize, &'a mut usize, usize);
111    impl<T> core::ops::Drop for PanicState<'_, T> {
112        fn drop(&mut self) {
113            if self.0.is_null() {
114                return;
115            }
116            // Reset the state of the data structure (leaking items)
117            *self.2 = self.3;
118            // Drop all partially-populated items
119            unsafe {
120                while self.1 != 0 {
121                    ptr::drop_in_place(&mut *self.0);
122                    ptr::write_bytes(self.0 as *mut u8, 0, size_of::<T>());
123                    self.0 = self.0.offset(1);
124                    self.1 -= 1;
125                }
126            }
127        }
128    }
129
130    let mut ptr = data.as_mut_ptr() as *mut T;
131    let mut clr = PanicState(ptr, 0, reset_slot, reset_value);
132    for i in 0..count {
133        let val = make(i);
134        // SAFETY: caller must ensure safety
135        unsafe {
136            ptr::write(ptr, val);
137        }
138        // SAFETY: caller must ensure safety
139        ptr = unsafe { ptr.offset(1) };
140        clr.1 += 1;
141    }
142    // Prevent drops and prevent reset
143    clr.0 = ptr::null_mut();
144    // Save the length once everything has been written
145    store_metadata(meta, &[count]);
146}