use crate::core::{
num::NonZeroUsize,
ptr::{self, NonNull},
};
#[cfg(not(feature = "unstable"))]
use crate::core::ptr::{NonNullStrict, Strict};
#[derive(Copy, Clone, Debug)]
pub struct BasePtr {
ptr: NonNull<u8>,
extent: usize,
}
impl BasePtr {
#[inline]
pub fn new(ptr: NonNull<u8>, extent: usize) -> BasePtr {
ptr.addr()
.get()
.checked_add(extent)
.expect("region limit overflows usize");
BasePtr { ptr, extent }
}
#[inline]
pub fn ptr(self) -> NonNull<u8> {
self.ptr
}
#[inline]
pub fn limit(self) -> NonZeroUsize {
NonZeroUsize::new(self.ptr.addr().get() + self.extent).unwrap()
}
#[inline]
pub fn contains_addr(self, addr: NonZeroUsize) -> bool {
self.ptr.addr() <= addr && addr < self.limit()
}
#[inline]
pub fn addr(self) -> NonZeroUsize {
self.ptr.addr()
}
pub fn offset_to(self, block: NonZeroUsize) -> usize {
block.get().checked_sub(self.ptr.addr().get()).unwrap()
}
#[inline]
pub unsafe fn init_link_at(self, addr: NonZeroUsize, link: BlockLink) {
#[cfg(debug_assertions)]
{
debug_assert!(self.contains_addr(addr));
if let Some(next) = link.next {
debug_assert!(self.contains_addr(next), "next link out of region");
}
}
unsafe {
self.with_addr(addr)
.cast::<BlockLink>()
.as_ptr()
.write(link)
};
}
#[inline]
pub unsafe fn init_double_link_at(self, addr: NonZeroUsize, link: DoubleBlockLink) {
debug_assert!(self.contains_addr(addr));
debug_assert!(
link.next.map_or(true, |next| self.contains_addr(next)),
"next link out of region"
);
debug_assert!(
link.prev.map_or(true, |prev| self.contains_addr(prev),),
"prev link out of region"
);
unsafe {
self.with_addr(addr)
.cast::<DoubleBlockLink>()
.as_ptr()
.write(link)
};
}
#[inline]
pub unsafe fn link_mut<'a>(self, link: NonZeroUsize) -> &'a mut BlockLink {
debug_assert!(self.contains_addr(link));
unsafe { self.ptr.with_addr(link).cast::<BlockLink>().as_mut() }
}
#[inline]
pub unsafe fn double_link_mut<'a>(self, link: NonZeroUsize) -> &'a mut DoubleBlockLink {
debug_assert!(self.contains_addr(link));
let link = unsafe { self.ptr.with_addr(link).cast::<DoubleBlockLink>().as_mut() };
debug_assert!(
link.next.map_or(true, |next| self.contains_addr(next)),
"next link out of region"
);
debug_assert!(
link.prev.map_or(true, |prev| self.contains_addr(prev),),
"prev link out of region"
);
link
}
#[inline]
pub fn with_addr(self, addr: NonZeroUsize) -> NonNull<u8> {
debug_assert!(self.contains_addr(addr));
self.ptr.with_addr(addr)
}
#[inline]
pub fn with_addr_and_size(self, addr: NonZeroUsize, len: usize) -> NonNull<[u8]> {
debug_assert!(self.contains_addr(addr));
let ptr = self.ptr.as_ptr().with_addr(addr.get());
let raw_slice = ptr::slice_from_raw_parts_mut(ptr, len);
unsafe { NonNull::new_unchecked(raw_slice) }
}
#[inline]
pub fn with_offset(self, offset: usize) -> Option<NonNull<u8>> {
let raw = self.ptr.addr().get().checked_add(offset)?;
let addr = NonZeroUsize::new(raw)?;
debug_assert!(self.contains_addr(addr));
Some(self.ptr.with_addr(addr))
}
}
#[repr(C)]
pub struct BlockLink {
pub next: Option<NonZeroUsize>,
}
#[repr(C)]
#[derive(Debug)]
pub struct DoubleBlockLink {
pub prev: Option<NonZeroUsize>,
pub next: Option<NonZeroUsize>,
}