diff options
author | Alex Orlenko <zxteam@protonmail.com> | 2023-03-10 09:59:22 +0000 |
---|---|---|
committer | Alex Orlenko <zxteam@protonmail.com> | 2023-03-10 10:34:41 +0000 |
commit | 304880bfd18c7d4ca6ea7b2e83b2022fd1003bcd (patch) | |
tree | 42471fdfd758d65081a1646c0885866ea7a0a2a9 /src/lua.rs | |
parent | 33c276d0b4904f5f7c00502d46422cb9e84555e4 (diff) | |
download | mlua-304880bfd18c7d4ca6ea7b2e83b2022fd1003bcd.zip |
Fast metatable check for Lua::push_userdata_ref
Diffstat (limited to 'src/lua.rs')
-rw-r--r-- | src/lua.rs | 16 |
1 files changed, 15 insertions, 1 deletions
@@ -84,6 +84,7 @@ pub(crate) struct ExtraData { registered_userdata: FxHashMap<TypeId, c_int>, registered_userdata_mt: FxHashMap<*const c_void, Option<TypeId>>, + last_checked_userdata_mt: (*const c_void, Option<TypeId>), // When Lua instance dropped, setting `None` would prevent collecting `RegistryKey`s registry_unref_list: Arc<Mutex<Option<Vec<c_int>>>>, @@ -553,6 +554,7 @@ impl Lua { inner: None, registered_userdata: FxHashMap::default(), registered_userdata_mt: FxHashMap::default(), + last_checked_userdata_mt: (ptr::null(), None), registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))), app_data: RefCell::new(FxHashMap::default()), safe: false, @@ -2615,6 +2617,9 @@ impl Lua { #[inline] pub(crate) unsafe fn deregister_raw_userdata_metatable(&self, ptr: *const c_void) { (*self.extra.get()).registered_userdata_mt.remove(&ptr); + if (*self.extra.get()).last_checked_userdata_mt.0 == ptr { + (*self.extra.get()).last_checked_userdata_mt = (ptr::null(), None); + } } // Pushes a LuaRef value onto the stack, checking that it's a registered @@ -2630,11 +2635,20 @@ impl Lua { let mt_ptr = ffi::lua_topointer(state, -1); ffi::lua_pop(state, 1); + // Fast path to skip looking up the metatable in the map + let (last_mt, last_type_id) = (*self.extra.get()).last_checked_userdata_mt; + if last_mt == mt_ptr { + return Ok(last_type_id); + } + match (*self.extra.get()).registered_userdata_mt.get(&mt_ptr) { Some(&type_id) if type_id == Some(TypeId::of::<DestructedUserdata>()) => { Err(Error::UserDataDestructed) } - Some(&type_id) => Ok(type_id), + Some(&type_id) => { + (*self.extra.get()).last_checked_userdata_mt = (mt_ptr, type_id); + Ok(type_id) + } None => Err(Error::UserDataTypeMismatch), } } |