use super::{
are_blocks_on_same_page, AllocationType, Region, Suballocation, SuballocationNode,
SuballocationType, Suballocator, SuballocatorError,
};
use crate::{
memory::{
allocator::{align_up, AllocationHandle, DeviceLayout},
DeviceAlignment,
},
DeviceSize,
};
use std::iter::FusedIterator;
#[derive(Debug)]
pub struct BumpAllocator {
region: Region,
free_start: DeviceSize,
prev_allocation_type: AllocationType,
}
impl BumpAllocator {
fn suballocation_node(&self, part: usize) -> SuballocationNode {
if part == 0 {
SuballocationNode {
offset: self.region.offset(),
size: self.free_start,
allocation_type: self.prev_allocation_type.into(),
}
} else {
debug_assert_eq!(part, 1);
SuballocationNode {
offset: self.region.offset() + self.free_start,
size: self.free_size(),
allocation_type: SuballocationType::Free,
}
}
}
}
unsafe impl Suballocator for BumpAllocator {
type Suballocations<'a> = Suballocations<'a>;
fn new(region: Region) -> Self {
BumpAllocator {
region,
free_start: 0,
prev_allocation_type: AllocationType::Unknown,
}
}
#[inline]
fn allocate(
&mut self,
layout: DeviceLayout,
allocation_type: AllocationType,
buffer_image_granularity: DeviceAlignment,
) -> Result<Suballocation, SuballocatorError> {
fn has_granularity_conflict(prev_ty: AllocationType, ty: AllocationType) -> bool {
prev_ty == AllocationType::Unknown || prev_ty != ty
}
let size = layout.size();
let alignment = layout.alignment();
let prev_end = self.region.offset() + self.free_start;
let mut offset = align_up(prev_end, alignment);
if buffer_image_granularity != DeviceAlignment::MIN
&& prev_end > 0
&& are_blocks_on_same_page(0, prev_end, offset, buffer_image_granularity)
&& has_granularity_conflict(self.prev_allocation_type, allocation_type)
{
offset = align_up(offset, buffer_image_granularity);
}
let relative_offset = offset - self.region.offset();
let free_start = relative_offset + size;
if free_start > self.region.size() {
return Err(SuballocatorError::OutOfRegionMemory);
}
self.free_start = free_start;
self.prev_allocation_type = allocation_type;
Ok(Suballocation {
offset,
size,
allocation_type,
handle: AllocationHandle::null(),
})
}
#[inline]
unsafe fn deallocate(&mut self, _suballocation: Suballocation) {
}
#[inline]
fn reset(&mut self) {
self.free_start = 0;
self.prev_allocation_type = AllocationType::Unknown;
}
#[inline]
fn free_size(&self) -> DeviceSize {
self.region.size() - self.free_start
}
#[inline]
fn suballocations(&self) -> Self::Suballocations<'_> {
let start = if self.free_start == 0 { 1 } else { 0 };
let end = if self.free_start == self.region.size() {
1
} else {
2
};
Suballocations {
allocator: self,
start,
end,
}
}
}
#[derive(Clone)]
pub struct Suballocations<'a> {
allocator: &'a BumpAllocator,
start: usize,
end: usize,
}
impl Iterator for Suballocations<'_> {
type Item = SuballocationNode;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.len() != 0 {
let node = self.allocator.suballocation_node(self.start);
self.start += 1;
Some(node)
} else {
None
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.len();
(len, Some(len))
}
#[inline]
fn last(mut self) -> Option<Self::Item> {
self.next_back()
}
}
impl DoubleEndedIterator for Suballocations<'_> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
if self.len() != 0 {
self.end -= 1;
let node = self.allocator.suballocation_node(self.end);
Some(node)
} else {
None
}
}
}
impl ExactSizeIterator for Suballocations<'_> {
#[inline]
fn len(&self) -> usize {
self.end - self.start
}
}
impl FusedIterator for Suballocations<'_> {}