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