use std::ptr::NonNull;
use dupe::Dupe;
use crate::values::layout::aligned_size::AlignedSize;
use crate::values::layout::heap::allocator::alloc::chunk::Chunk;
#[derive(Debug, Default, PartialEq)]
pub(crate) struct ChunkPart {
allocation: Chunk,
begin: AlignedSize,
end: AlignedSize,
}
impl ChunkPart {
#[inline]
pub(crate) fn new(allocation: Chunk) -> ChunkPart {
let len = allocation.len();
ChunkPart::new_subslice(allocation, AlignedSize::ZERO, len)
}
#[inline]
pub(crate) fn new_subslice(
allocation: Chunk,
begin: AlignedSize,
end: AlignedSize,
) -> ChunkPart {
assert!(begin <= end);
assert!(end <= allocation.len());
ChunkPart {
allocation,
begin,
end,
}
}
#[inline]
pub(crate) fn alloc_at_least(len: AlignedSize) -> ChunkPart {
ChunkPart::new(Chunk::alloc_at_least(len))
}
#[inline]
pub(crate) fn len(&self) -> AlignedSize {
self.end.unchecked_sub(self.begin)
}
#[inline]
pub(crate) fn begin(&self) -> NonNull<usize> {
self.allocation.ptr_at_offset(self.begin)
}
#[inline]
pub(crate) fn ptr_at_offset(&self, offset: AlignedSize) -> NonNull<usize> {
debug_assert!(offset <= self.len());
self.allocation.ptr_at_offset(self.begin + offset)
}
#[inline]
pub(crate) fn end(&self) -> NonNull<usize> {
self.allocation.ptr_at_offset(self.end)
}
pub(crate) fn allocated_bytes_with_metadata(&self) -> usize {
if self.chunk_ref_count() == 1 {
self.allocation.allocated_bytes_with_metadata()
} else {
(self.len().bytes() + Chunk::HEADER_SIZE.bytes() / self.chunk_ref_count()) as usize
}
}
#[inline]
pub(crate) fn is_full(&self) -> bool {
self.len() == self.allocation.len()
}
pub(crate) fn split_at_offset(self, offset: AlignedSize) -> (ChunkPart, ChunkPart) {
if offset == AlignedSize::ZERO {
(ChunkPart::default(), self)
} else if offset == self.len() {
(self, ChunkPart::default())
} else {
assert!(offset <= self.len());
let offset_relative_to_chunk = self.begin + offset;
(
ChunkPart::new_subslice(
self.allocation.dupe(),
self.begin,
offset_relative_to_chunk,
),
ChunkPart::new_subslice(self.allocation, offset_relative_to_chunk, self.end),
)
}
}
#[inline]
pub(crate) fn chunk_ref_count(&self) -> u32 {
self.allocation.ref_count()
}
#[cfg(test)]
pub(crate) fn chunk_ptr_eq(&self, other: &ChunkPart) -> bool {
self.allocation.ptr_eq(&other.allocation)
}
}
#[cfg(test)]
mod tests {
use crate::values::layout::aligned_size::AlignedSize;
use crate::values::layout::heap::allocator::alloc::chunk::Chunk;
use crate::values::layout::heap::allocator::alloc::chunk_part::ChunkPart;
use crate::values::layout::heap::repr::AValueHeader;
#[test]
fn test_split_at() {
let chunk_part = ChunkPart::new(Chunk::alloc_at_least(AlignedSize::new_bytes(
100 * AValueHeader::ALIGN,
)));
let (a, b) = chunk_part.split_at_offset(AlignedSize::new_bytes(50 * AValueHeader::ALIGN));
assert_eq!(a.allocation, b.allocation);
assert_eq!(2, a.allocation.ref_count());
assert_eq!(a.end, b.begin);
drop(a);
assert_eq!(1, b.allocation.ref_count());
}
#[test]
fn test_split_at_zero() {
let chunk_part = ChunkPart::new(Chunk::alloc_at_least(AlignedSize::new_bytes(
100 * AValueHeader::ALIGN,
)));
let len = chunk_part.len();
let (a, b) = chunk_part.split_at_offset(AlignedSize::ZERO);
assert_eq!(AlignedSize::ZERO, a.len());
assert_eq!(0, a.chunk_ref_count());
assert_eq!(len, b.len());
assert_eq!(1, b.chunk_ref_count());
}
#[test]
fn test_is_full() {
let chunk_part = ChunkPart::new(Chunk::alloc_at_least(AlignedSize::new_bytes(
100 * AValueHeader::ALIGN,
)));
assert!(chunk_part.is_full());
}
}