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}