Skip to main content

low_luajit/lua/
stack.rs

1use core::{ffi::CStr, hint::assert_unchecked};
2use crate::{LuaState, ffi};
3
4/// Stack (& indices) related functions.
5impl LuaState {
6	/// Pops `amount` elements from the stack.
7	///
8	/// If `amount` is `0`, nothing happens.
9	///
10	/// Negative values of `amount` are allowed, behaving the same way as passing `abs(amount) - 1` to [`LuaState::set_top`].
11	///
12	/// # Panics
13	///
14	/// Panics if the stack currently contains less than `amount` values.
15	#[inline]
16	pub fn pop(&mut self, amount: i32) {
17		assert!(amount <= self.get_top(), "tried to pop {amount} values but the stack currently holds {} values", self.get_top());
18
19		// SAFETY: The stack contains enough values to pop.
20		unsafe { self.pop_unchecked(amount); }
21	}
22
23	/// Behaves exactly as [`LuaState::pop`], but without checking if the stack contains enough values to pop.
24	///
25	/// # Safety
26	///
27	/// The stack must currently contain at least `amount` values.
28	#[inline]
29	pub unsafe fn pop_unchecked(&mut self, amount: i32) {
30		// SAFETY: self.as_ptr() is always valid and the caller must ensure to specify a valid amount.
31		unsafe { ffi::lua_pop(self.as_ptr(), amount); }
32	}
33
34	/// Grow the stack to be large enough to hold `extra` more elements. Does nothing if the stack is already large enough.
35	///
36	/// If the stack failed to grow to the desired size, this function will error using `extra_msg` as the error message.
37	///
38	/// This function never shrinks the stack.
39	///
40	/// # Lua Errors
41	///
42	/// Intentionally errors if the stack fails to grow.
43	#[inline]
44	pub fn check_stack(&mut self, extra: i32, extra_msg: &CStr) {
45		// SAFETY: self.as_ptr() and extra_msg are always valid.
46		unsafe { ffi::luaL_checkstack(self.as_ptr(), extra, extra_msg.as_ptr()); }
47	}
48
49	/// Attempts to grow the stack to be large enough to hold `extra` more elements. Does nothing if the stack is already large enough.
50	///
51	/// Returns `false` if it failed to grow the stack to the desired size, `true` otherwise.
52	///
53	/// This function never shrinks the stack.
54	#[inline]
55	pub fn try_check_stack(&mut self, extra: i32) -> bool {
56		// SAFETY: self.as_ptr() is always valid.
57		unsafe { ffi::lua_checkstack(self.as_ptr(), extra) != 0 }
58	}
59
60	/// Returns the index of the top element (last inserted) in the stack.
61	///
62	/// This also equals the amount of elements in the stack.
63	#[inline]
64	#[must_use]
65	pub fn get_top(&self) -> i32 {
66		// SAFETY: self.as_ptr() is always valid and there should never be a negative amount of elements.
67		unsafe {
68			let top = ffi::lua_gettop(self.as_ptr());
69			assert_unchecked(top >= 0);
70			top
71		}
72	}
73
74	/// Sets the stack top to be `index`.
75	///
76	/// If the new top is higher than the previous, the new elements are filled with `nil`.
77	///
78	/// An `index` of `0` removes all stack elements.
79	///
80	/// # Panics
81	///
82	/// Panics if `index` is a negative number and `abs(index) > top`.
83	///
84	/// # Lua Errors
85	///
86	/// Really high `index` values may cause a stack overflow.
87	#[inline]
88	pub fn set_top(&mut self, index: i32) {
89		assert!(index.is_positive() || self.is_index_ok(index), "cannot set the top to be {index}, as it is negative and out of bounds");
90
91		// SAFETY: self.as_ptr() is always valid and index is not out of bounds.
92		unsafe { ffi::lua_settop(self.as_ptr(), index); }
93	}
94
95	/// Pseudo-index to the registry: a pre-defined table that C/Rust can use to store whatever it needs.
96	///
97	/// Other loaded C/Rust modules may use this table too, thus you should make your keys unique to avoid collisions
98	/// (by using the module name itself for example).
99	///
100	/// The integer keys of the registry are used internally by Lua, and should not be touched.
101	pub const REGISTRY_INDEX: i32 = ffi::LUA_REGISTRYINDEX;
102
103	/// Pseudo-index to the environment of the running [`CFunction`].
104	///
105	/// [`CFunction`]: ./type.CFunction.html
106	pub const ENVIRONMENT_INDEX: i32 = ffi::LUA_ENVIRONINDEX;
107
108	/// Pseudo-index to the thread environment: the table containing all the global variables (`_G`).
109	pub const GLOBALS_INDEX: i32 = ffi::LUA_GLOBALSINDEX;
110
111	/// Returns the pseudo-index to the upvalue #`n` (i.e. `upvalue_index(1)` returns the pseudo-index to the first upvalue).
112	///
113	/// This function does *not* check if the upvalue exists, it just returns its hypothethical pseudo-index.
114	///
115	/// If `n` is `0`, this function will instead return [`LuaState::GLOBALS_INDEX`].
116	#[inline]
117	#[must_use]
118	pub const fn upvalue_index(n: u8) -> i32 {
119		ffi::lua_upvalueindex(n as i32)
120	}
121
122	/// Returns a boolean stating if `index` is a valid index.
123	///
124	/// `index` is valid if `1 ≤ abs(index) ≤ top`.
125	///
126	/// This function only checks if `index` is *valid*, not *acceptable*!
127	#[inline]
128	#[must_use]
129	pub fn is_index_valid(&self, index: i32) -> bool {
130		let abs_index = index.abs();
131		abs_index >= 1 && abs_index <= self.get_top()
132	}
133
134	/// Returns a boolean stating if `index` is an "ok" index.
135	///
136	/// `index` is "ok" if `abs(index) ≤ top`.
137	///
138	/// This function is used internally for assertions.
139	#[inline]
140	#[must_use]
141	pub fn is_index_ok(&self, index: i32) -> bool {
142		index.unsigned_abs() <= self.get_top().cast_unsigned()
143	}
144
145	/// Converts the value at the given acceptable `index` to a generic pointer and returns it.
146	///
147	/// Only userdata, tables, and functions are accepted. Anything else will return a null pointer.
148	///
149	/// There is no intended way to convert the pointer back to its original value.
150	#[inline]
151	#[must_use]
152	pub fn to_pointer(&self, index: i32) -> *const () {
153		// SAFETY: self.as_ptr() is always valid.
154		unsafe { ffi::lua_topointer(self.as_ptr(), index).cast() }
155	}
156
157	/// Pushes a copy of the value at the given valid `index` onto the stack.
158	///
159	/// If `index` is out of bounds, `nil` is pushed onto the stack.
160	#[inline]
161	pub fn push_value(&mut self, index: i32) {
162		// SAFETY: self.as_ptr() is always valid.
163		unsafe { ffi::lua_pushvalue(self.as_ptr(), index); }
164	}
165
166	/// Pops the top element and inserts it at the given valid `index`, shifting up the other elements to make space.
167	///
168	/// This function will do nothing if `index` is 0.
169	///
170	/// To replace the element located at `index` instead of shifting them up, see [`LuaState::replace`].
171	///
172	/// # Panics
173	///
174	/// Panics if `index` is out of bounds.
175	/// (Pseudo-indices are out of bounds as they are not real indices.)
176	#[inline]
177	pub fn insert(&mut self, index: i32) {
178		assert!(self.is_index_ok(index), "cannot insert the top element at index {index}, as there are only {} elements", self.get_top());
179
180		// SAFETY: self.as_ptr() is always valid and index is not out of bounds.
181		unsafe { ffi::lua_insert(self.as_ptr(), index); }
182	}
183
184	/// Pops the top element and inserts it at the given valid `index`, replacing the element that was previously located at `index`.
185	///
186	/// If `index` is zero, the top element is effectively removed.
187	///
188	/// To shift up the other elements instead of replacing it, see [`LuaState::insert`].
189	///
190	/// # Panics
191	///
192	/// Panics if `index` is out of bounds.
193	/// (Pseudo-indices are out of bounds as they are not real indices.)
194	#[inline]
195	pub fn replace(&mut self, index: i32) {
196		assert!(self.is_index_ok(index), "cannot replace the element at index {index} with the top element, as there are only {} elements", self.get_top());
197
198		// SAFETY: self.as_ptr() is always valid and index is not out of bounds.
199		unsafe { ffi::lua_replace(self.as_ptr(), index); }
200	}
201
202	/// Removes the element at the given valid `index`, shifting down the other elements to fill the gap.
203	///
204	/// # Panics
205	///
206	/// Panics if `index` is out of bounds or `0`.
207	/// (Pseudo-indices are out of bounds as they are not real indices.)
208	#[inline]
209	pub fn remove(&mut self, index: i32) {
210		assert!(self.is_index_valid(index), "cannot delete an element at invalid index {index}");
211
212		// SAFETY: self.as_ptr() is always valid and index is not out of bounds.
213		unsafe { ffi::lua_remove(self.as_ptr(), index); }
214	}
215
216	/// Copies the element at index `from_index` into the valid index `to_index`, replacing the element that was previously located at `to_index`.
217	///
218	/// If `from_index` is out of bounds, the element located at `to_index` will be replaced with `nil`.
219	///
220	/// # Panics
221	///
222	/// Panics if `to_index` is out of bounds.
223	#[inline]
224	pub fn copy(&mut self, from_index: i32, to_index: i32) {
225		assert!(
226			self.is_index_ok(to_index),
227			"cannot replace the element at index {to_index} with the element at index {from_index}, as there are only {} elements",
228			self.get_top()
229		);
230
231		unsafe { ffi::lua_copy(self.as_ptr(), from_index, to_index); }
232	}
233
234	/// Checks if there's an element of any type (including `nil`) at the given index `arg_n`.
235	///
236	/// If the check fails, `LuaState::arg_error` is called using `arg_n`.
237	///
238	/// # Lua Errors
239	///
240	/// Intentionally errors if the check fails.
241	#[inline]
242	pub fn check_any(&self, arg_n: i32) {
243		// SAFETY: self.as_ptr() is always valid.
244		unsafe { ffi::luaL_checkany(self.as_ptr(), arg_n); }
245	}
246
247	/// Returns `default` when the element at the given index `arg_n` is `nil` (or non-existing).
248	///
249	/// Otherwise, `func` is called with `self` and `arg_n` as its arguments, returning the value `func` returns.
250	#[inline]
251	pub fn opt<T>(&self, func: impl FnOnce(&LuaState, i32) -> T, arg_n: i32, default: T) -> T {
252		if self.is_none_or_nil(arg_n) {
253			default
254		} else {
255			func(self, arg_n)
256		}
257	}
258}