use crate::Table;
use crate::handle::RawHandle;
use crate::metamethod::TmEvent;
use crate::string::TString;
use crate::types::{
LUA_TBOOLEAN, LUA_TINTEGER, LUA_TLIGHTUSERDATA, LUA_TNIL, LUA_TNUMBER, LUA_TSTRING,
LUA_TVECTOR, LUA_VECTOR_SIZE,
};
use crate::value::{TValue, nil_object};
use super::{LuaNode, LuaNodeCursor, RawLuaNode};
#[allow(
clippy::missing_safety_doc,
reason = "Table's shared raw-handle contract is documented on Table"
)]
impl Table {
pub fn dummy_node_cursor() -> LuaNodeCursor {
struct SharedDummyNode(RawLuaNode);
unsafe impl Sync for SharedDummyNode {}
static DUMMY_NODE: SharedDummyNode = SharedDummyNode(crate::table::RAW_LUA_NODE_DUMMY);
LuaNodeCursor::from_ptr((&raw const DUMMY_NODE.0).cast_mut())
}
pub fn dummy_node() -> LuaNode {
unsafe { Self::dummy_node_cursor().node_unchecked() }
}
pub fn dummy_node_ptr() -> *mut RawLuaNode {
Self::dummy_node().as_ptr()
}
pub fn array_index(key: f64) -> Option<i32> {
let index = key as i32;
((index as f64) == key).then_some(index)
}
unsafe fn hash_pow2(&self, hash: u32) -> LuaNodeCursor {
unsafe {
self.node_cursor()
.add((hash as usize) & (self.node_count() - 1))
}
}
unsafe fn hash_str(&self, string: TString) -> LuaNodeCursor {
unsafe { self.hash_pow2(string.as_ptr().as_ref().unwrap_unchecked().hash) }
}
unsafe fn hash_boolean(&self, value: i32) -> LuaNodeCursor {
unsafe { self.hash_pow2(value as u32) }
}
unsafe fn hash_pointer(&self, pointer: *const ()) -> LuaNodeCursor {
let mut hash = pointer as usize as u32;
hash ^= hash >> 16;
hash = hash.wrapping_mul(0x85ebca6b);
hash ^= hash >> 13;
hash = hash.wrapping_mul(0xc2b2ae35);
hash ^= hash >> 16;
unsafe { self.hash_pow2(hash) }
}
pub(super) unsafe fn hash_num(&self, number: f64) -> LuaNodeCursor {
let bits = number.to_bits() & 0x7fff_ffff_ffff_ffff;
let mut h1 = bits as u32;
let mut h2 = (bits >> 32) as u32;
let m = 0x5bd1e995u32;
h1 ^= h2 >> 18;
h1 = h1.wrapping_mul(m);
h2 ^= h1 >> 22;
h2 = h2.wrapping_mul(m);
h1 ^= h2 >> 17;
h1 = h1.wrapping_mul(m);
h2 ^= h1 >> 19;
h2 = h2.wrapping_mul(m);
unsafe { self.hash_pow2(h2) }
}
unsafe fn hash_int(&self, integer: i64) -> LuaNodeCursor {
let bits = integer as u64;
let mut h1 = bits as u32;
let mut h2 = (bits >> 32) as u32;
let m = 0x5bd1e995u32;
h1 ^= h2 >> 18;
h1 = h1.wrapping_mul(m);
h2 ^= h1 >> 22;
h2 = h2.wrapping_mul(m);
h1 ^= h2 >> 17;
h1 = h1.wrapping_mul(m);
h2 ^= h1 >> 19;
h2 = h2.wrapping_mul(m);
unsafe { self.hash_pow2(h2) }
}
unsafe fn hash_vec(&self, vector: [f32; LUA_VECTOR_SIZE]) -> LuaNodeCursor {
let mut values = [
vector[0].to_bits(),
vector[1].to_bits(),
vector[2].to_bits(),
];
#[cfg(feature = "vector4")]
let value3 = vector[3].to_bits();
for value in &mut values {
if *value == 0x8000_0000 {
*value = 0;
}
*value ^= *value >> 17;
}
let hash = values[0].wrapping_mul(73_856_093)
^ values[1].wrapping_mul(19_349_663)
^ values[2].wrapping_mul(83_492_791);
#[cfg(feature = "vector4")]
{
let mut hash = hash;
let mut value3 = if value3 == 0x8000_0000 { 0 } else { value3 };
value3 ^= value3 >> 17;
hash ^= value3.wrapping_mul(39_916_801);
unsafe { self.hash_pow2(hash) }
}
#[cfg(not(feature = "vector4"))]
{
unsafe { self.hash_pow2(hash) }
}
}
pub(super) unsafe fn get_pointer_node(&self, key: *mut (), tag: i32) -> Option<LuaNodeCursor> {
let mut node = unsafe { self.hash_pointer(key.cast_const()) };
loop {
let node_key = unsafe { node.node_unchecked() }.key();
if node_key.tt() == LUA_TLIGHTUSERDATA
&& node_key.pointer_value() == key
&& node_key.light_userdata_tag() == tag
{
return Some(node);
}
let next = node_key.next();
if next == 0 {
break;
}
node = unsafe { node.offset(next as isize) };
}
None
}
pub unsafe fn main_position(&self, key: TValue) -> LuaNodeCursor {
unsafe {
match key.tt() {
x if x == LUA_TNUMBER => self.hash_num(key.number_value()),
x if x == LUA_TINTEGER => self.hash_int(key.integer_value()),
x if x == LUA_TVECTOR => self.hash_vec(key.vector_value()),
x if x == LUA_TSTRING => self.hash_str(key.string_value()),
x if x == LUA_TBOOLEAN => self.hash_boolean(key.boolean_value()),
x if x == LUA_TLIGHTUSERDATA => self.hash_pointer(key.pointer_value().cast_const()),
_ => self.hash_pointer(key.gc_value().as_ptr().cast()),
}
}
}
pub unsafe fn get_hash_node(&self, key: TValue) -> Option<LuaNodeCursor> {
let mut node = unsafe { self.main_position(key) };
loop {
if unsafe { node.node_unchecked() }.key().raw_equal_value(key) {
return Some(node);
}
let next = unsafe { node.node_unchecked() }.next();
if next == 0 {
break;
}
node = unsafe { node.offset(next as isize) };
}
None
}
#[inline(always)]
pub unsafe fn get_str_node(self, key: TString) -> Option<LuaNodeCursor> {
let mut node = unsafe { self.hash_str(key) };
loop {
let current = unsafe { node.node_unchecked() };
if current.has_string_key(key) {
return Some(node);
}
let next = current.next();
if next == 0 {
break;
}
node = unsafe { node.offset(next as isize) };
}
None
}
}
#[allow(
clippy::missing_safety_doc,
reason = "Table's shared raw-handle contract is documented on Table"
)]
impl Table {
pub unsafe fn get_num(&self, key: i32) -> TValue {
unsafe {
if let Some(slot) = self.array_slot_for_key(key) {
slot
} else if !self.has_dummy_node() {
let nk = key as f64;
let mut node_cursor = self.hash_num(nk);
loop {
let node_key = node_cursor.node_unchecked().key();
if node_key.tt() == LUA_TNUMBER && node_key.number_value() == nk {
return node_cursor.node_unchecked().value_unchecked();
}
let next = node_key.next();
if next == 0 {
break;
}
node_cursor = node_cursor.offset(next as isize);
}
nil_object()
} else {
nil_object()
}
}
}
pub unsafe fn get_str(&self, key: TString) -> TValue {
unsafe {
self.get_str_node(key)
.map(|node_cursor| node_cursor.node_unchecked().value_unchecked())
.unwrap_or_else(nil_object)
}
}
unsafe fn get_hash(&self, key: TValue) -> TValue {
unsafe {
self.get_hash_node(key)
.map(|node_cursor| node_cursor.node_unchecked().value_unchecked())
.unwrap_or_else(nil_object)
}
}
pub unsafe fn get(&self, key: TValue) -> TValue {
unsafe {
match key.tt() {
x if x == LUA_TNIL => nil_object(),
x if x == LUA_TSTRING => self.get_str(key.string_value()),
x if x == LUA_TNUMBER => {
if let Some(index) = Self::array_index(key.number_value()) {
self.get_num(index)
} else {
self.get_hash(key)
}
}
_ => self.get_hash(key),
}
}
}
pub unsafe fn getp(&self, key: *mut (), tag: i32) -> TValue {
unsafe {
self.get_pointer_node(key, tag)
.map(|node_cursor| node_cursor.node_unchecked().value_unchecked())
.unwrap_or_else(nil_object)
}
}
#[inline(always)]
pub unsafe fn getn(self) -> i32 {
unsafe {
let mut boundary = self.get_aboundary();
let size_array = self.as_ptr().as_ref().unwrap_unchecked().size_array;
let array = self.array_cursor();
if boundary > 0 {
let node_is_dummy = self.has_dummy_node();
if size_array > 0
&& !array.add((size_array - 1) as usize).is_nil_unchecked()
&& node_is_dummy
{
return size_array;
}
if boundary < size_array
&& !array.add((boundary - 1) as usize).is_nil_unchecked()
&& array.add(boundary as usize).is_nil_unchecked()
{
return boundary;
}
let found = self.update_aboundary(boundary);
if found > 0 {
return found;
}
}
let last_array_is_nil = if size_array > 0 {
array.add((size_array - 1) as usize).is_nil_unchecked()
} else {
false
};
if last_array_is_nil {
let mut base = array;
let mut rest = size_array;
while rest >> 1 != 0 {
let half = rest >> 1;
let half_slot = base.add(half as usize);
let half_is_nil = half_slot.is_nil_unchecked();
base = if half_is_nil { base } else { half_slot };
rest -= half;
}
let base_is_nil = base.is_nil_unchecked();
boundary = i32::from(!base_is_nil) + base.offset_from(array) as i32;
self.maybe_set_aboundary(boundary);
boundary
} else {
debug_assert!(self.has_dummy_node() || self.get_num(size_array + 1).is_nil());
size_array
}
}
}
pub unsafe fn get_tm(&self, event: TmEvent, event_name: TString) -> Option<TValue> {
let tm = unsafe { self.get_str(event_name) };
if tm.is_nil() {
unsafe {
self.as_ptr().as_mut().unwrap_unchecked().tm_cache =
self.as_ptr().as_ref().unwrap_unchecked().tm_cache | (1u8 << event as usize)
};
None
} else {
Some(tm)
}
}
}