#![no_std]
extern crate ptr as ptr_;
use ptr_::Unique;
use core::{cmp, fmt, mem, usize, ops::DerefMut, num::NonZeroUsize,
ptr::{self, NonNull}};
#[derive(Debug)]
pub struct Excess(pub NonNull<u8>, pub usize);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Layout {
size: usize,
align: NonZeroUsize,
}
impl Layout {
#[inline]
pub fn from_size_align(size: usize, align: usize) -> Option<Layout> {
if !align.is_power_of_two() {
return None;
}
if align > (1 << 31) {
return None;
}
if size > usize::MAX - (align - 1) {
return None;
}
unsafe { Some(Layout::from_size_align_unchecked(size, align)) }
}
#[inline]
pub const unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Layout {
Layout { size, align: NonZeroUsize::new_unchecked(align) }
}
#[inline]
pub fn size(&self) -> usize { self.size }
#[inline]
pub fn align(&self) -> NonZeroUsize { self.align }
#[inline]
pub fn new<T>() -> Self {
Layout { size: mem::size_of::<T>(),
align: unsafe { NonZeroUsize::new_unchecked(mem::align_of::<T>()) } }
}
#[inline]
pub fn for_value<T: ?Sized>(t: &T) -> Self {
let (size, align) = (mem::size_of_val(t), mem::align_of_val(t));
Layout::from_size_align(size, align).unwrap()
}
#[inline]
pub fn align_to(&self, align: usize) -> Self {
Layout::from_size_align(self.size, cmp::max(self.align.get(), align)).unwrap()
}
#[inline]
pub fn padding_needed_for(&self, align: NonZeroUsize) -> usize {
let len = self.size();
let align = align.get();
(len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1))
.wrapping_sub(len)
}
#[inline]
pub fn pad_to(&self, align: NonZeroUsize) -> Self {
Layout {
size: {
let align = align.get();
self.size().wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1)
},
align: self.align(),
}
}
#[inline]
pub fn repeat(&self, n: usize) -> Option<(Self, usize)> {
let padded_size = self.size.checked_add(self.padding_needed_for(self.align))?;
let alloc_size = padded_size.checked_mul(n)?;
Some((Layout::from_size_align(alloc_size, self.align.get()).unwrap(), padded_size))
}
#[inline]
pub fn extend(&self, next: Self) -> Option<(Self, usize)> {
let new_align = cmp::max(self.align, next.align);
let realigned = Layout::from_size_align(self.size, new_align.get())?;
let pad = realigned.padding_needed_for(next.align);
let offset = self.size.checked_add(pad)?;
let new_size = offset.checked_add(next.size)?;
Some((Layout::from_size_align(new_size, new_align.get())?, offset))
}
#[inline]
pub fn repeat_packed(&self, n: usize) -> Option<Self> {
Layout::from_size_align(self.size().checked_mul(n)?, self.align.get())
}
#[inline]
pub fn extend_packed(&self, next: Self) -> Option<(Self, usize)> {
let new_size = self.size().checked_add(next.size())?;
Some((Layout::from_size_align(new_size, self.align.get())?, self.size()))
}
#[inline]
pub fn array<T>(n: usize) -> Option<Self> {
Layout::new::<T>().repeat(n).map(|(k, offs)| {
debug_assert!(offs == mem::size_of::<T>());
k
})
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum AllocErr {
Exhausted { request: Layout },
Unsupported { details: &'static str },
}
impl AllocErr {
#[inline]
pub fn invalid_input(details: &'static str) -> Self {
AllocErr::Unsupported { details }
}
#[inline]
pub fn is_memory_exhausted(&self) -> bool {
if let AllocErr::Exhausted { .. } = *self { true } else { false }
}
#[inline]
pub fn is_request_unsupported(&self) -> bool {
if let AllocErr::Unsupported { .. } = *self { true } else { false }
}
#[inline]
pub fn description(&self) -> &str {
match *self {
AllocErr::Exhausted { .. } => "allocator memory exhausted",
AllocErr::Unsupported { .. } => "unsupported allocator request",
}
}
}
impl fmt::Display for AllocErr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.description()) }
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct CannotReallocInPlace;
impl CannotReallocInPlace {
pub fn description(&self) -> &str { "cannot reallocate allocator's memory in place" }
}
impl fmt::Display for CannotReallocInPlace {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.description()) }
}
pub unsafe trait Alloc {
unsafe fn alloc(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocErr>;
unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout);
#[inline]
fn usable_size(&self, layout: Layout) -> (usize, usize) { (layout.size(), layout.size()) }
unsafe fn realloc(&mut self,
ptr: NonNull<u8>,
layout: Layout,
new_size: usize) -> Result<NonNull<u8>, AllocErr> {
let old_size = layout.size();
if let Ok(()) = self.resize_in_place(ptr, layout, new_size) {
return Ok(ptr);
}
let result = self.alloc(Layout { size: new_size, align: layout.align() });
if let Ok(new_ptr) = result {
ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_ptr(),
cmp::min(old_size, new_size));
self.dealloc(ptr, layout);
}
result
}
#[inline]
unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocErr> {
let size = layout.size();
let r = self.alloc(layout);
if let Ok(p) = r {
ptr::write_bytes(p.as_ptr(), 0, size);
}
r
}
#[inline]
unsafe fn alloc_excess(&mut self, layout: Layout) -> Result<Excess, AllocErr> {
let usable_size = self.usable_size(layout);
self.alloc(layout).map(|p| Excess(p, usable_size.1))
}
#[inline]
unsafe fn realloc_excess(&mut self,
ptr: NonNull<u8>,
layout: Layout,
new_size: usize) -> Result<Excess, AllocErr> {
let new_layout = Layout { size: new_size, align: layout.align() };
let usable_size = self.usable_size(new_layout);
self.realloc(ptr, layout, new_size).map(|p| Excess(p, usable_size.1))
}
#[inline]
unsafe fn resize_in_place(&mut self,
ptr: NonNull<u8>,
layout: Layout,
new_size: usize) -> Result<(), CannotReallocInPlace> {
let _ = ptr; let (l, u) = self.usable_size(layout);
if u >= new_size && l <= new_size { Ok(()) }
else { Err(CannotReallocInPlace) }
}
#[inline]
fn alloc_one<T>(&mut self) -> Result<Unique<T>, AllocErr> {
let k = Layout::new::<T>();
if k.size() > 0 {
unsafe { self.alloc(k).map(|p| p.cast().into()) }
} else {
Err(AllocErr::invalid_input("zero-sized type invalid for alloc_one"))
}
}
#[inline]
unsafe fn dealloc_one<T>(&mut self, ptr: Unique<T>) {
let k = Layout::new::<T>();
if k.size() > 0 { self.dealloc(ptr.as_ptr().cast(), k); }
}
#[inline]
fn alloc_array<T>(&mut self, n: usize) -> Result<(Unique<T>, usize), AllocErr> {
match Layout::array::<T>(n) {
Some(ref layout) if layout.size() > 0 => { unsafe {
self.alloc_excess(layout.clone())
.map(|Excess(p, n)| (p.cast().into(), n / mem::size_of::<T>()))
} },
_ => Err(AllocErr::invalid_input("invalid layout for alloc_array")),
}
}
#[inline]
unsafe fn realloc_array<T>(&mut self,
ptr: Unique<T>,
n_old: usize,
n_new: usize) -> Result<(Unique<T>, usize), AllocErr> {
match (Layout::array::<T>(n_old), Layout::array::<T>(n_new), ptr.as_ptr()) {
(Some(ref k_old), Some(ref k_new), ptr) if k_old.size() > 0 && k_new.size() > 0 => {
self.realloc_excess(ptr.cast(), k_old.clone(), k_new.size())
.map(|Excess(p, n)| (p.cast().into(),
n / mem::size_of::<T>()))
}
_ => {
Err(AllocErr::invalid_input("invalid layout for realloc_array"))
},
}
}
#[inline]
unsafe fn dealloc_array<T>(&mut self, ptr: Unique<T>, n: usize) -> Result<(), AllocErr> {
match Layout::array::<T>(n) {
Some(ref k) if k.size() > 0 => {
Ok(self.dealloc(ptr.as_ptr().cast(), k.clone()))
},
_ => {
Err(AllocErr::invalid_input("invalid layout for dealloc_array"))
},
}
}
}
#[derive(Clone, Copy, Default, Debug)]
pub struct NullAllocator(());
unsafe impl Alloc for NullAllocator {
#[inline] unsafe fn alloc(&mut self, _: Layout) -> Result<NonNull<u8>, AllocErr> { Err(AllocErr::Unsupported { details: "" }) }
#[inline] unsafe fn dealloc(&mut self, _: NonNull<u8>, _: Layout) {}
}
unsafe impl<A: Alloc + ?Sized, P: DerefMut<Target = A>> Alloc for P {
#[inline] unsafe fn alloc(&mut self, l: Layout) -> Result<NonNull<u8>, AllocErr> { self.deref_mut().alloc(l) }
#[inline] unsafe fn dealloc(&mut self, ptr: NonNull<u8>, l: Layout) { self.deref_mut().dealloc(ptr, l) }
#[inline] unsafe fn realloc(&mut self, ptr: NonNull<u8>, old_l: Layout, new_size: usize) -> Result<NonNull<u8>, AllocErr> { self.deref_mut().realloc(ptr, old_l, new_size) }
#[inline] unsafe fn alloc_zeroed(&mut self, l: Layout) -> Result<NonNull<u8>, AllocErr> { self.deref_mut().alloc_zeroed(l) }
#[inline] unsafe fn alloc_excess(&mut self, l: Layout) -> Result<Excess, AllocErr> { self.deref_mut().alloc_excess(l) }
#[inline] unsafe fn realloc_excess(&mut self, ptr: NonNull<u8>, old_l: Layout, new_size: usize) -> Result<Excess, AllocErr> { self.deref_mut().realloc_excess(ptr, old_l, new_size) }
#[inline] unsafe fn resize_in_place(&mut self, ptr: NonNull<u8>, old_l: Layout, new_size: usize) -> Result<(), CannotReallocInPlace> { self.deref_mut().resize_in_place(ptr, old_l, new_size) }
#[inline] fn usable_size(&self, l: Layout) -> (usize, usize) { self.deref().usable_size(l) }
#[inline] fn alloc_one<T>(&mut self) -> Result<Unique<T>, AllocErr> { self.deref_mut().alloc_one() }
#[inline] unsafe fn dealloc_one<T>(&mut self, ptr: Unique<T>) { self.deref_mut().dealloc_one(ptr) }
#[inline] fn alloc_array<T>(&mut self, n: usize) -> Result<(Unique<T>, usize), AllocErr> { self.deref_mut().alloc_array(n) }
#[inline] unsafe fn realloc_array<T>(&mut self, ptr: Unique<T>, old_n: usize, new_n: usize) -> Result<(Unique<T>, usize), AllocErr> { self.deref_mut().realloc_array(ptr, old_n, new_n) }
#[inline] unsafe fn dealloc_array<T>(&mut self, ptr: Unique<T>, n: usize) -> Result<(), AllocErr> { self.deref_mut().dealloc_array(ptr, n) }
}