base/
alloc.rs

1// Copyright 2019 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::alloc::alloc;
6use std::alloc::alloc_zeroed;
7use std::alloc::dealloc;
8use std::alloc::Layout;
9use std::cmp::min;
10
11/// A contiguous memory allocation with a specified size and alignment, with a
12/// Drop impl to perform the deallocation.
13///
14/// Conceptually this is like a Box<[u8]> but for which we can select a minimum
15/// required alignment at the time of allocation.
16///
17/// # Example
18///
19/// ```
20/// use std::alloc::Layout;
21/// use std::mem;
22/// use base::LayoutAllocation;
23///
24/// #[repr(C)]
25/// struct Header {
26///     q: usize,
27///     entries: [Entry; 0], // flexible array member
28/// }
29///
30/// #[repr(C)]
31/// struct Entry {
32///     e: usize,
33/// }
34///
35/// fn demo(num_entries: usize) {
36///     let size = mem::size_of::<Header>() + num_entries * mem::size_of::<Entry>();
37///     let layout = Layout::from_size_align(size, mem::align_of::<Header>()).unwrap();
38///     let mut allocation = LayoutAllocation::zeroed(layout);
39///
40///     // SAFETY:
41///     // Safe to obtain an exclusive reference because there are no other
42///     // references to the allocation yet and all-zero is a valid bit pattern for
43///     // our header.
44///     let header = unsafe { allocation.as_mut::<Header>() };
45/// }
46/// ```
47pub struct LayoutAllocation {
48    ptr: *mut u8,
49    layout: Layout,
50}
51
52impl LayoutAllocation {
53    /// Allocates memory with the specified size and alignment. The content is
54    /// not initialized.
55    ///
56    /// Uninitialized data is not safe to read. Further, it is not safe to
57    /// obtain a reference to data potentially holding a bit pattern
58    /// incompatible with its type, for example an uninitialized bool or enum.
59    pub fn uninitialized(layout: Layout) -> Self {
60        let ptr = if layout.size() > 0 {
61            // SAFETY:
62            // Safe as long as we guarantee layout.size() > 0.
63            unsafe { alloc(layout) }
64        } else {
65            layout.align() as *mut u8
66        };
67        LayoutAllocation { ptr, layout }
68    }
69
70    /// Allocates memory with the specified size and alignment and initializes
71    /// the content to all zero-bytes.
72    ///
73    /// Note that zeroing the memory does not necessarily make it safe to obtain
74    /// a reference to the allocation. Depending on the intended type T,
75    /// all-zero may or may not be a legal bit pattern for that type. For
76    /// example obtaining a reference would immediately be undefined behavior if
77    /// one of the fields has type NonZeroUsize.
78    pub fn zeroed(layout: Layout) -> Self {
79        let ptr = if layout.size() > 0 {
80            // SAFETY:
81            // Safe as long as we guarantee layout.size() > 0.
82            unsafe { alloc_zeroed(layout) }
83        } else {
84            layout.align() as *mut u8
85        };
86        LayoutAllocation { ptr, layout }
87    }
88
89    /// Returns a raw pointer to the allocated data.
90    pub fn as_ptr<T>(&self) -> *mut T {
91        self.ptr as *mut T
92    }
93
94    /// Returns a reference to the `Layout` used to create this allocation.
95    pub fn layout(&self) -> &Layout {
96        &self.layout
97    }
98
99    /// Returns a shared reference to the allocated data.
100    ///
101    /// # Safety
102    ///
103    /// Caller is responsible for ensuring that the data behind this pointer has
104    /// been initialized as much as necessary and that there are no already
105    /// existing mutable references to any part of the data.
106    pub unsafe fn as_ref<T>(&self) -> &T {
107        &*self.as_ptr()
108    }
109
110    /// Returns an exclusive reference to the allocated data.
111    ///
112    /// # Safety
113    ///
114    /// Caller is responsible for ensuring that the data behind this pointer has
115    /// been initialized as much as necessary and that there are no already
116    /// existing references to any part of the data.
117    pub unsafe fn as_mut<T>(&mut self) -> &mut T {
118        &mut *self.as_ptr()
119    }
120
121    /// Returns a shared slice reference to the allocated data.
122    ///
123    /// # Arguments
124    ///
125    /// `num_elements` - Number of `T` elements to include in the slice.
126    ///                  The length of the slice will be capped to the allocation's size.
127    ///                  Caller must ensure that any sliced elements are initialized.
128    ///
129    /// # Safety
130    ///
131    /// Caller is responsible for ensuring that the data behind this pointer has
132    /// been initialized as much as necessary and that there are no already
133    /// existing mutable references to any part of the data.
134    pub unsafe fn as_slice<T>(&self, num_elements: usize) -> &[T] {
135        let len = min(num_elements, self.layout.size() / std::mem::size_of::<T>());
136        std::slice::from_raw_parts(self.as_ptr(), len)
137    }
138
139    /// Returns an exclusive slice reference to the allocated data.
140    ///
141    /// # Arguments
142    ///
143    /// `num_elements` - Number of `T` elements to include in the slice.
144    ///                  The length of the slice will be capped to the allocation's size.
145    ///                  Caller must ensure that any sliced elements are initialized.
146    ///
147    /// # Safety
148    ///
149    /// Caller is responsible for ensuring that the data behind this pointer has
150    /// been initialized as much as necessary and that there are no already
151    /// existing references to any part of the data.
152    pub unsafe fn as_mut_slice<T>(&mut self, num_elements: usize) -> &mut [T] {
153        let len = min(num_elements, self.layout.size() / std::mem::size_of::<T>());
154        std::slice::from_raw_parts_mut(self.as_ptr(), len)
155    }
156}
157
158impl Drop for LayoutAllocation {
159    fn drop(&mut self) {
160        if self.layout.size() > 0 {
161            // SAFETY:
162            // Safe as long as we guarantee layout.size() > 0.
163            unsafe {
164                dealloc(self.ptr, self.layout);
165            }
166        }
167    }
168}
169
170#[cfg(test)]
171mod tests {
172    use std::mem::align_of;
173    use std::mem::size_of;
174
175    use super::*;
176
177    #[test]
178    fn test_as_slice_u32() {
179        let layout = Layout::from_size_align(size_of::<u32>() * 15, align_of::<u32>()).unwrap();
180        let allocation = LayoutAllocation::zeroed(layout);
181        // SAFETY:
182        // Slice less than the allocation size, which will return a slice of only the requested
183        // length.
184        let slice: &[u32] = unsafe { allocation.as_slice(15) };
185        assert_eq!(slice.len(), 15);
186        assert_eq!(slice[0], 0);
187        assert_eq!(slice[14], 0);
188    }
189
190    #[test]
191    fn test_as_slice_u32_smaller_len() {
192        let layout = Layout::from_size_align(size_of::<u32>() * 15, align_of::<u32>()).unwrap();
193        let allocation = LayoutAllocation::zeroed(layout);
194
195        // SAFETY:
196        // Slice less than the allocation size, which will return a slice of only the requested
197        // length.
198        let slice: &[u32] = unsafe { allocation.as_slice(5) };
199        assert_eq!(slice.len(), 5);
200    }
201
202    #[test]
203    fn test_as_slice_u32_larger_len() {
204        let layout = Layout::from_size_align(size_of::<u32>() * 15, align_of::<u32>()).unwrap();
205        let allocation = LayoutAllocation::zeroed(layout);
206
207        // SAFETY:
208        // Slice more than the allocation size, which will clamp the returned slice len to the
209        // limit.
210        let slice: &[u32] = unsafe { allocation.as_slice(100) };
211        assert_eq!(slice.len(), 15);
212    }
213
214    #[test]
215    fn test_as_slice_u32_remainder() {
216        // Allocate a buffer that is not a multiple of u32 in size.
217        let layout = Layout::from_size_align(size_of::<u32>() * 15 + 2, align_of::<u32>()).unwrap();
218        let allocation = LayoutAllocation::zeroed(layout);
219
220        // SAFETY:
221        // Slice as many u32s as possible, which should return a slice that only includes the full
222        // u32s, not the trailing 2 bytes.
223        let slice: &[u32] = unsafe { allocation.as_slice(100) };
224        assert_eq!(slice.len(), 15);
225    }
226}