use core::alloc::Layout;
use core::ptr::NonNull;
use allocator_api2::alloc::{AllocError, Allocator};
#[allow(
clippy::map_err_ignore,
reason = "LayoutError carries no payload; only the AllocError variant matters"
)]
#[inline]
pub(crate) fn chunk_layout(header_size: usize, payload_size: usize, value_align: usize, base_align: usize) -> Result<Layout, AllocError> {
debug_assert!(value_align.is_power_of_two(), "value_align must be a power of two");
debug_assert!(base_align.is_power_of_two(), "base_align must be a power of two");
debug_assert!(base_align >= value_align, "base_align must be >= value_align");
let rounded = chunk_alloc_size(header_size, payload_size, value_align)?;
Layout::from_size_align(rounded, base_align).map_err(|_| AllocError)
}
#[inline]
pub(crate) fn chunk_alloc_size(header_size: usize, payload_size: usize, value_align: usize) -> Result<usize, AllocError> {
debug_assert!(value_align.is_power_of_two(), "value_align must be a power of two");
let total = header_size.checked_add(payload_size).ok_or(AllocError)?;
let mask = value_align - 1;
Ok(total.checked_add(mask).ok_or(AllocError)? & !mask)
}
#[cfg_attr(test, mutants::skip)]
#[inline]
pub(crate) fn alloc_chunk_raw<A: Allocator>(
allocator: &A,
header_size: usize,
payload_size: usize,
value_align: usize,
base_align: usize,
) -> Result<(*mut u8, Layout), AllocError> {
let layout = chunk_layout(header_size, payload_size, value_align, base_align)?;
let raw = allocator.allocate(layout)?;
let raw_u8_ptr: *mut u8 = raw.cast::<u8>().as_ptr();
let start_addr = raw_u8_ptr as usize;
let end_addr = start_addr.checked_add(layout.size()).ok_or(AllocError)?;
if end_addr > isize::MAX as usize {
unsafe {
allocator.deallocate(NonNull::new_unchecked(raw_u8_ptr), layout);
}
return Err(AllocError);
}
Ok((raw_u8_ptr, layout))
}
#[cfg(test)]
mod tests {
use super::chunk_layout;
#[test]
fn rounds_size_up_to_value_align() {
const BASE: usize = 65_536;
let cases = [
(10_usize, 7_usize, 8_usize, 24_usize), (34, 16, 8, 56), (1, 0, 8, 8), (10, 7, 16, 32), (5, 0, 4, 8), ];
for (header, payload, value_align, expected) in cases {
let layout = chunk_layout(header, payload, value_align, BASE).expect("layout fits");
assert_eq!(
layout.size(),
expected,
"round_up({header}+{payload}, {value_align}) must be {expected}"
);
assert_eq!(layout.size() % value_align, 0, "size must be a multiple of value_align");
assert_eq!(layout.align(), BASE, "alignment must be the base alignment");
}
}
#[test]
fn aligned_total_is_unchanged() {
const BASE: usize = 65_536;
let layout = chunk_layout(8, 8, 8, BASE).expect("layout fits"); assert_eq!(layout.size(), 16);
}
}