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}