• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::{
6     alloc::{alloc, alloc_zeroed, dealloc, Layout},
7     cmp::min,
8 };
9 
10 /// A contiguous memory allocation with a specified size and alignment, with a
11 /// Drop impl to perform the deallocation.
12 ///
13 /// Conceptually this is like a Box<[u8]> but for which we can select a minimum
14 /// required alignment at the time of allocation.
15 ///
16 /// # Example
17 ///
18 /// ```
19 /// use std::alloc::Layout;
20 /// use std::mem;
21 /// use sys_util_core::LayoutAllocation;
22 ///
23 /// #[repr(C)]
24 /// struct Header {
25 ///     q: usize,
26 ///     entries: [Entry; 0], // flexible array member
27 /// }
28 ///
29 /// #[repr(C)]
30 /// struct Entry {
31 ///     e: usize,
32 /// }
33 ///
34 /// fn demo(num_entries: usize) {
35 ///     let size = mem::size_of::<Header>() + num_entries * mem::size_of::<Entry>();
36 ///     let layout = Layout::from_size_align(size, mem::align_of::<Header>()).unwrap();
37 ///     let mut allocation = LayoutAllocation::zeroed(layout);
38 ///
39 ///     // Safe to obtain an exclusive reference because there are no other
40 ///     // references to the allocation yet and all-zero is a valid bit pattern for
41 ///     // our header.
42 ///     let header = unsafe { allocation.as_mut::<Header>() };
43 /// }
44 /// ```
45 pub struct LayoutAllocation {
46     ptr: *mut u8,
47     layout: Layout,
48 }
49 
50 impl LayoutAllocation {
51     /// Allocates memory with the specified size and alignment. The content is
52     /// not initialized.
53     ///
54     /// Uninitialized data is not safe to read. Further, it is not safe to
55     /// obtain a reference to data potentially holding a bit pattern
56     /// incompatible with its type, for example an uninitialized bool or enum.
uninitialized(layout: Layout) -> Self57     pub fn uninitialized(layout: Layout) -> Self {
58         let ptr = if layout.size() > 0 {
59             unsafe {
60                 // Safe as long as we guarantee layout.size() > 0.
61                 alloc(layout)
62             }
63         } else {
64             layout.align() as *mut u8
65         };
66         LayoutAllocation { ptr, layout }
67     }
68 
69     /// Allocates memory with the specified size and alignment and initializes
70     /// the content to all zero-bytes.
71     ///
72     /// Note that zeroing the memory does not necessarily make it safe to obtain
73     /// a reference to the allocation. Depending on the intended type T,
74     /// all-zero may or may not be a legal bit pattern for that type. For
75     /// example obtaining a reference would immediately be undefined behavior if
76     /// one of the fields has type NonZeroUsize.
zeroed(layout: Layout) -> Self77     pub fn zeroed(layout: Layout) -> Self {
78         let ptr = if layout.size() > 0 {
79             unsafe {
80                 // Safe as long as we guarantee layout.size() > 0.
81                 alloc_zeroed(layout)
82             }
83         } else {
84             layout.align() as *mut u8
85         };
86         LayoutAllocation { ptr, layout }
87     }
88 
89     /// Returns a raw pointer to the allocated data.
as_ptr<T>(&self) -> *mut T90     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.
layout(&self) -> &Layout95     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.
as_ref<T>(&self) -> &T106     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.
as_mut<T>(&mut self) -> &mut T117     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.
as_slice<T>(&self, num_elements: usize) -> &[T]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.
as_mut_slice<T>(&mut self, num_elements: usize) -> &mut [T]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 
158 impl Drop for LayoutAllocation {
drop(&mut self)159     fn drop(&mut self) {
160         if self.layout.size() > 0 {
161             unsafe {
162                 // Safe as long as we guarantee layout.size() > 0.
163                 dealloc(self.ptr, self.layout);
164             }
165         }
166     }
167 }
168 
169 #[cfg(test)]
170 mod tests {
171     use super::*;
172     use std::mem::{align_of, size_of};
173 
174     #[test]
test_as_slice_u32()175     fn test_as_slice_u32() {
176         let layout = Layout::from_size_align(size_of::<u32>() * 15, align_of::<u32>()).unwrap();
177         let allocation = LayoutAllocation::zeroed(layout);
178         let slice: &[u32] = unsafe { allocation.as_slice(15) };
179         assert_eq!(slice.len(), 15);
180         assert_eq!(slice[0], 0);
181         assert_eq!(slice[14], 0);
182     }
183 
184     #[test]
test_as_slice_u32_smaller_len()185     fn test_as_slice_u32_smaller_len() {
186         let layout = Layout::from_size_align(size_of::<u32>() * 15, align_of::<u32>()).unwrap();
187         let allocation = LayoutAllocation::zeroed(layout);
188 
189         // Slice less than the allocation size, which will return a slice of only the requested length.
190         let slice: &[u32] = unsafe { allocation.as_slice(5) };
191         assert_eq!(slice.len(), 5);
192     }
193 
194     #[test]
test_as_slice_u32_larger_len()195     fn test_as_slice_u32_larger_len() {
196         let layout = Layout::from_size_align(size_of::<u32>() * 15, align_of::<u32>()).unwrap();
197         let allocation = LayoutAllocation::zeroed(layout);
198 
199         // Slice more than the allocation size, which will clamp the returned slice len to the limit.
200         let slice: &[u32] = unsafe { allocation.as_slice(100) };
201         assert_eq!(slice.len(), 15);
202     }
203 
204     #[test]
test_as_slice_u32_remainder()205     fn test_as_slice_u32_remainder() {
206         // Allocate a buffer that is not a multiple of u32 in size.
207         let layout = Layout::from_size_align(size_of::<u32>() * 15 + 2, align_of::<u32>()).unwrap();
208         let allocation = LayoutAllocation::zeroed(layout);
209 
210         // Slice as many u32s as possible, which should return a slice that only includes the full
211         // u32s, not the trailing 2 bytes.
212         let slice: &[u32] = unsafe { allocation.as_slice(100) };
213         assert_eq!(slice.len(), 15);
214     }
215 }
216