Skip to main content

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}