From 173149f2f7389a3a4e4e94d4429a101bcee2d238 Mon Sep 17 00:00:00 2001 From: luaneko Date: Mon, 23 Jun 2025 11:40:20 +1000 Subject: [PATCH] Add stack pack and unpack methods --- crates/luajit/src/lib.rs | 85 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 82 insertions(+), 3 deletions(-) diff --git a/crates/luajit/src/lib.rs b/crates/luajit/src/lib.rs index 2b30597..c4d4cec 100644 --- a/crates/luajit/src/lib.rs +++ b/crates/luajit/src/lib.rs @@ -851,6 +851,85 @@ impl Stack { unsafe { lua_rawseti(self.as_ptr(), table.index(), n) } } + /// Packs the array-part of the table at index `idx` from the stack. + /// + /// This pops `n` values at the top of the stack, sets them as the fields of the table at index + /// `idx` at indices `1` to `n` inclusive, then sets the field `"n"` to `n`. Any existing values + /// in the table are overwritten. If the table already has more than `n` values in its + /// array-part, these values are **not** cleared. The number of values popped is returned, which + /// is always equal to `n`. + /// + /// This method does not invoke any metamethods. + /// + /// Equivalent to `table.pack(...)`. + /// + /// # Panic + /// + /// Panics if `n` is negative, there are not enough values on the stack, or the value at index + /// `idx` is not a table. + pub fn pack(&mut self, idx: c_int, n: c_int) -> c_int { + assert!(n >= 0, "n must be nonnegative"); + let size = self.size(); + let table = self.slot(idx); + assert!( + table.type_of() == Type::Table, + "expected table at index {idx}: {self:?}" + ); + assert!(n <= size, "expected {n} values: {self:?}"); + assert!(idx <= size - n, "cannot pack a table into itself: {self:?}"); + unsafe { + (0..n).for_each(|i| lua_rawseti(self.as_ptr(), table.index(), n - i)); + self.ensure(2); + lua_pushliteral(self.as_ptr(), "n"); + lua_pushinteger(self.as_ptr(), n as lua_Integer); + lua_rawset(self.as_ptr(), table.index()); + n + } + } + + /// Unpacks the array-part of the table at index `idx` onto the stack. + /// + /// If `j` is [`None`], then it is set to be the value of the field `"n"` interpreted as an + /// integer. If this field does not exist, then it is set to the be length of the table as + /// defined by the Lua `#` length operator. All values in indices `i` to `j` inclusive are + /// pushed at the top of the stack in ascending order. If `i > j`, then nothing is pushed. + /// Otherwise, `j - i + 1` values are pushed, and the number of values pushed is returned. + /// + /// This method does not invoke any metamethods. + /// + /// Equivalent to `table.unpack(list, i, j)`. + /// + /// # Panic + /// + /// Panics if the value at index `idx` is not a table. + pub fn unpack(&mut self, idx: c_int, i: c_int, j: Option) -> c_int { + let table = self.slot(idx); + assert!( + table.type_of() == Type::Table, + "expected table at index {idx}: {self:?}" + ); + let j = match j { + Some(j) => j, + None => unsafe { + self.ensure(1); + lua_pushliteral(self.as_ptr(), "n"); + lua_rawget(self.as_ptr(), table.index()); + let mut isnum = 0; + let n = lua_tointegerx(self.as_ptr(), -1, &raw mut isnum); + lua_pop(self.as_ptr(), 1); + (isnum != 0) + .then_some(n as c_int) + .unwrap_or_else(|| lua_objlen(self.as_ptr(), table.index()) as c_int) + }, + }; + let n = (j - i + 1).max(0); + if n > 0 { + self.ensure(n); + (0..n).for_each(|n| unsafe { lua_rawgeti(self.as_ptr(), table.index(), i + n) }); + } + n + } + /// Pushes the given chunk as a function at the top of the stack. /// /// Equivalent to [`lua_loadx`]. @@ -1144,7 +1223,7 @@ impl Stack { /// /// Equivalent to [`luaL_traceback`]. pub fn backtrace(&self, level: c_int) -> Option { - assert!(level >= 0, "backtrace level must be nonnegative"); + assert!(level >= 0, "level must be nonnegative"); self.ensure(LUA_MINSTACK); unsafe { luaL_traceback(self.as_ptr(), self.as_ptr(), ptr::null(), 0); @@ -1590,8 +1669,8 @@ impl NewTable { impl Push for NewTable { fn push(&self, stack: &mut Stack) { let Self { narr, nrec } = *self; - assert!(0 <= narr, "size of table array part must be nonnegative"); - assert!(0 <= nrec, "size of table hash part must be nonnegative"); + assert!(0 <= narr, "narr must be nonnegative"); + assert!(0 <= nrec, "nrec must be nonnegative"); stack.ensure(1); unsafe { lua_createtable(stack.as_ptr(), narr, nrec) } }