Skip to content

Commit 959e61b

Browse files
committed
Optimize tables:
- Use reference thread directly for simple cases - Fix issue when calling raw_set on readonly table (Luau) - Add fasttrack methods for get/set/len when metatable is not set
1 parent 1040c0a commit 959e61b

File tree

1 file changed

+45
-27
lines changed

1 file changed

+45
-27
lines changed

src/table.rs

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ impl<'lua> Table<'lua> {
5858
///
5959
/// [`raw_set`]: #method.raw_set
6060
pub fn set<K: ToLua<'lua>, V: ToLua<'lua>>(&self, key: K, value: V) -> Result<()> {
61+
// Fast track
62+
if !self.has_metatable() {
63+
return self.raw_set(key, value);
64+
}
65+
6166
let lua = self.0.lua;
6267
let key = key.to_lua(lua)?;
6368
let value = value.to_lua(lua)?;
@@ -98,6 +103,11 @@ impl<'lua> Table<'lua> {
98103
///
99104
/// [`raw_get`]: #method.raw_get
100105
pub fn get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
106+
// Fast track
107+
if !self.has_metatable() {
108+
return self.raw_get(key);
109+
}
110+
101111
let lua = self.0.lua;
102112
let key = key.to_lua(lua)?;
103113

@@ -116,18 +126,7 @@ impl<'lua> Table<'lua> {
116126

117127
/// Checks whether the table contains a non-nil value for `key`.
118128
pub fn contains_key<K: ToLua<'lua>>(&self, key: K) -> Result<bool> {
119-
let lua = self.0.lua;
120-
let key = key.to_lua(lua)?;
121-
122-
unsafe {
123-
let _sg = StackGuard::new(lua.state);
124-
check_stack(lua.state, 4)?;
125-
126-
lua.push_ref(&self.0);
127-
lua.push_value(key)?;
128-
protect_lua!(lua.state, 2, 1, fn(state) ffi::lua_gettable(state, -2))?;
129-
Ok(ffi::lua_isnil(lua.state, -1) == 0)
130-
}
129+
Ok(self.get::<_, Value>(key)? != Value::Nil)
131130
}
132131

133132
/// Compares two tables for equality.
@@ -199,7 +198,14 @@ impl<'lua> Table<'lua> {
199198
lua.push_ref(&self.0);
200199
lua.push_value(key)?;
201200
lua.push_value(value)?;
202-
if lua.unlikely_memory_error() {
201+
202+
#[cfg(not(feature = "luau"))]
203+
let protect = !lua.unlikely_memory_error();
204+
// If Luau table is readonly it will throw an exception
205+
#[cfg(feature = "luau")]
206+
let protect = !lua.unlikely_memory_error() || self.is_readonly();
207+
208+
if !protect {
203209
ffi::lua_rawset(lua.state, -3);
204210
ffi::lua_pop(lua.state, 1);
205211
Ok(())
@@ -295,6 +301,11 @@ impl<'lua> Table<'lua> {
295301
///
296302
/// [`raw_len`]: #method.raw_len
297303
pub fn len(&self) -> Result<Integer> {
304+
// Fast track
305+
if !self.has_metatable() {
306+
return Ok(self.raw_len());
307+
}
308+
298309
let lua = self.0.lua;
299310
unsafe {
300311
let _sg = StackGuard::new(lua.state);
@@ -307,14 +318,8 @@ impl<'lua> Table<'lua> {
307318

308319
/// Returns the result of the Lua `#` operator, without invoking the `__len` metamethod.
309320
pub fn raw_len(&self) -> Integer {
310-
let lua = self.0.lua;
311-
unsafe {
312-
let _sg = StackGuard::new(lua.state);
313-
assert_stack(lua.state, 1);
314-
315-
lua.push_ref(&self.0);
316-
ffi::lua_rawlen(lua.state, -1) as Integer
317-
}
321+
let ref_thread = self.0.lua.ref_thread();
322+
unsafe { ffi::lua_rawlen(ref_thread, self.0.index) as Integer }
318323
}
319324

320325
/// Returns a reference to the metatable of this table, or `None` if no metatable is set.
@@ -355,15 +360,28 @@ impl<'lua> Table<'lua> {
355360
}
356361
}
357362

363+
/// Returns true if the table has metatable attached.
364+
#[doc(hidden)]
365+
#[inline]
366+
pub fn has_metatable(&self) -> bool {
367+
let ref_thread = self.0.lua.ref_thread();
368+
unsafe {
369+
if ffi::lua_getmetatable(ref_thread, self.0.index) != 0 {
370+
ffi::lua_pop(ref_thread, 1);
371+
return true;
372+
}
373+
}
374+
false
375+
}
376+
358377
/// Sets `readonly` attribute on the table.
359378
///
360379
/// Requires `feature = "luau"`
361380
#[cfg(any(feature = "luau", doc))]
362381
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
363382
pub fn set_readonly(&self, enabled: bool) {
364-
let lua = self.0.lua;
383+
let ref_thread = self.0.lua.ref_thread();
365384
unsafe {
366-
let ref_thread = lua.ref_thread();
367385
ffi::lua_setreadonly(ref_thread, self.0.index, enabled as _);
368386
if !enabled {
369387
// Reset "safeenv" flag
@@ -378,8 +396,8 @@ impl<'lua> Table<'lua> {
378396
#[cfg(any(feature = "luau", doc))]
379397
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
380398
pub fn is_readonly(&self) -> bool {
381-
let lua = self.0.lua;
382-
unsafe { ffi::lua_getreadonly(lua.ref_thread(), self.0.index) != 0 }
399+
let ref_thread = self.0.lua.ref_thread();
400+
unsafe { ffi::lua_getreadonly(ref_thread, self.0.index) != 0 }
383401
}
384402

385403
/// Converts the table to a generic C pointer.
@@ -390,8 +408,8 @@ impl<'lua> Table<'lua> {
390408
/// Typically this function is used only for hashing and debug information.
391409
#[inline]
392410
pub fn to_pointer(&self) -> *const c_void {
393-
let lua = self.0.lua;
394-
unsafe { ffi::lua_topointer(lua.ref_thread(), self.0.index) }
411+
let ref_thread = self.0.lua.ref_thread();
412+
unsafe { ffi::lua_topointer(ref_thread, self.0.index) }
395413
}
396414

397415
/// Consume this table and return an iterator over the pairs of the table.

0 commit comments

Comments
 (0)