// Copyright 2019 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34use std::alloc::alloc;5use std::alloc::alloc_zeroed;6use std::alloc::dealloc;7use std::alloc::Layout;8use std::cmp::min;910/// A contiguous memory allocation with a specified size and alignment, with a11/// Drop impl to perform the deallocation.12///13/// Conceptually this is like a Box<[u8]> but for which we can select a minimum14/// required alignment at the time of allocation.15///16/// # Example17///18/// ```19/// use std::alloc::Layout;20/// use std::mem;21/// use base::LayoutAllocation;22///23/// #[repr(C)]24/// struct Header {25/// q: usize,26/// entries: [Entry; 0], // flexible array member27/// }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/// // SAFETY:40/// // Safe to obtain an exclusive reference because there are no other41/// // references to the allocation yet and all-zero is a valid bit pattern for42/// // our header.43/// let header = unsafe { allocation.as_mut::<Header>() };44/// }45/// ```46pub struct LayoutAllocation {47ptr: *mut u8,48layout: Layout,49}5051impl LayoutAllocation {52/// Allocates memory with the specified size and alignment. The content is53/// not initialized.54///55/// Uninitialized data is not safe to read. Further, it is not safe to56/// obtain a reference to data potentially holding a bit pattern57/// incompatible with its type, for example an uninitialized bool or enum.58pub fn uninitialized(layout: Layout) -> Self {59let ptr = if layout.size() > 0 {60// SAFETY:61// Safe as long as we guarantee layout.size() > 0.62unsafe { alloc(layout) }63} else {64layout.align() as *mut u865};66LayoutAllocation { ptr, layout }67}6869/// Allocates memory with the specified size and alignment and initializes70/// the content to all zero-bytes.71///72/// Note that zeroing the memory does not necessarily make it safe to obtain73/// 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. For75/// example obtaining a reference would immediately be undefined behavior if76/// one of the fields has type NonZeroUsize.77pub fn zeroed(layout: Layout) -> Self {78let ptr = if layout.size() > 0 {79// SAFETY:80// Safe as long as we guarantee layout.size() > 0.81unsafe { alloc_zeroed(layout) }82} else {83layout.align() as *mut u884};85LayoutAllocation { ptr, layout }86}8788/// Returns a raw pointer to the allocated data.89pub fn as_ptr<T>(&self) -> *mut T {90self.ptr as *mut T91}9293/// Returns a reference to the `Layout` used to create this allocation.94pub fn layout(&self) -> &Layout {95&self.layout96}9798/// Returns a shared reference to the allocated data.99///100/// # Safety101///102/// Caller is responsible for ensuring that the data behind this pointer has103/// been initialized as much as necessary and that there are no already104/// existing mutable references to any part of the data.105pub unsafe fn as_ref<T>(&self) -> &T {106&*self.as_ptr()107}108109/// Returns an exclusive reference to the allocated data.110///111/// # Safety112///113/// Caller is responsible for ensuring that the data behind this pointer has114/// been initialized as much as necessary and that there are no already115/// existing references to any part of the data.116pub unsafe fn as_mut<T>(&mut self) -> &mut T {117&mut *self.as_ptr()118}119120/// Returns a shared slice reference to the allocated data.121///122/// # Arguments123///124/// `num_elements` - Number of `T` elements to include in the slice.125/// The length of the slice will be capped to the allocation's size.126/// Caller must ensure that any sliced elements are initialized.127///128/// # Safety129///130/// Caller is responsible for ensuring that the data behind this pointer has131/// been initialized as much as necessary and that there are no already132/// existing mutable references to any part of the data.133pub unsafe fn as_slice<T>(&self, num_elements: usize) -> &[T] {134let len = min(num_elements, self.layout.size() / std::mem::size_of::<T>());135std::slice::from_raw_parts(self.as_ptr(), len)136}137138/// Returns an exclusive slice reference to the allocated data.139///140/// # Arguments141///142/// `num_elements` - Number of `T` elements to include in the slice.143/// The length of the slice will be capped to the allocation's size.144/// Caller must ensure that any sliced elements are initialized.145///146/// # Safety147///148/// Caller is responsible for ensuring that the data behind this pointer has149/// been initialized as much as necessary and that there are no already150/// existing references to any part of the data.151pub unsafe fn as_mut_slice<T>(&mut self, num_elements: usize) -> &mut [T] {152let len = min(num_elements, self.layout.size() / std::mem::size_of::<T>());153std::slice::from_raw_parts_mut(self.as_ptr(), len)154}155}156157impl Drop for LayoutAllocation {158fn drop(&mut self) {159if self.layout.size() > 0 {160// SAFETY:161// Safe as long as we guarantee layout.size() > 0.162unsafe {163dealloc(self.ptr, self.layout);164}165}166}167}168169#[cfg(test)]170mod tests {171use std::mem::align_of;172use std::mem::size_of;173174use super::*;175176#[test]177fn test_as_slice_u32() {178let layout = Layout::from_size_align(size_of::<u32>() * 15, align_of::<u32>()).unwrap();179let allocation = LayoutAllocation::zeroed(layout);180// SAFETY:181// Slice less than the allocation size, which will return a slice of only the requested182// length.183let slice: &[u32] = unsafe { allocation.as_slice(15) };184assert_eq!(slice.len(), 15);185assert_eq!(slice[0], 0);186assert_eq!(slice[14], 0);187}188189#[test]190fn test_as_slice_u32_smaller_len() {191let layout = Layout::from_size_align(size_of::<u32>() * 15, align_of::<u32>()).unwrap();192let allocation = LayoutAllocation::zeroed(layout);193194// SAFETY:195// Slice less than the allocation size, which will return a slice of only the requested196// length.197let slice: &[u32] = unsafe { allocation.as_slice(5) };198assert_eq!(slice.len(), 5);199}200201#[test]202fn test_as_slice_u32_larger_len() {203let layout = Layout::from_size_align(size_of::<u32>() * 15, align_of::<u32>()).unwrap();204let allocation = LayoutAllocation::zeroed(layout);205206// SAFETY:207// Slice more than the allocation size, which will clamp the returned slice len to the208// limit.209let slice: &[u32] = unsafe { allocation.as_slice(100) };210assert_eq!(slice.len(), 15);211}212213#[test]214fn test_as_slice_u32_remainder() {215// Allocate a buffer that is not a multiple of u32 in size.216let layout = Layout::from_size_align(size_of::<u32>() * 15 + 2, align_of::<u32>()).unwrap();217let allocation = LayoutAllocation::zeroed(layout);218219// SAFETY:220// Slice as many u32s as possible, which should return a slice that only includes the full221// u32s, not the trailing 2 bytes.222let slice: &[u32] = unsafe { allocation.as_slice(100) };223assert_eq!(slice.len(), 15);224}225}226227228