1
//! Helper functions and structs to deal with dynamic sized types. In particular to deal with the `TermShared`.
2
//!
3
//! This code is adapted from the `slice-dst` crate, but supports the `Allocator` unstable api through the `allocator-api2` crate. Furthermore, removed all code that
4
//! we are not using anyway.
5

            
6
use std::alloc::Layout;
7
use std::alloc::LayoutError;
8
use std::ptr::NonNull;
9
use std::ptr::slice_from_raw_parts_mut;
10

            
11
use allocator_api2::alloc::AllocError;
12
use allocator_api2::alloc::Allocator;
13

            
14
/// This trait should be implemented by dynamic sized types.
15
///
16
/// # Safety
17
///
18
/// Implementing this trait requires various unsafe memory manipulations, and the layout/length must be correct. Otherwise it results in undefined behaviour.
19
pub unsafe trait SliceDst {
20
    /// Returns the layout of the slice containing `length` elements for this DST.
21
    fn layout_for(length: usize) -> Result<Layout, LayoutError>;
22

            
23
    /// Add the type on an untyped pointer
24
    fn retype(ptr: NonNull<[()]>) -> NonNull<Self>;
25

            
26
    /// The number of elements in this dynamic sized type. This information is necessary for deallocation.
27
    fn length(&self) -> usize;
28
}
29

            
30
/// Blanket implemented for Sized T.
31
unsafe impl<T> SliceDst for T {
32
21
    fn layout_for(_length: usize) -> Result<Layout, LayoutError> {
33
21
        Ok(Layout::new::<T>())
34
21
    }
35

            
36
    fn retype(ptr: NonNull<[()]>) -> NonNull<Self> {
37
        unsafe {
38
            let raw_ptr = ptr.as_ptr() as *mut Self;
39
            NonNull::new_unchecked(raw_ptr)
40
        }
41
    }
42

            
43
21
    fn length(&self) -> usize {
44
21
        0
45
21
    }
46
}
47

            
48
/// To calculate the layout of a [repr(C)] structure and the offsets of the fields from its fields’ layouts:
49
///
50
/// Copied from the `Layout` documentation.
51
562889
pub fn repr_c<const N: usize>(fields: &[Layout; N]) -> Result<Layout, LayoutError> {
52
562889
    let mut layout = Layout::from_size_align(0, 1)?;
53
1688665
    for &field in fields {
54
1688665
        let (new_layout, _offset) = layout.extend(field)?;
55
1688665
        layout = new_layout;
56
    }
57

            
58
    // Remember to finalize with `pad_to_align`!
59
562889
    Ok(layout.pad_to_align())
60
562889
}
61

            
62
/// A trait that can be used to extend `Allocator` implementations with the
63
/// ability to allocate (and deallocate) dynamically sized slices that implement
64
/// `SliceDst`.
65
///
66
/// # Safety
67
///
68
/// This trait is unsafe because it relies on the correct implementation of
69
/// `SliceDst` for proper memory layout and deallocation.
70
pub unsafe trait AllocatorDst {
71
    /// Allocate an object whose type implements `SliceDst`. The resulting memory is uninitialize.
72
    fn allocate_slice_dst<T: SliceDst + ?Sized>(&self, length: usize) -> Result<NonNull<T>, AllocError>;
73

            
74
    /// Deallocates an allocation returned by `allocate_slice_dst`.
75
    fn deallocate_slice_dst<T: ?Sized + SliceDst>(&self, ptr: NonNull<T>, length: usize);
76
}
77

            
78
unsafe impl<A: Allocator> AllocatorDst for A {
79
562887
    fn allocate_slice_dst<T: SliceDst + ?Sized>(&self, length: usize) -> Result<NonNull<T>, AllocError> {
80
562887
        let ptr = self.allocate(T::layout_for(length).expect("Invalid layout for SliceDst"))?;
81
        // Create a slice of the correct length for proper metadata
82
562887
        let slice_ptr = unsafe { NonNull::new_unchecked(slice_from_raw_parts_mut(ptr.as_ptr() as *mut (), length)) };
83
562887
        Ok(T::retype(slice_ptr))
84
562887
    }
85

            
86
23
    fn deallocate_slice_dst<T: ?Sized + SliceDst>(&self, ptr: NonNull<T>, length: usize) {
87
23
        unsafe {
88
23
            self.deallocate(
89
23
                NonNull::new_unchecked(ptr.as_ptr() as *mut u8),
90
23
                T::layout_for(length).expect("Invalid layout for SliceDst"),
91
23
            );
92
23
        }
93
23
    }
94
}
95

            
96
#[cfg(test)]
97
mod tests {
98
    use super::*;
99

            
100
    use allocator_api2::alloc::Global;
101

            
102
    #[repr(C)]
103
    struct WithHeader<T> {
104
        length: usize,
105
        array: [T],
106
    }
107

            
108
    unsafe impl<T> SliceDst for WithHeader<T> {
109
2
        fn layout_for(length: usize) -> Result<Layout, LayoutError> {
110
2
            let header_layout = Layout::new::<usize>();
111
2
            let array_layout = Layout::array::<T>(length)?;
112

            
113
2
            repr_c(&[header_layout, array_layout])
114
2
        }
115

            
116
        fn length(&self) -> usize {
117
            self.length
118
        }
119

            
120
1
        fn retype(ptr: NonNull<[()]>) -> NonNull<Self> {
121
            unsafe {
122
1
                let raw_ptr = ptr.as_ptr() as *mut WithHeader<T>;
123
1
                NonNull::new_unchecked(raw_ptr)
124
            }
125
1
        }
126
    }
127

            
128
    #[test]
129
1
    fn test_variable_sized_array() {
130
1
        let ptr = Global
131
1
            .allocate_slice_dst::<WithHeader<usize>>(5)
132
1
            .expect("Allocation failed in test");
133

            
134
1
        Global.deallocate_slice_dst(ptr, 5);
135
1
    }
136
}