use core::mem::{MaybeUninit, align_of, size_of};
use super::aligned_vec::AlignedVec;
use super::alloc::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StackReq {
pub size: usize,
pub align: usize,
}
impl StackReq {
pub const ZERO: StackReq = StackReq { size: 0, align: 1 };
#[inline]
pub const fn new(size: usize, align: usize) -> Self {
StackReq { size, align }
}
#[inline]
pub const fn new_for<T>(count: usize) -> Self {
StackReq {
size: count * size_of::<T>(),
align: align_of::<T>(),
}
}
#[inline]
pub const fn and(self, other: Self) -> Self {
let align = if self.align > other.align {
self.align
} else {
other.align
};
let size1 = round_up_pow2(self.size, other.align);
StackReq {
size: size1 + other.size,
align,
}
}
#[inline]
pub const fn or(self, other: Self) -> Self {
let align = if self.align > other.align {
self.align
} else {
other.align
};
let size = if self.size > other.size {
self.size
} else {
other.size
};
StackReq { size, align }
}
#[inline]
pub const fn with_align(self, align: usize) -> Self {
let new_align = if self.align > align {
self.align
} else {
align
};
StackReq {
size: self.size,
align: new_align,
}
}
}
#[macro_export]
macro_rules! stack_req_all {
($($req:expr),* $(,)?) => {{
let mut result = $crate::memory::StackReq::ZERO;
$(
result = result.and($req);
)*
result
}};
}
#[macro_export]
macro_rules! stack_req_any {
($($req:expr),* $(,)?) => {{
let mut result = $crate::memory::StackReq::ZERO;
$(
result = result.or($req);
)*
result
}};
}
pub struct MemStack {
buffer: AlignedVec<u8>,
offset: usize,
}
impl MemStack {
pub fn new(req: StackReq) -> Self {
let size = round_up_pow2(req.size, req.align);
MemStack {
buffer: AlignedVec::zeros(size),
offset: 0,
}
}
pub fn with_size(size: usize) -> Self {
MemStack {
buffer: AlignedVec::zeros(size),
offset: 0,
}
}
#[inline]
pub fn remaining(&self) -> usize {
self.buffer.len() - self.offset
}
#[inline]
pub fn reset(&mut self) {
self.offset = 0;
}
pub fn alloc<T>(&mut self, count: usize) -> &mut [MaybeUninit<T>] {
let align = align_of::<T>();
let aligned_offset = round_up_pow2(self.offset, align);
let size = count * size_of::<T>();
let new_offset = aligned_offset + size;
assert!(new_offset <= self.buffer.len(), "MemStack overflow");
let ptr = unsafe { self.buffer.as_mut_ptr().add(aligned_offset) as *mut MaybeUninit<T> };
self.offset = new_offset;
unsafe { core::slice::from_raw_parts_mut(ptr, count) }
}
pub fn alloc_zeroed<T: bytemuck::Zeroable>(&mut self, count: usize) -> &mut [T] {
let slice = self.alloc::<T>(count);
unsafe {
core::ptr::write_bytes(slice.as_mut_ptr() as *mut u8, 0, count * size_of::<T>());
core::slice::from_raw_parts_mut(slice.as_mut_ptr() as *mut T, count)
}
}
pub fn make_sub_stack(&mut self) -> MemStack {
let _remaining = self.remaining();
let _ptr = unsafe { self.buffer.as_mut_ptr().add(self.offset) };
self.offset = self.buffer.len();
MemStack {
buffer: AlignedVec::new(),
offset: 0,
}
}
}