use core::{
alloc::{AllocError, Allocator, Layout},
ops::Range,
ptr::{NonNull, null},
};
use crate::Subscriber;
#[cfg(feature = "std")]
type DefaultAllocator = std::alloc::Global;
#[cfg(not(feature = "std"))]
type DefaultAllocator = crate::single_allocation::SingleAllocation;
pub struct Guard<A: Allocator = DefaultAllocator> {
allocator: A,
allocation: Option<(NonNull<[u8]>, Layout)>,
}
pub(crate) trait GuardTrait {
unsafe fn allocate(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>;
}
impl<A: Allocator> GuardTrait for Guard<A> {
unsafe fn allocate(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
let ptr = self.allocator.allocate(layout)?;
self.allocation = Some((ptr, layout));
Ok(ptr)
}
}
impl Default for Guard {
fn default() -> Self {
Self::new()
}
}
impl Guard {
#[cfg(feature = "std")]
pub const fn new() -> Self {
Self::new_in(std::alloc::Global)
}
pub fn as_ptr(&self) -> *const u8 {
if let Some((ptr, _)) = self.allocation {
ptr.as_ptr() as *const u8
} else {
null()
}
}
pub fn as_ptr_range(&self) -> Range<*const u8> {
if let Some((_, layout)) = self.allocation {
let ptr = self.as_ptr();
ptr..(ptr.wrapping_add(layout.size()))
} else {
null()..null()
}
}
}
impl<A: Allocator> Guard<A> {
pub const fn new_in(allocator: A) -> Self {
Self {
allocator,
allocation: None,
}
}
pub fn subscriber(&mut self) -> Subscriber<'_, '_> {
if self.allocation.is_some() {
panic!("This Guard has already been used for an allocation");
}
Subscriber::new(self)
}
}
impl<A: core::alloc::Allocator> Drop for Guard<A> {
fn drop(&mut self) {
if let Some((ptr, layout)) = self.allocation {
unsafe { self.allocator.deallocate(ptr.as_non_null_ptr(), layout) }
}
}
}