1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
use core::{ffi::CStr, fmt::{self, Display, Write}, hint::assert_unchecked};
use crate::{LuaState, LuaType, ffi};
/// Stack (& indices) related functions.
impl LuaState {
/// Pops `amount` elements from the stack.
///
/// If `amount` is `0`, nothing happens.
///
/// Negative values of `amount` are allowed, behaving the same way as passing `abs(amount) - 1` to [`LuaState::set_top`].
///
/// # Panics
///
/// Panics if the stack contains less than `amount` values currently.
#[inline]
pub fn pop(&mut self, amount: i32) {
assert!(amount <= self.get_top(), "tried to pop {amount} values but the stack currently holds {} values", self.get_top());
// SAFETY: self.0 is always valid and the stack contains enough values to pop.
unsafe { ffi::lua_pop(self.0, amount); }
}
/// Grow the stack to be large enough to hold `extra` more elements. Does nothing if the stack is already large enough.
///
/// If the stack failed to grow to the desired size, this function will error using `extra_msg` as the error message.
///
/// This function never shrinks the stack.
///
/// # Lua Errors
///
/// Intentionally errors if the stack fails to grow.
pub fn check_stack(&mut self, extra: i32, extra_msg: &CStr) {
// SAFETY: self.0 and extra_msg are always valid.
unsafe { ffi::luaL_checkstack(self.0, extra, extra_msg.as_ptr()); }
}
/// Attempts to grow the stack to be large enough to hold `extra` more elements. Does nothing if the stack is already large enough.
///
/// Returns `false` if it failed to grow the stack to the desired size, `true` otherwise.
///
/// This function never shrinks the stack.
#[inline]
pub fn try_check_stack(&mut self, extra: i32) -> bool {
// SAFETY: self.0 is always valid.
unsafe { ffi::lua_checkstack(self.0, extra) != 0 }
}
/// Returns the index of the top element (last inserted) in the stack.
///
/// This also equals the amount of elements in the stack.
#[inline]
#[must_use]
pub fn get_top(&self) -> i32 {
// SAFETY: self.0 is always valid and there should never be a negative amount of elements.
unsafe {
let top = ffi::lua_gettop(self.0);
assert_unchecked(top >= 0);
top
}
}
/// Sets the stack top to be `index`.
///
/// If the new top is higher than the previous, the new elements are filled with `nil`.
///
/// An `index` of `0` removes all stack elements.
///
/// # Panics
///
/// Panics if `index` is a negative number and `abs(index) > top`.
///
/// # Lua Errors
///
/// Really high `index` values may cause a stack overflow.
#[inline]
pub fn set_top(&mut self, index: i32) {
assert!(index.is_positive() || self.is_index_ok(index), "cannot set the top to be {index}, as it is negative and out of bounds");
// SAFETY: self.0 is always valid and index is not out of bounds.
unsafe { ffi::lua_settop(self.0, index); }
}
/// Pseudo-index to the registry: a pre-defined table that C/Rust can use to store whatever it needs.
///
/// Other loaded C/Rust modules may use this table too, thus you should make your keys unique to avoid collisions
/// (by using the module name itself for example).
///
/// The integer keys of the registry are used internally by Lua, and should not be touched.
pub const REGISTRY_INDEX: i32 = ffi::LUA_REGISTRYINDEX;
/// Pseudo-index to the environment of the running [`CFunction`].
///
/// [`CFunction`]: ./type.CFunction.html
pub const ENVIRONMENT_INDEX: i32 = ffi::LUA_ENVIRONINDEX;
/// Pseudo-index to the thread environment: the table containing all the global variables (`_G`).
pub const GLOBALS_INDEX: i32 = ffi::LUA_GLOBALSINDEX;
/// Returns the pseudo-index to the upvalue #`n` (i.e. `upvalue_index(1)` returns the pseudo-index to the first upvalue).
///
/// This function does *not* check if the upvalue exists, it just returns its hypothethical pseudo-index.
///
/// If `n` is `0`, this function will instead return [`LuaState::GLOBALS_INDEX`].
#[inline]
#[must_use]
pub const fn upvalue_index(n: u8) -> i32 {
ffi::lua_upvalueindex(n as i32)
}
/// Returns a boolean stating if `index` is a valid index.
///
/// `index` is valid if `1 ≤ abs(index) ≤ top`.
///
/// This function only checks if `index` is *valid*, not *acceptable*!
#[inline]
#[must_use]
pub fn is_index_valid(&self, index: i32) -> bool {
let abs_index = index.abs();
abs_index >= 1 && abs_index <= self.get_top()
}
/// Returns a boolean stating if `index` is an "ok" index.
///
/// `index` is "ok" if `abs(index) ≤ top`.
///
/// This function is used internally for assertions.
#[inline]
#[must_use]
pub fn is_index_ok(&self, index: i32) -> bool {
index.unsigned_abs() <= self.get_top().cast_unsigned()
}
/// Converts the value at the given acceptable `index` to a generic pointer and returns it.
///
/// Only userdata, tables, and functions are accepted. Anything else will return a null pointer.
///
/// There is no intended way to convert the pointer back to its original value.
#[inline]
#[must_use]
pub fn to_pointer(&self, index: i32) -> *const () {
// SAFETY: self.0 is always valid.
unsafe { ffi::lua_topointer(self.0, index).cast() }
}
/// Pushes a copy of the value at the given valid `index` onto the stack.
///
/// If `index` is out of bounds, `nil` is pushed onto the stack.
#[inline]
pub fn push_value(&mut self, index: i32) {
// SAFETY: self.0 is always valid.
unsafe { ffi::lua_pushvalue(self.0, index); }
}
/// Pops the top element and inserts it at the given valid `index`, shifting up the other elements to make space.
///
/// This function will do nothing if `index` is 0.
///
/// To replace the element located at `index` instead of shifting them up, see [`LuaState::replace`].
///
/// # Panics
///
/// Panics if `index` is out of bounds.
/// (Pseudo-indices are out of bounds as they are not real indices.)
#[inline]
pub fn insert(&mut self, index: i32) {
assert!(self.is_index_ok(index), "cannot insert the top element at index {index}, as there are only {} elements", self.get_top());
// SAFETY: self.0 is always valid and index is not out of bounds.
unsafe { ffi::lua_insert(self.0, index); }
}
/// Pops the top element and inserts it at the given valid `index`, replacing the element that was previously located at `index`.
///
/// If `index` is zero, the top element is effectively removed.
///
/// To shift up the other elements instead of replacing it, see [`LuaState::insert`].
///
/// # Panics
///
/// Panics if `index` is out of bounds.
/// (Pseudo-indices are out of bounds as they are not real indices.)
#[inline]
pub fn replace(&mut self, index: i32) {
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());
// SAFETY: self.0 is always valid and index is not out of bounds.
unsafe { ffi::lua_replace(self.0, index); }
}
/// Removes the element at the given valid `index`, shifting down the other elements to fill the gap.
///
/// # Panics
///
/// Panics if `index` is out of bounds or `0`.
/// (Pseudo-indices are out of bounds as they are not real indices.)
#[inline]
pub fn remove(&mut self, index: i32) {
assert!(self.is_index_valid(index), "cannot delete an element at invalid index {index}");
// SAFETY: self.0 is always valid and index is not out of bounds.
unsafe { ffi::lua_remove(self.0, index); }
}
/// Copies the element at index `from_index` into the valid index `to_index`, replacing the element that was previously located at `to_index`.
///
/// If `from_index` is out of bounds, the element located at `to_index` will be replaced with `nil`.
///
/// # Panics
///
/// Panics if `to_index` is out of bounds.
#[inline]
pub fn copy(&mut self, from_index: i32, to_index: i32) {
assert!(
self.is_index_ok(to_index),
"cannot replace the element at index {to_index} with the element at index {from_index}, as there are only {} elements",
self.get_top()
);
unsafe { ffi::lua_copy(self.0, from_index, to_index); }
}
/// Checks if there's an element of any type (including `nil`) at the given index `arg_n`.
///
/// If the check fails, `LuaState::arg_error` is called using `arg_n`.
///
/// # Lua Errors
///
/// Intentionally errors if the check fails.
#[inline]
pub fn check_any(&self, arg_n: i32) {
// SAFETY: self.0 is always valid.
unsafe { ffi::luaL_checkany(self.0, arg_n); }
}
/// Returns `default` when the element at the given index `arg_n` is `nil` (or non-existing).
///
/// Otherwise, `func` is called with `self` and `arg_n` as its arguments, returning the value `func` returns.
#[inline]
pub fn opt<T>(&self, func: impl FnOnce(&LuaState, i32) -> T, arg_n: i32, default: T) -> T {
if self.is_none_or_nil(arg_n) {
default
} else {
func(self, arg_n)
}
}
/// Returns a printable representation of the element at the given `index`.
///
/// The formatting is unspecified but it loosely mimics the formatting of Lua's `tostring` function. This may change in the future.
#[inline]
pub fn display(&self, index: i32) -> impl Display + '_ {
struct ElementDisplay<'a> {
lua: &'a LuaState,
index: i32,
}
impl Display for ElementDisplay<'_> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { lua, index } = *self;
match lua.type_of(index) {
LuaType::None => f.write_str("(no value)"),
LuaType::Nil => f.write_str("nil"),
LuaType::Boolean => lua.to_boolean(index).fmt(f),
LuaType::Number => lua.to_number(index).fmt(f),
LuaType::String => {
// SAFETY: The element can only be a string.
let bytes = unsafe { lua.to_byte_string(index).unwrap_unchecked() };
for chunk in bytes.utf8_chunks() {
chunk.valid().fmt(f)?;
if !chunk.invalid().is_empty() {
f.write_char(char::REPLACEMENT_CHARACTER)?;
}
}
Ok(())
},
LuaType::Table => write!(f, "table: {:p}", lua.to_pointer(index)),
LuaType::Function => write!(f, "function: {:p}", lua.to_pointer(index)),
LuaType::Userdata | LuaType::LightUserdata => write!(f, "userdata: {:p}", lua.to_pointer(index)),
LuaType::Thread => write!(f, "thread: {:p}", lua.to_pointer(index)),
}
}
}
ElementDisplay { lua: self, index }
}
}