use super::{i_lua_allocator::ILuaAllocator, lua_managed_allocator::LuaManagedAllocator};
pub struct LuaTrackedAllocator {
inner: LuaManagedAllocator,
limit_bytes: usize,
used_bytes: usize,
}
impl LuaTrackedAllocator {
pub fn new(limit_bytes: usize) -> Self {
Self {
inner: LuaManagedAllocator::default(),
limit_bytes,
used_bytes: 0,
}
}
pub fn used_bytes(&self) -> usize {
self.used_bytes
}
pub fn infallible_allocate(&mut self, size: usize, align: usize) -> Option<*mut u8> {
self.inner.enter_infallible_allocation_region();
let result = self.inner.allocate_new(size, align);
let _ = self.inner.try_exit_infallible_allocation_region();
if result.is_some() {
self.used_bytes += size.max(1);
}
result
}
pub fn is_infallible_allocation(&self) -> bool {
self.limit_bytes == 0
}
}
impl ILuaAllocator for LuaTrackedAllocator {
fn enter_infallible_allocation_region(&mut self) {
self.inner.enter_infallible_allocation_region();
}
fn try_exit_infallible_allocation_region(&mut self) -> bool {
self.inner.try_exit_infallible_allocation_region()
}
fn allocate_new(&mut self, size: usize, align: usize) -> Option<*mut u8> {
if self.limit_bytes != 0 && self.used_bytes + size.max(1) > self.limit_bytes {
return None;
}
let result = self.inner.allocate_new(size, align);
if result.is_some() {
self.used_bytes += size.max(1);
}
result
}
unsafe fn resize_allocation(&mut self, ptr: *mut u8, new_size: usize) -> Option<*mut u8> {
if self.limit_bytes != 0 && new_size > self.limit_bytes {
return None;
}
unsafe { self.inner.resize_allocation(ptr, new_size) }
}
}
#[cfg(test)]
mod tests {
use super::{ILuaAllocator, LuaTrackedAllocator};
#[test]
fn quota_blocks_over_limit() {
let mut alloc = LuaTrackedAllocator::new(64);
assert!(alloc.allocate_new(32, 1).is_some());
assert!(alloc.allocate_new(64, 1).is_none());
assert!(alloc.allocate_new(32, 1).is_some());
assert_eq!(alloc.used_bytes(), 64);
}
#[test]
fn infallible_channel_ignores_quota() {
let mut alloc = LuaTrackedAllocator::new(16);
assert!(alloc.infallible_allocate(8, 1).is_some());
assert!(!alloc.is_infallible_allocation());
assert!(LuaTrackedAllocator::new(0).is_infallible_allocation());
}
#[test]
fn unlimited_when_no_limit() {
let mut alloc = LuaTrackedAllocator::new(0);
assert!(alloc.allocate_new(4096, 1).is_some());
}
}