luau 0.732.0

Safe lifetime-bound Rust embedding API for the Luau runtime
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(())
}