use std::alloc::Layout;
use std::ptr::NonNull;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use luau::Lua;
use luau::allocator::{LuaAllocator, SystemLuaAllocator};
#[derive(Default)]
struct AllocationStats {
allocations: AtomicUsize,
reallocations: AtomicUsize,
deallocations: AtomicUsize,
}
struct CountingAllocator {
stats: Arc<AllocationStats>,
}
unsafe impl LuaAllocator for CountingAllocator {
unsafe fn allocate(&self, layout: Layout) -> Option<NonNull<u8>> {
let result = unsafe { SystemLuaAllocator.allocate(layout) };
if result.is_some() {
self.stats.allocations.fetch_add(1, Ordering::Relaxed);
}
result
}
unsafe fn reallocate(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Option<NonNull<u8>> {
let result = unsafe { SystemLuaAllocator.reallocate(ptr, old_layout, new_layout) };
if result.is_some() {
self.stats.reallocations.fetch_add(1, Ordering::Relaxed);
}
result
}
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
self.stats.deallocations.fetch_add(1, Ordering::Relaxed);
unsafe { SystemLuaAllocator.deallocate(ptr, layout) };
}
}
fn main() -> luau::Result<()> {
let stats = Arc::new(AllocationStats::default());
let allocator = CountingAllocator {
stats: Arc::clone(&stats),
};
let lua = Lua::new_with_allocator(allocator)?;
let len: i32 = lua
.load(
r#"
local values = {}
for index = 1, 100 do
values[index] = index
end
return #values
"#,
)
.set_name("custom_allocator")
.call(())?;
println!(
"len={len} allocations={} reallocations={} deallocations={}",
stats.allocations.load(Ordering::Relaxed),
stats.reallocations.load(Ordering::Relaxed),
stats.deallocations.load(Ordering::Relaxed)
);
Ok(())
}