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