low_luajit/lua/type/table.rs
1use core::{ffi::CStr, marker::PhantomData, ops::Not};
2use crate::{LuaState, ffi};
3
4macro_rules! iterator_impl {
5 ($iterator:ident, $assert_err:literal; $(impla {$($impl_fn:item)*};)? $($iter_fn:item)+) => {
6 impl $iterator<'_> {
7 /// Constructs a new iterator over the table at the given `index`.
8 ///
9 /// # Panics
10 ///
11 /// Panics if the element at the given `index` is not a table.
12 #[inline]
13 pub fn new(lua: &LuaState, index: i32) -> Self {
14 let iterator = Self { lua: lua.as_ptr(), _marker: PhantomData };
15
16 let mut lua = iterator.lua();
17 lua.push_value(index);
18 lua.push_nil();
19 lua.push_nil();
20 assert!(lua.is_table(-3), $assert_err, index);
21
22 iterator
23 }
24
25 /// Grants special access to the Lua state for the iterator. Do not expose to the public API!
26 const fn lua(&self) -> LuaState {
27 // SAFETY: self.lua is always valid and the originating LuaState should be borrowed.
28 // While usually this would not be safe enough (as the user can still *read* from the state),
29 // this iterator does not interact with elements it has not pushed itself and said elements are all referenced in a table.
30 unsafe { LuaState::from_ptr(self.lua) }
31 }
32
33 $($($impl_fn)*)?
34 }
35
36 impl Iterator for $iterator<'_> {
37 type Item = (i32, i32);
38
39 $($iter_fn)+
40 }
41
42 impl Drop for $iterator<'_> {
43 #[inline]
44 fn drop(&mut self) {
45 self.lua().pop(3);
46 }
47 }
48 };
49}
50
51/// Iterator over all key-value pairs of a specified table.
52///
53/// This iterator internally calls [`LuaState::next`] for you,
54/// returning for each iteration the indices to the key and value, respectively.
55/// (Currently, the returned indices are always `-2` and `-1`, this may change in the future.)
56///
57/// Once created, three elements will be pushed onto the stack *(the table, the key, and the value)*.
58/// Each iteration overwrites the previous key-value pair's elements with the new pair's.
59/// All three elements will be automatically popped once the iterator is *dropped*.
60///
61/// After traversing the entire table, the iterator can be reused to reiterate over the entire table again and again if needed.
62///
63/// Multiple `Pairs` (and [`IPairs`]) may exist simultaneously for nested iterations,
64/// but the iterators should be used and dropped in the order in which they were created:
65/// doing otherwise is considered a logic error and will result in unexpected behavior *(not UB)*.
66///
67/// Finishing the loop without properly dropping the iterator is also considered a logic error (as the elements would not be popped).
68///
69/// Lastly, getting the key's value using a function that can replace the number stored in the stack with a string
70/// (like [`LuaState::to_string`]) may cause a logic error if said replacement occurs.
71#[repr(transparent)]
72#[derive(Debug)]
73#[must_use = "iterators are lazy and do nothing unless consumed"]
74pub struct Pairs<'a> {
75 lua: *mut ffi::lua_State,
76 _marker: PhantomData<&'a LuaState>,
77}
78
79iterator_impl!(
80 Pairs,
81 "cannot iterate over all key-value pairs of the element at index {} as it is not a table";
82
83 #[inline]
84 fn next(&mut self) -> Option<Self::Item> {
85 let mut lua = self.lua();
86 lua.pop(1);
87
88 // SAFETY: The element at index -2 should always be a table.
89 if unsafe { lua.next_unchecked(-2) } {
90 Some((-2, -1))
91 } else {
92 lua.push_nil();
93 lua.push_nil();
94 None
95 }
96 }
97);
98
99/// Iterator over the indexed key-value pairs of a specified table.
100///
101/// This iterator returns for each iteration the indices to the key and value, respectively.
102/// (Currently, the returned indices are always `-2` and `-1`, this may change in the future.)
103/// Just like Lua's `ipairs` function, it starts at key `1` and increments it for each iteration;
104/// stopping once the key does not contain a value in the table.
105///
106/// Once created, three elements will be pushed onto the stack *(the table, the value, and the key)*.
107/// Each iteration overwrites the previous key-value pair's elements with the new pair's.
108/// All three elements will be automatically popped once the iterator is *dropped*.
109///
110/// Calling `next()` after the iterator ended will still cause it to increment the key and read the next key-value pair,
111/// which could be used to skip over gaps.
112///
113/// Multiple `IPairs` (and [`Pairs`]) may exist simultaneously for nested iterations,
114/// but the iterators should be used and dropped in the order in which they were created:
115/// doing otherwise is considered a logic error and will result in unexpected behavior *(not UB)*.
116///
117/// Finishing the loop without properly dropping the iterator is also considered a logic error (as the elements would not be popped).
118#[repr(transparent)]
119#[derive(Debug)]
120#[must_use = "iterators are lazy and do nothing unless consumed"]
121pub struct IPairs<'a> {
122 lua: *mut ffi::lua_State,
123 _marker: PhantomData<&'a LuaState>,
124}
125
126iterator_impl!(
127 IPairs,
128 "cannot iterate over the indexed key-value pairs of the element at index {} as it is not a table";
129
130 #[inline]
131 #[allow(clippy::cast_possible_truncation)]
132 fn next(&mut self) -> Option<Self::Item> {
133 let mut lua = self.lua();
134 let i = lua.to_integer(-2) + 1;
135 lua.pop(2);
136
137 lua.push_integer(i);
138 // SAFETY: lua.0 is always valid and the element at index -2 should always be a table.
139 unsafe { ffi::lua_rawgeti(lua.0, -2, i as i32); }
140 lua.is_nil(-1).not().then_some((-2, -1))
141 }
142
143 #[inline]
144 fn size_hint(&self) -> (usize, Option<usize>) {
145 (0, Some(self.lua().object_length(-3)))
146 }
147);
148
149impl IPairs<'_> {
150 /// Returns the index that the iterator has last read.
151 ///
152 /// If the iterator has not done its first iteration yet, `0` will be returned.
153 #[inline]
154 pub fn current_index(&self) -> usize {
155 self.lua().to_integer(-2).cast_unsigned()
156 }
157}
158
159/// Table related functions.
160impl LuaState {
161 /// Returns `true` if the value at the given acceptable `index` is a table, `false` otherwise.
162 #[inline]
163 #[must_use]
164 pub fn is_table(&self, index: i32) -> bool {
165 // SAFETY: self.0 is always valid.
166 unsafe { ffi::lua_istable(self.0, index) != 0 }
167 }
168
169 /// Creates a new empty table and pushes it onto the stack.
170 ///
171 /// To pre-allocate elements when creating a new table, see [`LuaState::create_table`].
172 #[inline]
173 pub fn new_table(&mut self) {
174 // SAFETY: self.0 is always valid.
175 unsafe { ffi::lua_newtable(self.0); }
176 }
177
178 /// Creates a new empty table and pushes it onto the stack.
179 ///
180 /// The table will have `n_arr` array elements and `n_rec` non-array elements pre-allocated.
181 ///
182 /// To create a new table without pre-allocating, see [`LuaState::new_table`].
183 ///
184 /// # Lua Errors
185 ///
186 /// Calling this function way too many times may trigger a stack overflow.
187 ///
188 /// Incredibly high values for `n_arr` or `n_rec` may instantly trigger a table overflow.
189 #[inline]
190 pub fn create_table(&mut self, n_arr: i32, n_rec: i32) {
191 // SAFETY: self.0 is always valid.
192 unsafe { ffi::lua_createtable(self.0, n_arr, n_rec); }
193 }
194
195 /// Does the equivalent of the Lua code `t[key]`, where `t` is the value located at the given valid `index`.
196 ///
197 /// The result is pushed onto the stack (even if `nil`).
198 ///
199 /// For a function that does the same but allows any value instead of `key`, see [`LuaState::get_table`].
200 ///
201 /// # Lua Errors
202 ///
203 /// Not every value `t` can be indexed: for example, if `t` is `nil` this function will error.
204 ///
205 /// Indexing `t` may also trigger the `__index` metamethod which could error.
206 #[inline]
207 pub fn get_field(&mut self, index: i32, key: &CStr) {
208 // SAFETY: self.0 is always valid and key's pointer is valid for the duration of this function.
209 unsafe { ffi::lua_getfield(self.0, index, key.as_ptr()); }
210 }
211
212 /// Does the equivalent of the Lua code `t[key] = v`, where `t` is the value located at the given valid `index`
213 /// and `v` is the value at the top of the stack (which is popped).
214 ///
215 /// For a function that does the same but allows any value instead of `key`, see [`LuaState::set_table`].
216 ///
217 /// # Panics
218 ///
219 /// Panics if the stack is empty.
220 ///
221 /// # Lua Errors
222 ///
223 /// Not every value `t` can be indexed: for example, if `t` is `nil` this function will error.
224 ///
225 /// Indexing `t` may also trigger the `__newindex` metamethod which could error.
226 #[inline]
227 pub fn set_field(&mut self, index: i32, key: &CStr) {
228 assert!(self.get_top() > 0, "tried to set field {key:?} for the table at index {index} despite having an empty stack");
229
230 // SAFETY: self.0 is always valid, key's pointer is valid for the duration of this function, and the stack is not empty.
231 unsafe { ffi::lua_setfield(self.0, index, key.as_ptr()); }
232 }
233
234 /// Gets the global named `name` and pushes it onto the stack (even if `nil`).
235 ///
236 /// # Lua Errors
237 ///
238 /// Indexing the global environment may trigger the `__index` metamethod which could error.
239 #[inline]
240 pub fn get_global(&mut self, name: &CStr) {
241 // SAFETY: self.0 is always valid and name's pointer is valid for the duration of this function.
242 unsafe { ffi::lua_getglobal(self.0, name.as_ptr()); }
243 }
244
245 /// Pops the top element and sets it as the value of the global named `name`.
246 ///
247 /// # Panics
248 ///
249 /// Panics if the stack is empty.
250 ///
251 /// # Lua Errors
252 ///
253 /// Indexing the global environment may trigger the `__newindex` metamethod which could error.
254 #[inline]
255 pub fn set_global(&mut self, name: &CStr) {
256 assert!(self.get_top() > 0, "tried to set global {name:?} despite having an empty stack");
257
258 // SAFETY: self.0 and name's pointer are always valid and the stack is not empty.
259 unsafe { ffi::lua_setglobal(self.0, name.as_ptr()); }
260 }
261
262 /// Does the equivalent of the Lua code `t[k]`, where `t` is the value located at the given valid `index`
263 /// and `k` is the value at the top of the stack (which is popped).
264 ///
265 /// The result is pushed onto the stack, replacing `k` (even if `nil`).
266 ///
267 /// # Panics
268 ///
269 /// Panics if the stack is empty.
270 ///
271 /// # Lua Errors
272 ///
273 /// Not every value `t` can be indexed: for example, if `t` is `nil` this function will error.
274 ///
275 /// Indexing `t` may also trigger the `__index` metamethod which could error.
276 #[inline]
277 pub fn get_table(&mut self, index: i32) {
278 assert!(self.get_top() > 0, "tried to get a value from the table at index {index} despite having an empty stack");
279
280 // SAFETY: self.0 is always valid and the stack is not empty.
281 unsafe { ffi::lua_gettable(self.0, index); }
282 }
283
284 /// Does the equivalent of the Lua code `t[k] = v`, where `t` is the value located at the given valid `index`
285 /// and `v` and `k` are the values at the top of the stack (which are popped from the stack in said order).
286 ///
287 /// # Panics
288 ///
289 /// Panics if the stack does not contain both `v` and `k` (thus contains less than 2 elements).
290 ///
291 /// # Lua Errors
292 ///
293 /// Not every value `t` or `k` can be used: if `t` or `k` are `nil` this function will error.
294 ///
295 /// Indexing `t` may also trigger the `__newindex` metamethod which could error.
296 #[inline]
297 pub fn set_table(&mut self, index: i32) {
298 assert!(2 <= self.get_top(), "tried to set a value for the table at index {index} despite not having `v` and `k` in the stack");
299
300 // SAFETY: self.0 is always valid and v and k are in the stack.
301 unsafe { ffi::lua_settable(self.0, index); }
302 }
303
304 /// Accesses a table similarly to [`LuaState::get_table`], but without triggering any metamethod (see [`LuaState::get_table`]).
305 ///
306 /// # Panics
307 ///
308 /// Panics if the element at the given valid `index` is not a table or if the stack is empty.
309 #[inline]
310 pub fn raw_get(&mut self, index: i32) {
311 assert!(self.is_table(index), "cannot raw get a value from the element at index {index} as it is not a table");
312 assert!(self.get_top() > 0, "tried to raw get a value from the table at index {index} despite having an empty stack");
313
314 // SAFETY: self.0 is always valid, the element specified is a table and the stack is not empty.
315 unsafe { ffi::lua_rawget(self.0, index); }
316 }
317
318 /// Accesses a table similarly to [`LuaState::set_table`], but without triggering any metamethod (see [`LuaState::set_table`]).
319 ///
320 /// # Panics
321 ///
322 /// Panics if the element at the given valid `index` is not a table or if the stack is empty.
323 ///
324 /// # Lua errors
325 ///
326 /// Not every value `k` can be used: if `k` is `nil` this function will error.
327 #[inline]
328 pub fn raw_set(&mut self, index: i32) {
329 assert!(self.is_table(index), "cannot raw set a value for the element at index {index} as it is not a table");
330 assert!(2 <= self.get_top(), "tried to raw set a value for the table at index {index} despite not having `v` and `k` in the stack");
331
332 // SAFETY: self.0 is always valid, the element specified is a table and v and k are in the stack.
333 unsafe { ffi::lua_rawset(self.0, index); }
334 }
335
336 /// Does the equivalent of the Lua code `t[i]`, where `t` is the value located at the given valid `index`.
337 ///
338 /// The result is pushed onto the stack.
339 ///
340 /// # Panics
341 ///
342 /// Panics if the element at the given valid `index` is not a table.
343 #[inline]
344 pub fn raw_geti(&mut self, index: i32, i: i32) {
345 assert!(self.is_table(index), "cannot raw get an element from the element at index {index} as it is not a table");
346
347 // SAFETY: self.0 is always valid and the element specified is a table.
348 unsafe { ffi::lua_rawgeti(self.0, index, i); }
349 }
350
351 /// Does the equivalent of the Lua code `t[i] = v`, where `t` is the value located at the given valid `index`
352 /// and `v` is the value at the top of the stack (which is popped).
353 ///
354 /// # Panics
355 ///
356 /// Panics if the element at the given valid `index` is not a table or if the stack is empty.
357 #[inline]
358 pub fn raw_seti(&mut self, index: i32, i: i32) {
359 assert!(self.is_table(index), "cannot raw set a value for the element at index {index} as it is not a table");
360 assert!(self.get_top() > 0, "tried to raw set a value for the table at index {index} despite having an empty stack");
361
362 // SAFETY: self.0 is always valid, the element specified is a table and the stack is not empty.
363 unsafe { ffi::lua_rawseti(self.0, index, i); }
364 }
365
366 /// Pops the last element of the stack, treating it as the key,
367 /// and pushes onto the stack the *next* key-value pair from the table at the given `index`, returning `true`.
368 ///
369 /// If there is no next key-value pair, then nothing is pushed and `false` is returned instead.
370 ///
371 /// This function can be used to traverse all elements of a table, see the example below.
372 ///
373 /// # Panics
374 ///
375 /// Panics if the element at the given `index` is not a table.
376 ///
377 /// # Lua Errors
378 ///
379 /// If the given key is neither `nil` nor present in the table, this function will error.
380 ///
381 /// # Examples
382 ///
383 /// ```
384 /// # use low_luajit::{LuaState, AmountReturned};
385 /// # extern "C-unwind" fn example(mut lua: LuaState) -> AmountReturned {
386 /// # let t = 1;
387 /// // Assume the table is located in the stack at index t.
388 /// lua.push_nil(); // Push the first (dummy) key.
389 /// while lua.next(t) { // Get the next key-value pair.
390 /// // (The key is located at index -2, while the value is located at index -1.)
391 /// // Use the key-value pair in some way...
392 /// lua.pop(1); // Pop the value, so that the key can be used to get the next key-value pair.
393 /// }
394 /// # AmountReturned::NONE }
395 /// ```
396 pub fn next(&mut self, index: i32) -> bool {
397 assert!(self.is_table(index), "cannot get the next key-value pair of the element at index {index} as it is not a table");
398
399 // SAFETY: The element at the given index is a table.
400 unsafe { self.next_unchecked(index) }
401 }
402
403 /// Behaves exactly as [`LuaState::next`], but without doing any checks.
404 ///
405 /// # Safety
406 ///
407 /// The element at the given `index` must be a table.
408 pub unsafe fn next_unchecked(&mut self, index: i32) -> bool {
409 // SAFETY: The caller must ensure the element at the given index is a table.
410 unsafe { ffi::lua_next(self.0, index) != 0 }
411 }
412
413 /// Returns an iterator over all key-value pairs of the table at the given `index`,
414 /// mimicking the behavior of Lua's `pairs` function.
415 ///
416 /// For each iteration, the indices of the current key-value pair are returned.
417 ///
418 /// *[See also the `Pairs` struct](struct.Pairs.html)*.
419 ///
420 /// # Panics
421 ///
422 /// Panics if the element at the given `index` is not a table.
423 ///
424 /// # Examples
425 ///
426 /// Printing the Lua standard library:
427 ///
428 /// ```
429 /// # use low_luajit::{LuaState, AmountReturned};
430 /// # extern "C-unwind" fn example(lua: LuaState) -> AmountReturned {
431 /// for (k, v) in lua.pairs(LuaState::GLOBALS_INDEX) {
432 /// println!("{}: {}", lua.display(k), lua.display(v));
433 ///
434 /// if lua.is_table(v) {
435 /// for (k, v) in lua.pairs(v) {
436 /// println!("\t{}: {}", lua.display(k), lua.display(v));
437 /// }
438 /// }
439 /// }
440 /// # AmountReturned::NONE }
441 /// ```
442 #[inline]
443 pub fn pairs(&self, index: i32) -> Pairs<'_> {
444 Pairs::new(self, index)
445 }
446
447 /// Returns an iterator over the indexed key-value pairs of the table at the given `index`,
448 /// mimicking the behavior of Lua's `ipairs` function.
449 ///
450 /// For each iteration, the indices of the current key-value pair are returned.
451 ///
452 /// *[See also the `IPairs` struct](struct.IPairs.html)*.
453 ///
454 /// # Panics
455 ///
456 /// Panics if the element at the given `index` is not a table.
457 #[inline]
458 pub fn ipairs(&self, index: i32) -> IPairs<'_> {
459 IPairs::new(self, index)
460 }
461
462 /// Gets the metatable of the value at the given acceptable `index` and pushes it onto the stack, returning `true`.
463 ///
464 /// If `index` is invalid or the value has no metatable, nothing is pushed and the function returns `false`.
465 #[inline]
466 pub fn get_metatable(&mut self, index: i32) -> bool {
467 // SAFETY: self.0 is always valid.
468 unsafe { ffi::lua_getmetatable(self.0, index) != 0 }
469 }
470
471 /// Pops the top elemenet and sets it as the metatable of the value at the given acceptable `index`.
472 ///
473 /// This function always returns `true`.
474 ///
475 /// # Panics
476 ///
477 /// Panics if the top element is not a table or if the stack is empty.
478 #[inline]
479 pub fn set_metatable(&mut self, index: i32) -> bool {
480 assert!(self.get_top() > 0, "tried to set the metatable for the element at index {index} despite having an empty stack");
481 assert!(self.is_table(-1), "cannot set the metatable for the element at index {index}, as the given metatable is not a table");
482
483 // SAFETY: self.0 is always valid, the stack is not empty and the top element is a table.
484 unsafe { ffi::lua_setmetatable(self.0, index) != 0 }
485 }
486
487 /// Gets the environment table of the value at the given `index` and pushes it onto the stack.
488 ///
489 /// If `index` is invalid or the value has no environment table, `nil` is pushed onto the stack.
490 #[inline]
491 pub fn get_fenv(&mut self, index: i32) {
492 // SAFETY: self.0 is always valid.
493 unsafe { ffi::lua_getfenv(self.0, index); }
494 }
495
496 /// Pops a table from the stack and sets it as the environment table of the value at the given `index`, returning `true`.
497 ///
498 /// Only functions, userdata and threads may have an environment table.
499 ///
500 /// If `index` is invalid or the value's type is not one listed above, the stack is still popped but the function returns `false`.
501 ///
502 /// # Panics
503 ///
504 /// Panics if the top element is not a table or if the stack is empty.
505 #[inline]
506 pub fn set_fenv(&mut self, index: i32) -> bool {
507 assert!(self.get_top() > 0, "tried to set the environment table for the element at index {index} despite having an empty stack");
508 assert!(self.is_table(-1), "cannot set the environment table for the element at index {index}, as the given environment table is not a table");
509
510 // SAFETY: self.0 is always valid, the stack is not empty and the top element is a table.
511 unsafe { ffi::lua_setfenv(self.0, index) != 0 }
512 }
513
514 /// Calls the metamethod `method` from the metatable of the element at the given `index`.
515 ///
516 /// The metamethod will receive the element itself as its only argument and its result will be pushed onto the stack, returning `true`.
517 ///
518 /// If there is no metatable or no metamethod, nothing is pushed and this function returns `false` instead.
519 ///
520 /// # Lua Errors
521 ///
522 /// The called metamethod may error.
523 #[inline]
524 pub fn call_meta(&mut self, index: i32, method: &CStr) -> bool {
525 // SAFETY: method's pointer is valid for the duration of this function.
526 unsafe { ffi::luaL_callmeta(self.0, index, method.as_ptr()) != 0 }
527 }
528}