Skip to main content

luau_vm/table/
lookup.rs

1use crate::Table;
2use crate::handle::RawHandle;
3use crate::metamethod::TmEvent;
4use crate::string::TString;
5use crate::types::{
6    LUA_TBOOLEAN, LUA_TINTEGER, LUA_TLIGHTUSERDATA, LUA_TNIL, LUA_TNUMBER, LUA_TSTRING,
7    LUA_TVECTOR, LUA_VECTOR_SIZE,
8};
9use crate::value::{TValue, nil_object};
10
11use super::{LuaNode, LuaNodeCursor, RawLuaNode};
12
13#[allow(
14    clippy::missing_safety_doc,
15    reason = "Table's shared raw-handle contract is documented on Table"
16)]
17impl Table {
18    pub fn dummy_node_cursor() -> LuaNodeCursor {
19        struct SharedDummyNode(RawLuaNode);
20
21        unsafe impl Sync for SharedDummyNode {}
22
23        static DUMMY_NODE: SharedDummyNode = SharedDummyNode(crate::table::RAW_LUA_NODE_DUMMY);
24
25        LuaNodeCursor::from_ptr((&raw const DUMMY_NODE.0).cast_mut())
26    }
27
28    pub fn dummy_node() -> LuaNode {
29        unsafe { Self::dummy_node_cursor().node_unchecked() }
30    }
31
32    pub fn dummy_node_ptr() -> *mut RawLuaNode {
33        Self::dummy_node().as_ptr()
34    }
35
36    /// `arrayindex`
37    pub fn array_index(key: f64) -> Option<i32> {
38        let index = key as i32;
39        ((index as f64) == key).then_some(index)
40    }
41
42    /// `hashpow2`
43    unsafe fn hash_pow2(&self, hash: u32) -> LuaNodeCursor {
44        unsafe {
45            self.node_cursor()
46                .add((hash as usize) & (self.node_count() - 1))
47        }
48    }
49
50    /// `hashstr`
51    unsafe fn hash_str(&self, string: TString) -> LuaNodeCursor {
52        unsafe { self.hash_pow2(string.as_ptr().as_ref().unwrap_unchecked().hash) }
53    }
54
55    /// `hashboolean`
56    unsafe fn hash_boolean(&self, value: i32) -> LuaNodeCursor {
57        unsafe { self.hash_pow2(value as u32) }
58    }
59
60    /// `hashpointer`
61    unsafe fn hash_pointer(&self, pointer: *const ()) -> LuaNodeCursor {
62        let mut hash = pointer as usize as u32;
63        hash ^= hash >> 16;
64        hash = hash.wrapping_mul(0x85ebca6b);
65        hash ^= hash >> 13;
66        hash = hash.wrapping_mul(0xc2b2ae35);
67        hash ^= hash >> 16;
68        unsafe { self.hash_pow2(hash) }
69    }
70
71    /// `hashnum`
72    pub(super) unsafe fn hash_num(&self, number: f64) -> LuaNodeCursor {
73        let bits = number.to_bits() & 0x7fff_ffff_ffff_ffff;
74        let mut h1 = bits as u32;
75        let mut h2 = (bits >> 32) as u32;
76        let m = 0x5bd1e995u32;
77
78        h1 ^= h2 >> 18;
79        h1 = h1.wrapping_mul(m);
80        h2 ^= h1 >> 22;
81        h2 = h2.wrapping_mul(m);
82        h1 ^= h2 >> 17;
83        h1 = h1.wrapping_mul(m);
84        h2 ^= h1 >> 19;
85        h2 = h2.wrapping_mul(m);
86
87        unsafe { self.hash_pow2(h2) }
88    }
89
90    /// `hashint`
91    unsafe fn hash_int(&self, integer: i64) -> LuaNodeCursor {
92        let bits = integer as u64;
93        let mut h1 = bits as u32;
94        let mut h2 = (bits >> 32) as u32;
95        let m = 0x5bd1e995u32;
96
97        h1 ^= h2 >> 18;
98        h1 = h1.wrapping_mul(m);
99        h2 ^= h1 >> 22;
100        h2 = h2.wrapping_mul(m);
101        h1 ^= h2 >> 17;
102        h1 = h1.wrapping_mul(m);
103        h2 ^= h1 >> 19;
104        h2 = h2.wrapping_mul(m);
105
106        unsafe { self.hash_pow2(h2) }
107    }
108
109    /// `hashvec`
110    unsafe fn hash_vec(&self, vector: [f32; LUA_VECTOR_SIZE]) -> LuaNodeCursor {
111        let mut values = [
112            vector[0].to_bits(),
113            vector[1].to_bits(),
114            vector[2].to_bits(),
115        ];
116        #[cfg(feature = "vector4")]
117        let value3 = vector[3].to_bits();
118        for value in &mut values {
119            if *value == 0x8000_0000 {
120                *value = 0;
121            }
122            *value ^= *value >> 17;
123        }
124
125        let hash = values[0].wrapping_mul(73_856_093)
126            ^ values[1].wrapping_mul(19_349_663)
127            ^ values[2].wrapping_mul(83_492_791);
128        #[cfg(feature = "vector4")]
129        {
130            let mut hash = hash;
131            let mut value3 = if value3 == 0x8000_0000 { 0 } else { value3 };
132            value3 ^= value3 >> 17;
133            hash ^= value3.wrapping_mul(39_916_801);
134            unsafe { self.hash_pow2(hash) }
135        }
136        #[cfg(not(feature = "vector4"))]
137        {
138            unsafe { self.hash_pow2(hash) }
139        }
140    }
141
142    pub(super) unsafe fn get_pointer_node(&self, key: *mut (), tag: i32) -> Option<LuaNodeCursor> {
143        let mut node = unsafe { self.hash_pointer(key.cast_const()) };
144
145        loop {
146            let node_key = unsafe { node.node_unchecked() }.key();
147            if node_key.tt() == LUA_TLIGHTUSERDATA
148                && node_key.pointer_value() == key
149                && node_key.light_userdata_tag() == tag
150            {
151                return Some(node);
152            }
153
154            let next = node_key.next();
155            if next == 0 {
156                break;
157            }
158
159            node = unsafe { node.offset(next as isize) };
160        }
161
162        None
163    }
164
165    /// `mainposition`
166    pub unsafe fn main_position(&self, key: TValue) -> LuaNodeCursor {
167        unsafe {
168            match key.tt() {
169                x if x == LUA_TNUMBER => self.hash_num(key.number_value()),
170                x if x == LUA_TINTEGER => self.hash_int(key.integer_value()),
171                x if x == LUA_TVECTOR => self.hash_vec(key.vector_value()),
172                x if x == LUA_TSTRING => self.hash_str(key.string_value()),
173                x if x == LUA_TBOOLEAN => self.hash_boolean(key.boolean_value()),
174                x if x == LUA_TLIGHTUSERDATA => self.hash_pointer(key.pointer_value().cast_const()),
175                _ => self.hash_pointer(key.gc_value().as_ptr().cast()),
176            }
177        }
178    }
179
180    pub unsafe fn get_hash_node(&self, key: TValue) -> Option<LuaNodeCursor> {
181        let mut node = unsafe { self.main_position(key) };
182
183        loop {
184            if unsafe { node.node_unchecked() }.key().raw_equal_value(key) {
185                return Some(node);
186            }
187
188            let next = unsafe { node.node_unchecked() }.next();
189            if next == 0 {
190                break;
191            }
192
193            node = unsafe { node.offset(next as isize) };
194        }
195
196        None
197    }
198
199    #[inline(always)]
200    pub unsafe fn get_str_node(self, key: TString) -> Option<LuaNodeCursor> {
201        let mut node = unsafe { self.hash_str(key) };
202
203        loop {
204            let current = unsafe { node.node_unchecked() };
205            if current.has_string_key(key) {
206                return Some(node);
207            }
208
209            let next = current.next();
210            if next == 0 {
211                break;
212            }
213
214            node = unsafe { node.offset(next as isize) };
215        }
216
217        None
218    }
219}
220
221#[allow(
222    clippy::missing_safety_doc,
223    reason = "Table's shared raw-handle contract is documented on Table"
224)]
225impl Table {
226    /// `luaH_getnum`
227    pub unsafe fn get_num(&self, key: i32) -> TValue {
228        unsafe {
229            if let Some(slot) = self.array_slot_for_key(key) {
230                slot
231            } else if !self.has_dummy_node() {
232                let nk = key as f64;
233                let mut node_cursor = self.hash_num(nk);
234
235                loop {
236                    let node_key = node_cursor.node_unchecked().key();
237                    if node_key.tt() == LUA_TNUMBER && node_key.number_value() == nk {
238                        return node_cursor.node_unchecked().value_unchecked();
239                    }
240
241                    let next = node_key.next();
242                    if next == 0 {
243                        break;
244                    }
245
246                    node_cursor = node_cursor.offset(next as isize);
247                }
248
249                nil_object()
250            } else {
251                nil_object()
252            }
253        }
254    }
255
256    /// `luaH_getstr`
257    pub unsafe fn get_str(&self, key: TString) -> TValue {
258        unsafe {
259            self.get_str_node(key)
260                .map(|node_cursor| node_cursor.node_unchecked().value_unchecked())
261                .unwrap_or_else(nil_object)
262        }
263    }
264
265    unsafe fn get_hash(&self, key: TValue) -> TValue {
266        unsafe {
267            self.get_hash_node(key)
268                .map(|node_cursor| node_cursor.node_unchecked().value_unchecked())
269                .unwrap_or_else(nil_object)
270        }
271    }
272
273    /// `luaH_get`
274    pub unsafe fn get(&self, key: TValue) -> TValue {
275        unsafe {
276            match key.tt() {
277                x if x == LUA_TNIL => nil_object(),
278                x if x == LUA_TSTRING => self.get_str(key.string_value()),
279                x if x == LUA_TNUMBER => {
280                    if let Some(index) = Self::array_index(key.number_value()) {
281                        self.get_num(index)
282                    } else {
283                        self.get_hash(key)
284                    }
285                }
286                _ => self.get_hash(key),
287            }
288        }
289    }
290
291    /// `luaH_getp`
292    pub unsafe fn getp(&self, key: *mut (), tag: i32) -> TValue {
293        unsafe {
294            self.get_pointer_node(key, tag)
295                .map(|node_cursor| node_cursor.node_unchecked().value_unchecked())
296                .unwrap_or_else(nil_object)
297        }
298    }
299
300    /// `luaH_getn`
301    #[inline(always)]
302    pub unsafe fn getn(self) -> i32 {
303        unsafe {
304            let mut boundary = self.get_aboundary();
305            let size_array = self.as_ptr().as_ref().unwrap_unchecked().size_array;
306            let array = self.array_cursor();
307
308            if boundary > 0 {
309                let node_is_dummy = self.has_dummy_node();
310
311                if size_array > 0
312                    && !array.add((size_array - 1) as usize).is_nil_unchecked()
313                    && node_is_dummy
314                {
315                    return size_array;
316                }
317
318                if boundary < size_array
319                    && !array.add((boundary - 1) as usize).is_nil_unchecked()
320                    && array.add(boundary as usize).is_nil_unchecked()
321                {
322                    return boundary;
323                }
324
325                let found = self.update_aboundary(boundary);
326                if found > 0 {
327                    return found;
328                }
329            }
330
331            let last_array_is_nil = if size_array > 0 {
332                array.add((size_array - 1) as usize).is_nil_unchecked()
333            } else {
334                false
335            };
336
337            if last_array_is_nil {
338                let mut base = array;
339                let mut rest = size_array;
340
341                while rest >> 1 != 0 {
342                    let half = rest >> 1;
343                    let half_slot = base.add(half as usize);
344                    let half_is_nil = half_slot.is_nil_unchecked();
345                    base = if half_is_nil { base } else { half_slot };
346                    rest -= half;
347                }
348
349                let base_is_nil = base.is_nil_unchecked();
350                boundary = i32::from(!base_is_nil) + base.offset_from(array) as i32;
351                self.maybe_set_aboundary(boundary);
352                boundary
353            } else {
354                debug_assert!(self.has_dummy_node() || self.get_num(size_array + 1).is_nil());
355                size_array
356            }
357        }
358    }
359
360    /// `luaT_gettm`
361    pub unsafe fn get_tm(&self, event: TmEvent, event_name: TString) -> Option<TValue> {
362        let tm = unsafe { self.get_str(event_name) };
363        if tm.is_nil() {
364            unsafe {
365                self.as_ptr().as_mut().unwrap_unchecked().tm_cache =
366                    self.as_ptr().as_ref().unwrap_unchecked().tm_cache | (1u8 << event as usize)
367            };
368            None
369        } else {
370            Some(tm)
371        }
372    }
373}