use std::{
cell::UnsafeCell,
mem::{self, align_of, replace, size_of},
ptr,
};
use crate::DropStack;
#[derive(Clone)]
pub(crate) struct Allocation {
pub base: *mut u8,
pub len: usize,
pub capacity: usize,
}
impl Allocation {
pub fn get_slice<'a, T>(
&mut self,
parent: &'a UnsafeCell<Allocation>,
len: usize,
) -> (DropStack<'a>, (*mut T, usize)) {
unsafe {
let required_bytes_pessimistic = (align_of::<T>() - 1) + (size_of::<T>() * len);
self.ensure_capacity(required_bytes_pessimistic);
let restore = self.clone();
let base = self.base.offset(self.len as isize);
let align = base.align_offset(align_of::<T>());
let ptr = base.offset(align as isize);
self.len += align + (size_of::<T>() * len);
(
DropStack {
restore,
location: parent,
},
(ptr as *mut T, len),
)
}
}
fn ensure_capacity(&mut self, capacity: usize) {
if self.remaining_bytes() < capacity {
let mut new_capacity = 64.max(self.capacity * 2);
while new_capacity < capacity {
new_capacity *= 2;
}
let mut dealloc = replace(self, Allocation::new(new_capacity));
dealloc.try_dealloc();
}
}
pub fn ref_eq(&self, other: &Self) -> bool {
self.base == other.base
}
pub fn null() -> Self {
Self {
base: ptr::null_mut(),
len: 0,
capacity: 0,
}
}
pub fn remaining_bytes(&self) -> usize {
self.capacity - self.len
}
pub fn new(size_in_bytes: usize) -> Self {
let mut v = Vec::<u8>::with_capacity(size_in_bytes);
let base = v.as_mut_ptr();
mem::forget(v);
Self {
base,
len: 0,
capacity: size_in_bytes,
}
}
pub unsafe fn force_dealloc(&mut self) {
if self.base == ptr::null_mut() {
return;
}
drop(Vec::from_raw_parts(self.base, 0, self.capacity));
self.base = ptr::null_mut();
}
pub fn try_dealloc(&mut self) {
if self.len != 0 {
return;
}
unsafe { self.force_dealloc() }
}
}