use crate::prelude::*;
use core::fmt;
use core::ptr::NonNull;
pub struct RawAlloc<A: Allocator = Global> {
ptr: NonNull<[u8]>,
layout: Layout,
allocator: A,
}
impl<A: Allocator> RawAlloc<A> {
pub fn new_in(layout: Layout, allocator: A) -> Result<Self, AllocError> {
if layout.size() == 0 {
return Err(AllocError);
}
let ptr = allocator.allocate(layout)?;
Ok(Self {
ptr,
layout,
allocator,
})
}
pub fn new_zeroed_in(layout: Layout, allocator: A) -> Result<Self, AllocError> {
if layout.size() == 0 {
return Err(AllocError);
}
let ptr = allocator.allocate_zeroed(layout)?;
Ok(Self {
ptr,
layout,
allocator,
})
}
pub fn grow(&mut self, new_layout: Layout) -> Result<(), AllocError> {
if new_layout.size() == 0 {
return Err(AllocError);
}
if new_layout.size() <= self.layout.size() {
return Err(AllocError);
}
let new_ptr = unsafe {
self.allocator.grow(
NonNull::new_unchecked(self.ptr.as_ptr() as *mut u8),
self.layout,
new_layout,
)?
};
self.ptr = new_ptr;
self.layout = new_layout;
Ok(())
}
pub fn grow_zeroed(&mut self, new_layout: Layout) -> Result<(), AllocError> {
if new_layout.size() == 0 {
return Err(AllocError);
}
if new_layout.size() <= self.layout.size() {
return Err(AllocError);
}
let new_ptr = unsafe {
self.allocator.grow_zeroed(
NonNull::new_unchecked(self.ptr.as_ptr() as *mut u8),
self.layout,
new_layout,
)?
};
self.ptr = new_ptr;
self.layout = new_layout;
Ok(())
}
pub fn shrink(&mut self, new_layout: Layout) -> Result<(), AllocError> {
if new_layout.size() == 0 {
return Err(AllocError);
}
if new_layout.size() >= self.layout.size() {
return Err(AllocError);
}
let new_ptr = unsafe {
self.allocator.shrink(
NonNull::new_unchecked(self.ptr.as_ptr() as *mut u8),
self.layout,
new_layout,
)?
};
self.ptr = new_ptr;
self.layout = new_layout;
Ok(())
}
pub fn as_ptr(&self) -> *const u8 {
self.ptr.as_ptr() as *const u8
}
pub fn as_mut_ptr(&mut self) -> *mut u8 {
self.ptr.as_ptr() as *mut u8
}
pub fn as_slice(&self) -> &[u8] {
unsafe { core::slice::from_raw_parts(self.as_ptr(), self.layout.size()) }
}
pub fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe { core::slice::from_raw_parts_mut(self.as_mut_ptr(), self.layout.size()) }
}
pub fn layout(&self) -> Layout {
self.layout
}
pub fn len(&self) -> usize {
self.layout.size()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl<A: Allocator> Drop for RawAlloc<A> {
fn drop(&mut self) {
unsafe {
self.allocator.deallocate(
NonNull::new_unchecked(self.ptr.as_ptr() as *mut u8),
self.layout,
);
}
}
}
impl<A: Allocator> fmt::Debug for RawAlloc<A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RawAlloc")
.field("ptr", &self.ptr)
.field("layout", &self.layout)
.finish()
}
}
impl RawAlloc {
pub fn new(layout: Layout) -> Result<Self, AllocError> {
Self::new_in(layout, Global)
}
pub fn new_zeroed(layout: Layout) -> Result<Self, AllocError> {
Self::new_zeroed_in(layout, Global)
}
}
unsafe impl<A: Allocator> Send for RawAlloc<A> {}
unsafe impl<A: Allocator> Sync for RawAlloc<A> {}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec::Vec;
use core::mem::size_of;
#[test]
fn zero_sized_alloc_returns_error() {
let layout = Layout::from_size_align(0, 1).unwrap();
assert!(RawAlloc::new(layout).is_err());
}
#[test]
fn basic_alloc_and_write() {
let layout = Layout::new::<u32>();
let mut alloc = RawAlloc::new(layout).unwrap();
unsafe {
core::ptr::write(alloc.as_mut_ptr() as *mut u32, 0xDEADBEEF);
assert_eq!(core::ptr::read(alloc.as_ptr() as *const u32), 0xDEADBEEF);
}
}
#[test]
fn zeroed_allocation() {
let size = 1024;
let layout = Layout::array::<u8>(size).unwrap();
let alloc = RawAlloc::new_zeroed(layout).unwrap();
unsafe {
let slice = core::slice::from_raw_parts(alloc.as_ptr(), size);
assert!(slice.iter().all(|&x| x == 0));
}
}
#[test]
fn custom_allocator() {
let layout = Layout::new::<i32>();
let alloc = RawAlloc::new_in(layout, Global).unwrap();
assert_eq!(alloc.layout().size(), size_of::<i32>());
}
#[test]
fn array_allocation() {
let elements = 100;
let layout = Layout::array::<u64>(elements).unwrap();
let mut alloc = RawAlloc::new(layout).unwrap();
unsafe {
let slice = core::slice::from_raw_parts_mut(alloc.as_mut_ptr() as *mut u64, elements);
for (i, item) in slice.iter_mut().enumerate() {
*item = i as u64;
}
assert_eq!(slice[42], 42);
}
}
#[test]
fn alignment_requirements() {
let align = 64; let size = 128;
let layout = Layout::from_size_align(size, align).unwrap();
let alloc = RawAlloc::new(layout).unwrap();
let addr = alloc.as_ptr() as usize;
assert_eq!(addr % align, 0, "Allocation not properly aligned");
}
#[test]
fn multiple_allocations() {
let layout = Layout::new::<u8>();
let mut allocations = Vec::new();
for i in 0..100 {
let mut alloc = RawAlloc::new(layout).unwrap();
unsafe {
core::ptr::write(alloc.as_mut_ptr(), i as u8);
}
allocations.push(alloc);
}
for (i, alloc) in allocations.iter().enumerate() {
unsafe {
assert_eq!(core::ptr::read(alloc.as_ptr()), i as u8);
}
}
}
#[test]
fn oversized_allocation() {
let layout = Layout::array::<u8>(1024 * 1024).unwrap();
let result = RawAlloc::new(layout);
let _ = result.is_ok();
}
#[test]
fn grow_allocation() {
let initial_size = 100;
let layout = Layout::array::<u8>(initial_size).unwrap();
let mut alloc = RawAlloc::new(layout).unwrap();
unsafe {
let slice = core::slice::from_raw_parts_mut(alloc.as_mut_ptr(), initial_size);
slice[0] = 42;
}
let new_size = 200;
let new_layout = Layout::array::<u8>(new_size).unwrap();
alloc.grow(new_layout).unwrap();
unsafe {
let slice = core::slice::from_raw_parts(alloc.as_ptr(), new_size);
assert_eq!(slice[0], 42);
}
}
#[test]
fn grow_zeroed_allocation() {
let initial_size = 100;
let layout = Layout::array::<u8>(initial_size).unwrap();
let mut alloc = RawAlloc::new(layout).unwrap();
unsafe {
let slice = core::slice::from_raw_parts_mut(alloc.as_mut_ptr(), initial_size);
slice[0] = 42;
}
let new_size = 200;
let new_layout = Layout::array::<u8>(new_size).unwrap();
alloc.grow_zeroed(new_layout).unwrap();
unsafe {
let slice = core::slice::from_raw_parts(alloc.as_ptr(), new_size);
assert_eq!(slice[0], 42);
assert!(slice[initial_size..].iter().all(|&x| x == 0));
}
}
#[test]
fn shrink_allocation() {
let initial_size = 200;
let layout = Layout::array::<u8>(initial_size).unwrap();
let mut alloc = RawAlloc::new(layout).unwrap();
unsafe {
let slice = core::slice::from_raw_parts_mut(alloc.as_mut_ptr(), initial_size);
slice[0] = 42;
}
let new_size = 100;
let new_layout = Layout::array::<u8>(new_size).unwrap();
alloc.shrink(new_layout).unwrap();
unsafe {
let slice = core::slice::from_raw_parts(alloc.as_ptr(), new_size);
assert_eq!(slice[0], 42);
}
}
#[test]
fn grow_zero_size_fails() {
let layout = Layout::array::<u8>(100).unwrap();
let mut alloc = RawAlloc::new(layout).unwrap();
let new_layout = Layout::from_size_align(0, 1).unwrap();
assert!(alloc.grow(new_layout).is_err());
}
#[test]
fn shrink_zero_size_fails() {
let layout = Layout::array::<u8>(100).unwrap();
let mut alloc = RawAlloc::new(layout).unwrap();
let new_layout = Layout::from_size_align(0, 1).unwrap();
assert!(alloc.shrink(new_layout).is_err());
}
#[test]
fn grow_smaller_size_fails() {
let layout = Layout::array::<u8>(200).unwrap();
let mut alloc = RawAlloc::new(layout).unwrap();
let new_layout = Layout::array::<u8>(100).unwrap();
assert!(alloc.grow(new_layout).is_err());
}
#[test]
fn shrink_larger_size_fails() {
let layout = Layout::array::<u8>(100).unwrap();
let mut alloc = RawAlloc::new(layout).unwrap();
let new_layout = Layout::array::<u8>(200).unwrap();
assert!(alloc.shrink(new_layout).is_err());
}
}