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
19755841
    fn layout_for(_length: usize) -> Result<Layout, LayoutError> {
33
19755841
        Ok(Layout::new::<T>())
34
19755841
    }
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
19755841
    fn length(&self) -> usize {
44
19755841
        0
45
19755841
    }
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
4
pub fn repr_c<const N: usize>(fields: &[Layout; N]) -> Result<Layout, LayoutError> {
52
4
    let mut layout = Layout::from_size_align(0, 1)?;
53
8
    for &field in fields {
54
8
        let (new_layout, _offset) = layout.extend(field)?;
55
8
        layout = new_layout;
56
    }
57

            
58
    // Remember to finalize with `pad_to_align`!
59
4
    Ok(layout.pad_to_align())
60
4
}
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
2
    fn allocate_slice_dst<T: SliceDst + ?Sized>(&self, length: usize) -> Result<NonNull<T>, AllocError> {
80
2
        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
2
        let slice_ptr = unsafe { NonNull::new_unchecked(slice_from_raw_parts_mut(ptr.as_ptr() as *mut (), length)) };
83
2
        Ok(T::retype(slice_ptr))
84
2
    }
85

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

            
138
#[cfg(kani)]
139
mod verification {
140
    use super::*;
141

            
142
    #[kani::proof]
143
    fn sized_type_layout_matches_layout_new() {
144
        let layout_zero = <u32 as SliceDst>::layout_for(0).expect("layout for sized must succeed");
145
        assert_eq!(layout_zero, Layout::new::<u32>());
146

            
147
        let n: usize = kani::any();
148
        let layout_n = <u32 as SliceDst>::layout_for(n).expect("layout for sized ignores length");
149
        assert_eq!(layout_n, Layout::new::<u32>());
150
    }
151

            
152
    #[kani::proof]
153
    fn sized_type_length_is_zero() {
154
        let value: u32 = kani::any();
155
        assert_eq!(<u32 as SliceDst>::length(&value), 0);
156
    }
157

            
158
    #[kani::proof]
159
    #[kani::unwind(5)]
160
    fn repr_c_is_at_least_as_large_as_any_field() {
161
        let a: Layout = Layout::new::<u32>();
162
        let b: Layout = Layout::new::<u8>();
163
        let composite = repr_c(&[a, b]).expect("layout composes for fixed inputs");
164

            
165
        // pad_to_align preserves the maximum field alignment.
166
        assert!(composite.align() >= a.align());
167
        assert!(composite.align() >= b.align());
168
        // The composite must be large enough to hold both fields back-to-back.
169
        assert!(composite.size() >= a.size() + b.size());
170
    }
171
}