use crate::Result;
use core::{
alloc::{AllocErr, AllocInit, AllocRef, Layout, LayoutErr, MemoryBlock, ReallocPlacement},
marker::PhantomData,
mem::{self, MaybeUninit},
ptr::{self, NonNull},
};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Affix<Alloc, Prefix = (), Suffix = ()> {
pub alloc: Alloc,
_prefix: PhantomData<Prefix>,
_suffix: PhantomData<Suffix>,
}
impl<Alloc, Prefix, Suffix> Default for Affix<Alloc, Prefix, Suffix>
where
Alloc: Default,
{
fn default() -> Self {
Self::new(Alloc::default())
}
}
impl<Alloc, Prefix, Suffix> Affix<Alloc, Prefix, Suffix> {
pub const fn new(alloc: Alloc) -> Self {
Self {
alloc,
_prefix: PhantomData,
_suffix: PhantomData,
}
}
pub unsafe fn prefix(ptr: NonNull<u8>, layout: Layout) -> NonNull<Prefix> {
let prefix = Layout::new::<Prefix>();
let offset = prefix.size() + prefix.padding_needed_for(layout.align());
NonNull::new_unchecked(ptr.as_ptr().sub(offset)).cast()
}
pub unsafe fn suffix(ptr: NonNull<u8>, layout: Layout) -> NonNull<Suffix> {
let offset = layout.size() + layout.padding_needed_for(mem::align_of::<Suffix>());
NonNull::new_unchecked(ptr.as_ptr().add(offset)).cast()
}
fn extend_layout(layout: Layout) -> Result<(Layout, usize, usize), LayoutErr> {
let prefix_layout = Layout::new::<Prefix>();
let suffix_layout = Layout::new::<Suffix>();
let (layout, prefix_offset) = prefix_layout.extend(layout)?;
let (layout, suffix_offset) = layout.extend(suffix_layout)?;
Ok((layout, prefix_offset, suffix_offset))
}
}
unsafe impl<Alloc, Prefix, Suffix> AllocRef for Affix<Alloc, Prefix, Suffix>
where
Alloc: AllocRef,
{
fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result {
let (layout, offset_prefix, offset_suffix) =
Self::extend_layout(layout).map_err(|_| AllocErr)?;
let memory = self.alloc.alloc(layout, init)?;
Ok(MemoryBlock {
ptr: unsafe { NonNull::new_unchecked(memory.ptr.as_ptr().add(offset_prefix)) },
size: if mem::size_of::<Suffix>() == 0 {
memory.size - offset_prefix
} else {
offset_suffix - offset_prefix
},
})
}
unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout) {
let (layout, prefix_offset, _) = Self::extend_layout(layout).expect("Invalid layout");
let base_ptr = ptr.as_ptr().sub(prefix_offset);
self.alloc.dealloc(NonNull::new_unchecked(base_ptr), layout)
}
unsafe fn grow(
&mut self,
ptr: NonNull<u8>,
old_layout: Layout,
new_size: usize,
placement: ReallocPlacement,
init: AllocInit,
) -> Result {
let (old_alloc_layout, old_offset_prefix, old_offset_suffix) =
Self::extend_layout(old_layout).unwrap();
let ptr = ptr.as_ptr().sub(old_offset_prefix);
let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align());
let (new_alloc_layout, new_offset_prefix, new_offset_suffix) =
Self::extend_layout(new_layout).unwrap();
let suffix: MaybeUninit<Suffix> = ptr::read(ptr.add(old_offset_suffix) as *mut _);
let memory = self.alloc.grow(
NonNull::new_unchecked(ptr),
old_alloc_layout,
new_alloc_layout.size(),
placement,
init,
)?;
if init == AllocInit::Zeroed {
ptr::write_bytes(
memory.ptr.as_ptr().add(old_offset_suffix),
0,
mem::size_of::<Suffix>(),
);
}
ptr::write(memory.ptr.as_ptr().add(new_offset_suffix) as *mut _, suffix);
Ok(MemoryBlock {
ptr: NonNull::new_unchecked(memory.ptr.as_ptr().add(new_offset_prefix)),
size: if mem::size_of::<Suffix>() == 0 {
memory.size - new_offset_prefix
} else {
new_offset_suffix - new_offset_prefix
},
})
}
unsafe fn shrink(
&mut self,
ptr: NonNull<u8>,
old_layout: Layout,
new_size: usize,
placement: ReallocPlacement,
) -> Result {
let (old_alloc_layout, old_offset_prefix, old_offset_suffix) =
Self::extend_layout(old_layout).unwrap();
let ptr = ptr.as_ptr().sub(old_offset_prefix);
let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align());
let (new_alloc_layout, new_offset_prefix, new_offset_suffix) =
Self::extend_layout(new_layout).unwrap();
let suffix: MaybeUninit<Suffix> = ptr::read(ptr.add(old_offset_suffix) as *mut _);
let memory = self.alloc.shrink(
NonNull::new_unchecked(ptr),
old_alloc_layout,
new_alloc_layout.size(),
placement,
)?;
ptr::write(memory.ptr.as_ptr().add(new_offset_suffix) as *mut _, suffix);
Ok(MemoryBlock {
ptr: NonNull::new_unchecked(memory.ptr.as_ptr().add(new_offset_prefix)),
size: if mem::size_of::<Suffix>() == 0 {
memory.size - new_offset_prefix
} else {
new_offset_suffix - new_offset_prefix
},
})
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::wildcard_imports)]
use super::*;
use crate::{
helper::{AsSlice, Tracker},
Proxy,
};
use core::fmt;
use std::alloc::System;
#[allow(clippy::too_many_lines)]
fn test_alloc<Prefix, Suffix>(prefix: Prefix, layout: Layout, suffix: Suffix)
where
Prefix: fmt::Debug + Copy + PartialEq,
Suffix: fmt::Debug + Copy + PartialEq,
{
unsafe {
let mut alloc = Proxy {
alloc: Affix::<System, Prefix, Suffix>::default(),
callbacks: Tracker::default(),
};
let memory = alloc
.alloc(layout, AllocInit::Zeroed)
.unwrap_or_else(|_| panic!("Could not allocate {} bytes", layout.size()));
Affix::<System, Prefix, Suffix>::prefix(memory.ptr, layout)
.as_ptr()
.write(prefix);
Affix::<System, Prefix, Suffix>::suffix(memory.ptr, layout)
.as_ptr()
.write(suffix);
assert_eq!(
Affix::<System, Prefix, Suffix>::prefix(memory.ptr, layout).as_ref(),
&prefix
);
assert_eq!(memory.as_slice(), &vec![0_u8; memory.size][..]);
assert_eq!(
Affix::<System, Prefix, Suffix>::suffix(memory.ptr, layout).as_ref(),
&suffix
);
let growed_memory = alloc
.grow(
memory.ptr,
layout,
memory.size * 2,
ReallocPlacement::MayMove,
AllocInit::Zeroed,
)
.expect("Could not grow allocation");
let new_layout =
Layout::from_size_align(memory.size * 2, layout.align()).expect("Invalid layout");
assert_eq!(
Affix::<System, Prefix, Suffix>::prefix(growed_memory.ptr, new_layout).as_ref(),
&prefix
);
assert_eq!(
growed_memory.as_slice(),
&vec![0_u8; growed_memory.size][..]
);
assert_eq!(
Affix::<System, Prefix, Suffix>::suffix(growed_memory.ptr, new_layout).as_ref(),
&suffix
);
let memory = alloc
.shrink(
growed_memory.ptr,
new_layout,
layout.size(),
ReallocPlacement::MayMove,
)
.expect("Could not shrink allocation");
assert_eq!(
Affix::<System, Prefix, Suffix>::prefix(memory.ptr, layout).as_ref(),
&prefix
);
assert_eq!(memory.as_slice(), &vec![0_u8; memory.size][..]);
assert_eq!(
Affix::<System, Prefix, Suffix>::suffix(memory.ptr, layout).as_ref(),
&suffix
);
alloc.dealloc(memory.ptr, layout);
}
}
#[test]
fn test_alloc_u32_u64_u32() {
test_alloc::<u32, u32>(0xDEDE_DEDE, Layout::new::<u64>(), 0xEFEF_EFEF)
}
#[test]
fn test_alloc_zst_u64_zst() {
test_alloc::<(), ()>((), Layout::new::<u64>(), ())
}
#[test]
fn test_alloc_zst_u64_u32() {
test_alloc::<(), u32>((), Layout::new::<u64>(), 0xEFEF_EFEF)
}
#[test]
fn test_alloc_u32_u64_zst() {
test_alloc::<u32, ()>(0xDEDE_DEDE, Layout::new::<u64>(), ())
}
#[repr(align(1024))]
#[derive(Debug, Copy, Clone, PartialEq)]
struct AlignTo1024 {
a: u32,
}
#[repr(align(16))]
#[derive(Debug, Copy, Clone, PartialEq)]
struct AlignTo16;
#[test]
fn test_alloc_a1024_u64_zst() {
test_alloc::<AlignTo1024, ()>(AlignTo1024 { a: 0xDEDE_DEDE }, Layout::new::<u64>(), ())
}
#[test]
fn test_alloc_u32_u64_a1024() {
test_alloc::<u32, AlignTo1024>(0xDEDE_DEDE, Layout::new::<u64>(), AlignTo1024 {
a: 0xDEDE_DEDE,
})
}
#[test]
fn test_alloc_a16_u64_zst() {
test_alloc::<AlignTo16, ()>(AlignTo16, Layout::new::<u64>(), ())
}
#[test]
fn test_alloc_u32_u64_a16() {
test_alloc::<u32, AlignTo16>(0xDEDE_DEDE, Layout::new::<u64>(), AlignTo16)
}
}