Everwood: the HD/OctoWoW line of benilla, imported onto upstream

This checkout had no git history at all, so this one commit carries the whole
of the Everwood work as a single import on top of chairmanpow/benilla-octo's
`main` (706cd71). The two trees have genuinely diverged — each has files the
other does not — so this is a fork point, not a catch-up.

Today's work, which is the reason a baseline exists at all:

- Booth previews no longer cast world shadows (`NO_BOOTH_SHADOW`). Portrait
  characters were entering the sun's shadow cascades, so every skinned-rig
  shadow specialization compiled live mid-frame; the hitch detector logged
  "main thread blocked 1316 ms" beside a `compiled LIVE` warning. This was
  the Fashion/Tmog stutter.
- Per-pane dress-up: each `DressUpModel` pane assembles and stages its own
  tried-on look instead of every pane mirroring the live player.
- Item template cache is append-only JSONL written on the IO pool, replacing
  an 11.6 MB whole-map JSON rewrite on the main thread every 10 s.
- `WOW_SYSTEM_TIMING` rebuilt per-thread: it took 2 mutexes per system enter
  and 4 per exit, costing 15 ms a frame (590 fps -> 60) and corrupting the
  measurements it was taken to produce.
- The visibility-churn sweep moved to its own `WOW_FLASH_CHURN` switch, so a
  frame-cost reading no longer pays 2.7 ms a frame for it.

Local state (`benilla-config/`, `logs/`, `target/`) is excluded by the
existing .gitignore; the account credential it holds is not in this commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
This commit is contained in:
2026-10-15 15:01:14 -06:00
co-authored by Claude Opus 5
parent 706cd717a3
commit 81d96af35d
1019 changed files with 85223 additions and 130908 deletions
+552
View File
@@ -0,0 +1,552 @@
//! MLua compatibility layer for Lua 5.1/JIT
//!
//! Based on github.com/keplerproject/lua-compat-5.3
use std::os::raw::{c_char, c_int, c_void};
use std::{mem, ptr};
use super::lauxlib::*;
use super::lua::*;
#[inline(always)]
unsafe fn compat53_reverse(L: *mut lua_State, mut a: c_int, mut b: c_int) {
while a < b {
lua_pushvalue(L, a);
lua_pushvalue(L, b);
lua_replace(L, a);
lua_replace(L, b);
a += 1;
b -= 1;
}
}
const COMPAT53_LEVELS1: c_int = 10; // size of the first part of the stack
const COMPAT53_LEVELS2: c_int = 11; // size of the second part of the stack
unsafe fn compat53_countlevels(L: *mut lua_State) -> c_int {
let mut ar: lua_Debug = mem::zeroed();
let (mut li, mut le) = (1, 1);
// find an upper bound
while lua_getstack(L, le, &mut ar) != 0 {
li = le;
le *= 2;
}
// do a binary search
while li < le {
let m = (li + le) / 2;
if lua_getstack(L, m, &mut ar) != 0 {
li = m + 1
} else {
le = m;
}
}
le - 1
}
unsafe fn compat53_checkmode(
L: *mut lua_State,
mode: *const c_char,
modename: *const c_char,
err: c_int,
) -> c_int {
unsafe fn strchr(s: *const c_char, c: c_char) -> *const c_char {
let mut st = s;
while *st != 0 && *st != c {
st = st.offset(1);
}
if *st == c { st } else { ptr::null() }
}
if !mode.is_null() && strchr(mode, *modename).is_null() {
lua_pushfstring(
L,
cstr!("attempt to load a %s chunk (mode is '%s')"),
modename,
mode,
);
return err;
}
LUA_OK
}
// BENILLA DELTA · `compat53_findfield` and `compat53_pushglobalfuncname` deleted.
//
// Upstream's pair back-ports Lua 5.3's `luaL_traceback` naming: for *every* stack level of a
// traceback it searched the globals table, and then one level into every table in it, for a value
// identical to the frame's function, so it could name the frame `function 'Lib.name'`. The search
// is O(keys reachable two levels from `_G`) per level, per raise, and `_G` on a loaded 1.12
// interface is not small: 27,114 globals and 83,372 keys one level in, measured by
// `benilla-app`'s `addon_harness::tick_bench` on the shipped addon set. That cost 6.95 ms for one
// `Usage:` raise out of a Rust binding — mlua builds this traceback for every `Err` a binding
// returns (`mlua` `state/util.rs`, `callback_error_ext`) — against 0.08 ms for the same raise in a
// bare VM, and 2.8 us once this delta is in. A raise an addon wraps in `pcall` pays it in full and
// leaves no trace in any log: five a frame measured 44.7 ms of VM tick, error log empty.
//
// Nothing benilla imitates ever did that search. Lua 5.1's own `debug.traceback`
// (`third_party/lua-src/lua-5.1.5/ldblib.c`, `db_errorfb`) names a frame from `lua_getinfo`
// alone — `" in function " LUA_QS` off `ar.name` when `ar.namewhat` is non-empty — and so does
// the 1.12 client's `debugstack` (`0x703760`, reimplemented in `benilla-ui`'s
// `script::stdlib::debugstack`). benilla's own handler-dispatch traceback
// (`benilla_ui::script::event::PROTECTED_CALL`) already builds that shape, for this same reason.
//
// So `compat53_pushfuncname` below keeps 5.1's naming with mlua's quoting: the frames the search
// used to name are the frames `ar.namewhat` names anyway (a binding called as a global carries
// `namewhat = "global"`, `name = "UnitName"`), which is why the logged text does not move.
unsafe fn compat53_pushfuncname(L: *mut lua_State, _L1: *mut lua_State, ar: *mut lua_Debug) {
// BENILLA DELTA · the name comes from the call site, as Lua 5.1's own `db_errorfb` takes it
// (`" in function " LUA_QS`, `ar.name`), not from a search of `_G`. See the banner above.
if *(*ar).namewhat != b'\0' as c_char {
// use name from code
lua_pushfstring(L, cstr!("function '%s'"), (*ar).name);
} else if *(*ar).what == b'm' as c_char {
// main?
lua_pushliteral(L, c"main chunk");
} else if *(*ar).what != b'C' as c_char {
// for Lua functions, use <file:line>
let short_src = (*ar).short_src.as_ptr();
lua_pushfstring(L, cstr!("function <%s:%d>"), short_src, (*ar).linedefined);
} else {
lua_pushliteral(L, c"?");
}
}
//
// lua ported functions
//
#[inline(always)]
pub unsafe fn lua_absindex(L: *mut lua_State, mut idx: c_int) -> c_int {
if idx < 0 && idx > LUA_REGISTRYINDEX {
idx += lua_gettop(L) + 1;
}
idx
}
pub unsafe fn lua_rotate(L: *mut lua_State, mut idx: c_int, mut n: c_int) {
idx = lua_absindex(L, idx);
if n > 0 {
// Faster version
for _ in 0..n {
lua_insert(L, idx);
}
return;
}
let n_elems = lua_gettop(L) - idx + 1;
if n < 0 {
n += n_elems;
}
if n > 0 && n < n_elems {
luaL_checkstack(L, 2, cstr!("not enough stack slots available"));
n = n_elems - n;
compat53_reverse(L, idx, idx + n - 1);
compat53_reverse(L, idx + n, idx + n_elems - 1);
compat53_reverse(L, idx, idx + n_elems - 1);
}
}
#[inline(always)]
pub unsafe fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int) {
let abs_to = lua_absindex(L, toidx);
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
lua_pushvalue(L, fromidx);
lua_replace(L, abs_to);
}
#[inline(always)]
pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int {
if lua_type(L, idx) == LUA_TNUMBER {
let n = lua_tonumber(L, idx);
let i = lua_tointeger(L, idx);
// Lua 5.3+ returns "false" for `-0.0`
if n.to_bits() == (i as lua_Number).to_bits() {
return 1;
}
}
0
}
#[inline(always)]
pub unsafe fn lua_tointeger(L: *mut lua_State, i: c_int) -> lua_Integer {
lua_tointegerx(L, i, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn lua_tonumberx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> lua_Number {
let n = lua_tonumber(L, i);
if !isnum.is_null() {
*isnum = (n != 0.0 || lua_isnumber(L, i) != 0) as c_int;
}
n
}
#[inline(always)]
pub unsafe fn lua_tointegerx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> lua_Integer {
let mut ok = 0;
let n = lua_tonumberx(L, i, &mut ok);
let n_int = n as lua_Integer;
if ok != 0 && (n - n_int as lua_Number).abs() < lua_Number::EPSILON {
if !isnum.is_null() {
*isnum = 1;
}
return n_int;
}
if !isnum.is_null() {
*isnum = 0;
}
0
}
#[inline(always)]
pub unsafe fn lua_rawlen(L: *mut lua_State, idx: c_int) -> usize {
lua_objlen(L, idx)
}
#[inline(always)]
pub unsafe fn lua_pushlstring(L: *mut lua_State, s: *const c_char, l: usize) -> *const c_char {
if l == 0 {
lua_pushlstring_(L, cstr!(""), 0);
} else {
lua_pushlstring_(L, s, l);
}
lua_tostring(L, -1)
}
#[inline(always)]
pub unsafe fn lua_pushstring(L: *mut lua_State, s: *const c_char) -> *const c_char {
lua_pushstring_(L, s);
lua_tostring(L, -1)
}
#[inline(always)]
pub unsafe fn lua_getglobal(L: *mut lua_State, var: *const c_char) -> c_int {
lua_getglobal_(L, var);
lua_type(L, -1)
}
#[inline(always)]
pub unsafe fn lua_gettable(L: *mut lua_State, idx: c_int) -> c_int {
lua_gettable_(L, idx);
lua_type(L, -1)
}
#[inline(always)]
pub unsafe fn lua_getfield(L: *mut lua_State, idx: c_int, k: *const c_char) -> c_int {
lua_getfield_(L, idx, k);
lua_type(L, -1)
}
#[inline(always)]
pub unsafe fn lua_geti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) -> c_int {
idx = lua_absindex(L, idx);
lua_pushinteger(L, n);
lua_gettable(L, idx)
}
#[inline(always)]
pub unsafe fn lua_rawget(L: *mut lua_State, idx: c_int) -> c_int {
lua_rawget_(L, idx);
lua_type(L, -1)
}
#[inline(always)]
pub unsafe fn lua_rawgeti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int {
let n = n.try_into().expect("cannot convert index to lua_Integer");
lua_rawgeti_(L, idx, n);
lua_type(L, -1)
}
#[inline(always)]
pub unsafe fn lua_rawgetp(L: *mut lua_State, idx: c_int, p: *const c_void) -> c_int {
let abs_i = lua_absindex(L, idx);
lua_pushlightuserdata(L, p as *mut c_void);
lua_rawget(L, abs_i)
}
#[inline(always)]
pub unsafe fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int {
lua_getfenv(L, idx);
lua_type(L, -1)
}
#[inline(always)]
pub unsafe fn lua_seti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) {
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
idx = lua_absindex(L, idx);
lua_pushinteger(L, n);
lua_insert(L, -2);
lua_settable(L, idx);
}
#[inline(always)]
pub unsafe fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer) {
let n = n.try_into().expect("cannot convert index from lua_Integer");
lua_rawseti_(L, idx, n)
}
#[inline(always)]
pub unsafe fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void) {
let abs_i = lua_absindex(L, idx);
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
lua_pushlightuserdata(L, p as *mut c_void);
lua_insert(L, -2);
lua_rawset(L, abs_i);
}
#[inline(always)]
pub unsafe fn lua_setuservalue(L: *mut lua_State, idx: c_int) {
luaL_checktype(L, -1, LUA_TTABLE);
lua_setfenv(L, idx);
}
#[inline(always)]
pub unsafe fn lua_dump(L: *mut lua_State, writer: lua_Writer, data: *mut c_void, _strip: c_int) -> c_int {
lua_dump_(L, writer, data)
}
#[inline(always)]
pub unsafe fn lua_len(L: *mut lua_State, idx: c_int) {
match lua_type(L, idx) {
LUA_TSTRING => {
lua_pushnumber(L, lua_objlen(L, idx) as lua_Number);
}
LUA_TTABLE => {
if luaL_callmeta(L, idx, cstr!("__len")) == 0 {
lua_pushnumber(L, lua_objlen(L, idx) as lua_Number);
}
}
LUA_TUSERDATA if luaL_callmeta(L, idx, cstr!("__len")) != 0 => {}
_ => {
luaL_error(
L,
cstr!("attempt to get length of a %s value"),
lua_typename(L, lua_type(L, idx)),
);
}
}
}
#[inline(always)]
pub unsafe fn lua_pushglobaltable(L: *mut lua_State) {
lua_pushvalue(L, LUA_GLOBALSINDEX);
}
#[inline(always)]
pub unsafe fn lua_resume(L: *mut lua_State, _from: *mut lua_State, narg: c_int, nres: *mut c_int) -> c_int {
let ret = lua_resume_(L, narg);
if (ret == LUA_OK || ret == LUA_YIELD) && !(nres.is_null()) {
*nres = lua_gettop(L);
}
ret
}
//
// lauxlib ported functions
//
#[inline(always)]
pub unsafe fn luaL_checkstack(L: *mut lua_State, sz: c_int, msg: *const c_char) {
if lua_checkstack(L, sz + LUA_MINSTACK) == 0 {
if !msg.is_null() {
luaL_error(L, cstr!("stack overflow (%s)"), msg);
} else {
lua_pushliteral(L, c"stack overflow");
lua_error(L);
}
}
}
#[inline(always)]
pub unsafe fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int {
if luaL_getmetafield_(L, obj, e) != 0 {
lua_type(L, -1)
} else {
LUA_TNIL
}
}
#[inline(always)]
pub unsafe fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_int {
if luaL_newmetatable_(L, tname) != 0 {
lua_pushstring_(L, tname);
lua_setfield(L, -2, cstr!("__name"));
1
} else {
0
}
}
pub unsafe fn luaL_loadbufferenv(
L: *mut lua_State,
data: *const c_char,
size: usize,
name: *const c_char,
mode: *const c_char,
mut env: c_int,
) -> c_int {
if env != 0 {
env = lua_absindex(L, env);
}
let status = luaL_loadbufferx(L, data, size, name, mode);
if status == LUA_OK && env != 0 {
lua_pushvalue(L, env);
lua_setfenv(L, -2);
}
status
}
#[inline(always)]
pub unsafe fn luaL_loadbufferx(
L: *mut lua_State,
buff: *const c_char,
sz: usize,
name: *const c_char,
mode: *const c_char,
) -> c_int {
let status = if sz > 0 && *buff as u8 == LUA_SIGNATURE[0] {
compat53_checkmode(L, mode, cstr!("binary"), LUA_ERRSYNTAX)
} else {
compat53_checkmode(L, mode, cstr!("text"), LUA_ERRSYNTAX)
};
if status != LUA_OK {
return status;
}
luaL_loadbuffer(L, buff, sz, name)
}
#[inline(always)]
pub unsafe fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer {
let mut isnum = 0;
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
lua_len(L, idx);
let res = lua_tointegerx(L, -1, &mut isnum);
lua_pop(L, 1);
if isnum == 0 {
luaL_error(L, cstr!("object length is not an integer"));
}
res
}
pub unsafe fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, mut level: c_int) {
let mut ar: lua_Debug = mem::zeroed();
let top = lua_gettop(L);
let numlevels = compat53_countlevels(L1);
#[rustfmt::skip]
let mut limit = if numlevels - level > COMPAT53_LEVELS1 + COMPAT53_LEVELS2 { COMPAT53_LEVELS1 } else { -1 };
if !msg.is_null() {
lua_pushfstring(L, cstr!("%s\n"), msg);
}
lua_pushliteral(L, c"stack traceback:");
while lua_getstack(L1, level, &mut ar) != 0 {
if limit == 0 {
// too many levels?
let n = numlevels - level - COMPAT53_LEVELS2;
// add warning about skip ("n + 1" because we skip current level too)
lua_pushfstring(L, cstr!("\n\t...\t(skipping %d levels)"), n + 1); // add warning about skip
level += n; // and skip to last levels
} else {
lua_getinfo(L1, cstr!("Sln"), &mut ar);
if *ar.what != b't' as c_char {
if ar.currentline <= 0 {
lua_pushfstring(L, cstr!("\n\t%s: in "), ar.short_src.as_ptr());
} else {
lua_pushfstring(L, cstr!("\n\t%s:%d: in "), ar.short_src.as_ptr(), ar.currentline);
}
compat53_pushfuncname(L, L1, &mut ar);
lua_concat(L, lua_gettop(L) - top);
} else {
lua_pushstring(L, cstr!("\n\t(...tail calls...)"));
}
}
level += 1;
limit -= 1;
}
lua_concat(L, lua_gettop(L) - top);
}
pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize) -> *const c_char {
idx = lua_absindex(L, idx);
if luaL_callmeta(L, idx, cstr!("__tostring")) == 0 {
match lua_type(L, idx) {
LUA_TNIL => {
lua_pushliteral(L, c"nil");
}
LUA_TSTRING | LUA_TNUMBER => {
lua_pushvalue(L, idx);
}
LUA_TBOOLEAN => {
if lua_toboolean(L, idx) == 0 {
lua_pushliteral(L, c"false");
} else {
lua_pushliteral(L, c"true");
}
}
t => {
let tt = luaL_getmetafield(L, idx, cstr!("__name"));
let name = if tt == LUA_TSTRING {
lua_tostring(L, -1)
} else {
lua_typename(L, t)
};
lua_pushfstring(L, cstr!("%s: %p"), name, lua_topointer(L, idx));
if tt != LUA_TNIL {
lua_replace(L, -2); // remove '__name'
}
}
};
} else if lua_isstring(L, -1) == 0 {
luaL_error(L, cstr!("'__tostring' must return a string"));
}
lua_tolstring(L, -1, len)
}
#[inline(always)]
pub unsafe fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char) {
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
luaL_getmetatable(L, tname);
lua_setmetatable(L, -2);
}
pub unsafe fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_char) -> c_int {
let abs_i = lua_absindex(L, idx);
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
lua_pushstring_(L, fname);
if lua_gettable(L, abs_i) == LUA_TTABLE {
return 1;
}
lua_pop(L, 1);
lua_newtable(L);
lua_pushstring_(L, fname);
lua_pushvalue(L, -2);
lua_settable(L, abs_i);
0
}
pub unsafe fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int) {
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
if lua_getfield(L, -1, modname) == LUA_TNIL {
lua_pop(L, 1);
lua_pushcfunction(L, openf);
lua_pushstring(L, modname);
#[cfg(feature = "lua51")]
{
lua_call(L, 1, 1);
lua_pushvalue(L, -1);
lua_setfield(L, -3, modname);
}
#[cfg(feature = "luajit")]
{
lua_call(L, 1, 0);
lua_getfield(L, -1, modname);
}
}
if glb != 0 {
lua_pushvalue(L, -1);
lua_setglobal(L, modname);
} else {
lua_pushnil(L);
lua_setglobal(L, modname);
}
lua_replace(L, -2);
}
+197
View File
@@ -0,0 +1,197 @@
//! Contains definitions from `lauxlib.h`.
use std::os::raw::{c_char, c_int, c_void};
use std::ptr;
use super::lua::{self, lua_CFunction, lua_Integer, lua_Number, lua_State};
// Extra error code for 'luaL_load'
pub const LUA_ERRFILE: c_int = lua::LUA_ERRERR + 1;
// Key, in the registry, for table of loaded modules
pub const LUA_LOADED_TABLE: *const c_char = cstr!("_LOADED");
#[repr(C)]
pub struct luaL_Reg {
pub name: *const c_char,
pub func: lua_CFunction,
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
unsafe extern "C-unwind" {
pub fn luaL_register(L: *mut lua_State, libname: *const c_char, l: *const luaL_Reg);
#[link_name = "luaL_getmetafield"]
pub fn luaL_getmetafield_(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
pub fn luaL_callmeta(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
pub fn luaL_typerror(L: *mut lua_State, narg: c_int, tname: *const c_char) -> c_int;
pub fn luaL_argerror(L: *mut lua_State, narg: c_int, extramsg: *const c_char) -> c_int;
pub fn luaL_checklstring(L: *mut lua_State, narg: c_int, l: *mut usize) -> *const c_char;
pub fn luaL_optlstring(
L: *mut lua_State,
narg: c_int,
def: *const c_char,
l: *mut usize,
) -> *const c_char;
pub fn luaL_checknumber(L: *mut lua_State, narg: c_int) -> lua_Number;
pub fn luaL_optnumber(L: *mut lua_State, narg: c_int, def: lua_Number) -> lua_Number;
pub fn luaL_checkinteger(L: *mut lua_State, narg: c_int) -> lua_Integer;
pub fn luaL_optinteger(L: *mut lua_State, narg: c_int, def: lua_Integer) -> lua_Integer;
#[link_name = "luaL_checkstack"]
pub fn luaL_checkstack_(L: *mut lua_State, sz: c_int, msg: *const c_char);
pub fn luaL_checktype(L: *mut lua_State, narg: c_int, t: c_int);
pub fn luaL_checkany(L: *mut lua_State, narg: c_int);
#[link_name = "luaL_newmetatable"]
pub fn luaL_newmetatable_(L: *mut lua_State, tname: *const c_char) -> c_int;
pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
pub fn luaL_where(L: *mut lua_State, lvl: c_int);
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> c_int;
pub fn luaL_checkoption(
L: *mut lua_State,
narg: c_int,
def: *const c_char,
lst: *const *const c_char,
) -> c_int;
}
// Pre-defined references
pub const LUA_NOREF: c_int = -2;
pub const LUA_REFNIL: c_int = -1;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
unsafe extern "C-unwind" {
pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int;
pub fn luaL_unref(L: *mut lua_State, t: c_int, r#ref: c_int);
pub fn luaL_loadfile(L: *mut lua_State, filename: *const c_char) -> c_int;
pub fn luaL_loadbuffer(L: *mut lua_State, buff: *const c_char, sz: usize, name: *const c_char) -> c_int;
pub fn luaL_loadstring(L: *mut lua_State, s: *const c_char) -> c_int;
pub fn luaL_newstate() -> *mut lua_State;
pub fn luaL_gsub(
L: *mut lua_State,
s: *const c_char,
p: *const c_char,
r: *const c_char,
) -> *const c_char;
pub fn luaL_findtable(
L: *mut lua_State,
idx: c_int,
fname: *const c_char,
szhint: c_int,
) -> *const c_char;
}
//
// Some useful macros (implemented as Rust functions)
//
#[inline(always)]
pub unsafe fn luaL_argcheck(L: *mut lua_State, cond: c_int, narg: c_int, extramsg: *const c_char) {
if cond == 0 {
luaL_argerror(L, narg, extramsg);
}
}
#[inline(always)]
pub unsafe fn luaL_checkstring(L: *mut lua_State, n: c_int) -> *const c_char {
luaL_checklstring(L, n, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn luaL_optstring(L: *mut lua_State, n: c_int, d: *const c_char) -> *const c_char {
luaL_optlstring(L, n, d, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn luaL_typename(L: *mut lua_State, i: c_int) -> *const c_char {
lua::lua_typename(L, lua::lua_type(L, i))
}
pub unsafe fn luaL_dofile(L: *mut lua_State, filename: *const c_char) -> c_int {
let status = luaL_loadfile(L, filename);
if status == 0 {
lua::lua_pcall(L, 0, lua::LUA_MULTRET, 0)
} else {
status
}
}
#[inline(always)]
pub unsafe fn luaL_dostring(L: *mut lua_State, s: *const c_char) -> c_int {
let status = luaL_loadstring(L, s);
if status == 0 {
lua::lua_pcall(L, 0, lua::LUA_MULTRET, 0)
} else {
status
}
}
#[inline(always)]
pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) {
lua::lua_getfield_(L, lua::LUA_REGISTRYINDEX, n);
}
#[inline(always)]
pub unsafe fn luaL_opt<T>(
L: *mut lua_State,
f: unsafe extern "C-unwind" fn(*mut lua_State, c_int) -> T,
n: c_int,
d: T,
) -> T {
if lua::lua_isnoneornil(L, n) != 0 {
d
} else {
f(L, n)
}
}
//
// Generic Buffer Manipulation
//
#[cfg(target_arch = "wasm32")]
const BUFSIZ: usize = 1024; // WASI libc's BUFSIZ is 1024
#[cfg(not(target_arch = "wasm32"))]
const BUFSIZ: usize = libc::BUFSIZ as usize;
// The buffer size used by the lauxlib buffer system.
// The "16384" workaround is taken from the LuaJIT source code.
pub const LUAL_BUFFERSIZE: usize = if BUFSIZ > 16384 { 8192 } else { BUFSIZ };
#[repr(C)]
pub struct luaL_Buffer {
pub p: *mut c_char, // current position in buffer
pub lvl: c_int, // number of strings in the stack
pub L: *mut lua_State,
pub buffer: [c_char; LUAL_BUFFERSIZE],
}
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
unsafe extern "C-unwind" {
pub fn luaL_buffinit(L: *mut lua_State, B: *mut luaL_Buffer);
pub fn luaL_prepbuffer(B: *mut luaL_Buffer) -> *mut c_char;
pub fn luaL_addlstring(B: *mut luaL_Buffer, s: *const c_char, l: usize);
pub fn luaL_addstring(B: *mut luaL_Buffer, s: *const c_char);
pub fn luaL_addvalue(B: *mut luaL_Buffer);
pub fn luaL_pushresult(B: *mut luaL_Buffer);
}
#[inline(always)]
pub unsafe fn luaL_addchar(B: *mut luaL_Buffer, c: c_char) {
let buffer_end = (*B).buffer.as_mut_ptr().add(LUAL_BUFFERSIZE);
if (*B).p >= buffer_end {
luaL_prepbuffer(B);
}
*(*B).p = c;
(*B).p = (*B).p.add(1);
}
#[inline(always)]
pub unsafe fn luaL_addsize(B: *mut luaL_Buffer, n: usize) {
(*B).p = (*B).p.add(n);
}
+404
View File
@@ -0,0 +1,404 @@
//! Contains definitions from `lua.h`.
use std::ffi::CStr;
use std::marker::{PhantomData, PhantomPinned};
use std::os::raw::{c_char, c_double, c_int, c_void};
use std::ptr;
// Mark for precompiled code (`<esc>Lua`)
#[cfg(not(feature = "luajit"))]
pub const LUA_SIGNATURE: &[u8] = b"\x1bLua";
#[cfg(feature = "luajit")]
pub const LUA_SIGNATURE: &[u8] = b"\x1bLJ";
// Option for multiple returns in 'lua_pcall' and 'lua_call'
pub const LUA_MULTRET: c_int = -1;
//
// Pseudo-indices
//
pub const LUA_REGISTRYINDEX: c_int = -10000;
pub const LUA_ENVIRONINDEX: c_int = -10001;
pub const LUA_GLOBALSINDEX: c_int = -10002;
pub const fn lua_upvalueindex(i: c_int) -> c_int {
LUA_GLOBALSINDEX - i
}
//
// Thread status
//
pub const LUA_OK: c_int = 0;
pub const LUA_YIELD: c_int = 1;
pub const LUA_ERRRUN: c_int = 2;
pub const LUA_ERRSYNTAX: c_int = 3;
pub const LUA_ERRMEM: c_int = 4;
pub const LUA_ERRERR: c_int = 5;
/// A raw Lua state associated with a thread.
#[repr(C)]
pub struct lua_State {
_data: [u8; 0],
_marker: PhantomData<(*mut u8, PhantomPinned)>,
}
//
// Basic types
//
pub const LUA_TNONE: c_int = -1;
pub const LUA_TNIL: c_int = 0;
pub const LUA_TBOOLEAN: c_int = 1;
pub const LUA_TLIGHTUSERDATA: c_int = 2;
pub const LUA_TNUMBER: c_int = 3;
pub const LUA_TSTRING: c_int = 4;
pub const LUA_TTABLE: c_int = 5;
pub const LUA_TFUNCTION: c_int = 6;
pub const LUA_TUSERDATA: c_int = 7;
pub const LUA_TTHREAD: c_int = 8;
/// Type produced by LuaJIT FFI module
#[cfg(feature = "luajit")]
pub const LUA_TCDATA: c_int = 10;
/// Minimum Lua stack available to a C function
pub const LUA_MINSTACK: c_int = 20;
/// A Lua number, usually equivalent to `f64`
pub type lua_Number = c_double;
/// A Lua integer, usually equivalent to `i64`
#[cfg(target_pointer_width = "32")]
pub type lua_Integer = i32;
#[cfg(target_pointer_width = "64")]
pub type lua_Integer = i64;
/// Type for native C functions that can be passed to Lua.
pub type lua_CFunction = unsafe extern "C-unwind" fn(L: *mut lua_State) -> c_int;
// Type for functions that read/write blocks when loading/dumping Lua chunks
#[rustfmt::skip]
pub type lua_Reader =
unsafe extern "C-unwind" fn(L: *mut lua_State, ud: *mut c_void, sz: *mut usize) -> *const c_char;
#[rustfmt::skip]
pub type lua_Writer =
unsafe extern "C-unwind" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
/// Type for memory-allocation functions (no unwinding)
#[rustfmt::skip]
pub type lua_Alloc =
unsafe extern "C" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
unsafe extern "C-unwind" {
//
// State manipulation
//
pub fn lua_newstate(f: lua_Alloc, ud: *mut c_void) -> *mut lua_State;
pub fn lua_close(L: *mut lua_State);
pub fn lua_newthread(L: *mut lua_State) -> *mut lua_State;
pub fn lua_atpanic(L: *mut lua_State, panicf: lua_CFunction) -> lua_CFunction;
//
// Basic stack manipulation
//
pub fn lua_gettop(L: *mut lua_State) -> c_int;
pub fn lua_settop(L: *mut lua_State, idx: c_int);
pub fn lua_pushvalue(L: *mut lua_State, idx: c_int);
pub fn lua_remove(L: *mut lua_State, idx: c_int);
pub fn lua_insert(L: *mut lua_State, idx: c_int);
pub fn lua_replace(L: *mut lua_State, idx: c_int);
pub fn lua_checkstack(L: *mut lua_State, sz: c_int) -> c_int;
pub fn lua_xmove(from: *mut lua_State, to: *mut lua_State, n: c_int);
//
// Access functions (stack -> C)
//
pub fn lua_isnumber(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_isstring(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_iscfunction(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_isuserdata(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_type(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_typename(L: *mut lua_State, tp: c_int) -> *const c_char;
pub fn lua_equal(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
pub fn lua_rawequal(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
pub fn lua_lessthan(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
pub fn lua_tonumber(L: *mut lua_State, idx: c_int) -> lua_Number;
#[link_name = "lua_tointeger"]
pub fn lua_tointeger_(L: *mut lua_State, idx: c_int) -> lua_Integer;
pub fn lua_toboolean(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
pub fn lua_objlen(L: *mut lua_State, idx: c_int) -> usize;
pub fn lua_tocfunction(L: *mut lua_State, idx: c_int) -> Option<lua_CFunction>;
pub fn lua_touserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
pub fn lua_tothread(L: *mut lua_State, idx: c_int) -> *mut lua_State;
pub fn lua_topointer(L: *mut lua_State, idx: c_int) -> *const c_void;
//
// Push functions (C -> stack)
//
pub fn lua_pushnil(L: *mut lua_State);
pub fn lua_pushnumber(L: *mut lua_State, n: lua_Number);
pub fn lua_pushinteger(L: *mut lua_State, n: lua_Integer);
#[link_name = "lua_pushlstring"]
pub fn lua_pushlstring_(L: *mut lua_State, s: *const c_char, l: usize);
#[link_name = "lua_pushstring"]
pub fn lua_pushstring_(L: *mut lua_State, s: *const c_char);
// lua_pushvfstring
pub fn lua_pushfstring(L: *mut lua_State, fmt: *const c_char, ...) -> *const c_char;
pub fn lua_pushcclosure(L: *mut lua_State, f: lua_CFunction, n: c_int);
pub fn lua_pushboolean(L: *mut lua_State, b: c_int);
pub fn lua_pushlightuserdata(L: *mut lua_State, p: *mut c_void);
pub fn lua_pushthread(L: *mut lua_State) -> c_int;
//
// Get functions (Lua -> stack)
//
#[link_name = "lua_gettable"]
pub fn lua_gettable_(L: *mut lua_State, idx: c_int);
#[link_name = "lua_getfield"]
pub fn lua_getfield_(L: *mut lua_State, idx: c_int, k: *const c_char);
#[link_name = "lua_rawget"]
pub fn lua_rawget_(L: *mut lua_State, idx: c_int);
#[link_name = "lua_rawgeti"]
pub fn lua_rawgeti_(L: *mut lua_State, idx: c_int, n: c_int);
pub fn lua_createtable(L: *mut lua_State, narr: c_int, nrec: c_int);
pub fn lua_newuserdata(L: *mut lua_State, sz: usize) -> *mut c_void;
pub fn lua_getmetatable(L: *mut lua_State, objindex: c_int) -> c_int;
pub fn lua_getfenv(L: *mut lua_State, idx: c_int);
//
// Set functions (stack -> Lua)
//
pub fn lua_settable(L: *mut lua_State, idx: c_int);
pub fn lua_setfield(L: *mut lua_State, idx: c_int, k: *const c_char);
pub fn lua_rawset(L: *mut lua_State, idx: c_int);
#[link_name = "lua_rawseti"]
pub fn lua_rawseti_(L: *mut lua_State, idx: c_int, n: c_int);
pub fn lua_setmetatable(L: *mut lua_State, objindex: c_int) -> c_int;
pub fn lua_setfenv(L: *mut lua_State, idx: c_int) -> c_int;
//
// 'load' and 'call' functions (load and run Lua code)
//
pub fn lua_call(L: *mut lua_State, nargs: c_int, nresults: c_int);
pub fn lua_pcall(L: *mut lua_State, nargs: c_int, nresults: c_int, errfunc: c_int) -> c_int;
pub fn lua_cpcall(L: *mut lua_State, f: lua_CFunction, ud: *mut c_void) -> c_int;
pub fn lua_load(
L: *mut lua_State,
reader: lua_Reader,
data: *mut c_void,
chunkname: *const c_char,
) -> c_int;
#[link_name = "lua_dump"]
pub fn lua_dump_(L: *mut lua_State, writer: lua_Writer, data: *mut c_void) -> c_int;
//
// Coroutine functions
//
pub fn lua_yield(L: *mut lua_State, nresults: c_int) -> c_int;
#[link_name = "lua_resume"]
pub fn lua_resume_(L: *mut lua_State, narg: c_int) -> c_int;
pub fn lua_status(L: *mut lua_State) -> c_int;
}
//
// Garbage-collection function and options
//
pub const LUA_GCSTOP: c_int = 0;
pub const LUA_GCRESTART: c_int = 1;
pub const LUA_GCCOLLECT: c_int = 2;
pub const LUA_GCCOUNT: c_int = 3;
pub const LUA_GCCOUNTB: c_int = 4;
pub const LUA_GCSTEP: c_int = 5;
pub const LUA_GCSETPAUSE: c_int = 6;
pub const LUA_GCSETSTEPMUL: c_int = 7;
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
unsafe extern "C-unwind" {
pub fn lua_gc(L: *mut lua_State, what: c_int, data: c_int) -> c_int;
}
//
// Miscellaneous functions
//
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
unsafe extern "C-unwind" {
#[link_name = "lua_error"]
fn lua_error_(L: *mut lua_State) -> c_int;
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_concat(L: *mut lua_State, n: c_int);
pub fn lua_getallocf(L: *mut lua_State, ud: *mut *mut c_void) -> lua_Alloc;
pub fn lua_setallocf(L: *mut lua_State, f: lua_Alloc, ud: *mut c_void);
}
// lua_error does not return but is declared to return int, and Rust translates
// ! to void which can cause link-time errors if the platform linker is aware
// of return types and requires they match (for example: wasm does this).
#[inline(always)]
pub unsafe fn lua_error(L: *mut lua_State) -> ! {
lua_error_(L);
unreachable!();
}
//
// Some useful macros (implemented as Rust functions)
//
#[inline(always)]
pub unsafe fn lua_pop(L: *mut lua_State, n: c_int) {
lua_settop(L, -n - 1)
}
#[inline(always)]
pub unsafe fn lua_newtable(L: *mut lua_State) {
lua_createtable(L, 0, 0)
}
#[inline(always)]
pub unsafe fn lua_register(L: *mut lua_State, n: *const c_char, f: lua_CFunction) {
lua_pushcfunction(L, f);
lua_setglobal(L, n)
}
#[inline(always)]
pub unsafe fn lua_pushcfunction(L: *mut lua_State, f: lua_CFunction) {
lua_pushcclosure(L, f, 0)
}
#[inline(always)]
pub unsafe fn lua_strlen(L: *mut lua_State, i: c_int) -> usize {
lua_objlen(L, i)
}
#[inline(always)]
pub unsafe fn lua_isfunction(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TFUNCTION) as c_int
}
#[inline(always)]
pub unsafe fn lua_istable(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TTABLE) as c_int
}
#[inline(always)]
pub unsafe fn lua_islightuserdata(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TLIGHTUSERDATA) as c_int
}
#[inline(always)]
pub unsafe fn lua_isnil(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TNIL) as c_int
}
#[inline(always)]
pub unsafe fn lua_isboolean(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TBOOLEAN) as c_int
}
#[inline(always)]
pub unsafe fn lua_isthread(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TTHREAD) as c_int
}
#[inline(always)]
pub unsafe fn lua_isnone(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) == LUA_TNONE) as c_int
}
#[inline(always)]
pub unsafe fn lua_isnoneornil(L: *mut lua_State, n: c_int) -> c_int {
(lua_type(L, n) <= LUA_TNIL) as c_int
}
#[inline(always)]
pub unsafe fn lua_pushliteral(L: *mut lua_State, s: &'static CStr) {
lua_pushstring_(L, s.as_ptr());
}
#[inline(always)]
pub unsafe fn lua_setglobal(L: *mut lua_State, var: *const c_char) {
lua_setfield(L, LUA_GLOBALSINDEX, var)
}
#[inline(always)]
pub unsafe fn lua_getglobal_(L: *mut lua_State, var: *const c_char) {
lua_getfield_(L, LUA_GLOBALSINDEX, var)
}
#[inline(always)]
pub unsafe fn lua_tolightuserdata(L: *mut lua_State, idx: c_int) -> *mut c_void {
if lua_islightuserdata(L, idx) != 0 {
return lua_touserdata(L, idx);
}
ptr::null_mut()
}
#[inline(always)]
pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
lua_tolstring(L, i, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn lua_xpush(from: *mut lua_State, to: *mut lua_State, idx: c_int) {
lua_pushvalue(from, idx);
lua_xmove(from, to, 1);
}
//
// Debug API
//
// Maximum size for the description of the source of a function in debug information.
const LUA_IDSIZE: usize = 60;
// Event codes
pub const LUA_HOOKCALL: c_int = 0;
pub const LUA_HOOKRET: c_int = 1;
pub const LUA_HOOKLINE: c_int = 2;
pub const LUA_HOOKCOUNT: c_int = 3;
pub const LUA_HOOKTAILCALL: c_int = 4;
// Event masks
pub const LUA_MASKCALL: c_int = 1 << (LUA_HOOKCALL as usize);
pub const LUA_MASKRET: c_int = 1 << (LUA_HOOKRET as usize);
pub const LUA_MASKLINE: c_int = 1 << (LUA_HOOKLINE as usize);
pub const LUA_MASKCOUNT: c_int = 1 << (LUA_HOOKCOUNT as usize);
/// Type for functions to be called on debug events.
pub type lua_Hook = unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug);
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
unsafe extern "C-unwind" {
pub fn lua_getstack(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int;
pub fn lua_getinfo(L: *mut lua_State, what: *const c_char, ar: *mut lua_Debug) -> c_int;
pub fn lua_getlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char;
pub fn lua_setlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char;
pub fn lua_getupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
pub fn lua_setupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
pub fn lua_sethook(L: *mut lua_State, func: Option<lua_Hook>, mask: c_int, count: c_int) -> c_int;
pub fn lua_gethook(L: *mut lua_State) -> Option<lua_Hook>;
pub fn lua_gethookmask(L: *mut lua_State) -> c_int;
pub fn lua_gethookcount(L: *mut lua_State) -> c_int;
}
#[repr(C)]
pub struct lua_Debug {
pub event: c_int,
pub name: *const c_char,
pub namewhat: *const c_char,
pub what: *const c_char,
pub source: *const c_char,
pub currentline: c_int,
pub nups: c_int,
pub linedefined: c_int,
pub lastlinedefined: c_int,
pub short_src: [c_char; LUA_IDSIZE],
// lua.h mentions this is for private use
i_ci: c_int,
}
+43
View File
@@ -0,0 +1,43 @@
//! Contains definitions from `lualib.h`.
use std::os::raw::{c_char, c_int};
use super::lua::lua_State;
pub const LUA_COLIBNAME: *const c_char = cstr!("coroutine");
pub const LUA_TABLIBNAME: *const c_char = cstr!("table");
pub const LUA_IOLIBNAME: *const c_char = cstr!("io");
pub const LUA_OSLIBNAME: *const c_char = cstr!("os");
pub const LUA_STRLIBNAME: *const c_char = cstr!("string");
pub const LUA_MATHLIBNAME: *const c_char = cstr!("math");
pub const LUA_DBLIBNAME: *const c_char = cstr!("debug");
pub const LUA_LOADLIBNAME: *const c_char = cstr!("package");
#[cfg(feature = "luajit")]
pub const LUA_BITLIBNAME: *const c_char = cstr!("bit");
#[cfg(feature = "luajit")]
pub const LUA_JITLIBNAME: *const c_char = cstr!("jit");
#[cfg(feature = "luajit")]
pub const LUA_FFILIBNAME: *const c_char = cstr!("ffi");
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
unsafe extern "C-unwind" {
pub fn luaopen_base(L: *mut lua_State) -> c_int;
pub fn luaopen_table(L: *mut lua_State) -> c_int;
pub fn luaopen_io(L: *mut lua_State) -> c_int;
pub fn luaopen_os(L: *mut lua_State) -> c_int;
pub fn luaopen_string(L: *mut lua_State) -> c_int;
pub fn luaopen_math(L: *mut lua_State) -> c_int;
pub fn luaopen_debug(L: *mut lua_State) -> c_int;
pub fn luaopen_package(L: *mut lua_State) -> c_int;
#[cfg(feature = "luajit")]
pub fn luaopen_bit(L: *mut lua_State) -> c_int;
#[cfg(feature = "luajit")]
pub fn luaopen_jit(L: *mut lua_State) -> c_int;
#[cfg(feature = "luajit")]
pub fn luaopen_ffi(L: *mut lua_State) -> c_int;
// open all builtin libraries
pub fn luaL_openlibs(L: *mut lua_State);
}
+11
View File
@@ -0,0 +1,11 @@
//! Low level bindings to Lua 5.1.
pub use compat::*;
pub use lauxlib::*;
pub use lua::*;
pub use lualib::*;
pub mod compat;
pub mod lauxlib;
pub mod lua;
pub mod lualib;