use alloc::raw_vec::RawVec;
use alloc::allocator::Layout;
use core;
use std::cell::Cell;
pub struct StackAllocator {
stack: RawVec<u8>,
current_offset: Cell<*mut u8>,
}
impl StackAllocator {
pub fn with_capacity(capacity: usize) -> Self {
let stack = RawVec::with_capacity(capacity);
let current_offset = Cell::new(stack.ptr() as *mut u8);
StackAllocator {
stack,
current_offset,
}
}
pub fn stack(&self) -> &RawVec<u8> {
&self.stack
}
pub fn reset(&self) {
self.current_offset.set(self.stack.ptr());
}
pub fn alloc<T>(&self, value: T) -> &mut T {
let layout = Layout::new::<T>(); let offset = layout.align() + layout.size();
let old_stack_top = self.current_offset.get();
unsafe {
let unaligned_ptr = old_stack_top.offset(offset as isize) as usize;
let mask = layout.align() - 1;
let misalignment = unaligned_ptr & mask;
let adjustment = layout.align() - misalignment;
let aligned_ptr = (unaligned_ptr + adjustment) as *mut u8;
assert!((self.stack.ptr().offset_to(aligned_ptr).unwrap() as usize) < self.stack.cap());
self.current_offset.set(aligned_ptr);
core::ptr::write::<T>(old_stack_top as *mut T, value);
&mut *(old_stack_top as *mut T)
}
}
pub fn marker(&self) -> *mut u8 {
self.current_offset.get()
}
pub fn reset_to_marker(&self, marker: *mut u8) {
self.current_offset.set(marker);
}
}
#[cfg(test)]
mod stack_allocator_test {
use super::*;
extern crate time;
#[test]
fn creation_with_right_capacity() {
let alloc = StackAllocator::with_capacity(200);
let cap_used = alloc.stack.ptr().offset_to(alloc.current_offset.get()).unwrap() as usize;
let cap_remaining = (alloc.stack.cap() - cap_used) as isize;
assert_eq!(cap_used, 0);
assert_eq!(cap_remaining, 200);
}
#[test]
fn allocation_test() {
let alloc = StackAllocator::with_capacity(200);
let _test_1_byte = alloc.alloc::<u8>(2);
let cap_used = alloc.stack.ptr().offset_to(alloc.current_offset.get()).unwrap() as usize;
let cap_remaining = (alloc.stack.cap() - cap_used) as isize;
assert_eq!(cap_used, 3); assert_eq!(cap_remaining, 197);
let _test_4_bytes = alloc.alloc::<u32>(60000);
let cap_used = alloc.stack.ptr().offset_to(alloc.current_offset.get()).unwrap() as usize;
let cap_remaining = (alloc.stack.cap() - cap_used) as isize;
assert_eq!(cap_used, 12); assert_eq!(cap_remaining, 188);
let _test_8_bytes = alloc.alloc::<u64>(100000);
let cap_used = alloc.stack.ptr().offset_to(alloc.current_offset.get()).unwrap() as usize;
let cap_remaining = (alloc.stack.cap() - cap_used) as isize;
assert_eq!(cap_used, 32); assert_eq!(cap_remaining, 168); }
#[test]
fn test_reset() {
let alloc = StackAllocator::with_capacity(200);
let test_1_byte = alloc.alloc::<u8>(2);
assert_eq!(test_1_byte, &mut 2);
alloc.reset();
let test_1_byte = alloc.alloc::<u8>(5);
assert_eq!(test_1_byte, &mut 5);
}
struct Monster {
hp :u32,
level: u32,
}
impl Default for Monster {
fn default() -> Self {
Monster {
hp: 1,
level: 1,
}
}
}
}