Skip to main content

low_luajit/lua/
stack.rs

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