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}