Use zero for no hash.

This commit is contained in:
Stephen Chung 2021-12-05 17:05:19 +08:00
parent 6f6380ca3a
commit 54f78c5cd9
3 changed files with 76 additions and 27 deletions

View File

@ -1,6 +1,7 @@
//! Module defining the AST (abstract syntax tree). //! Module defining the AST (abstract syntax tree).
use crate::calc_fn_hash; use crate::calc_fn_hash;
use crate::func::hashing::DEFAULT_HASH;
use crate::module::NamespaceRef; use crate::module::NamespaceRef;
use crate::tokenizer::Token; use crate::tokenizer::Token;
use crate::types::dynamic::Union; use crate::types::dynamic::Union;
@ -1801,7 +1802,7 @@ impl OpAssignment<'_> {
pub struct FnCallHashes { pub struct FnCallHashes {
/// Pre-calculated hash for a script-defined function ([`None`] if native functions only). /// Pre-calculated hash for a script-defined function ([`None`] if native functions only).
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
pub script: Option<u64>, pub script: u64,
/// Pre-calculated hash for a native Rust function with no parameter types. /// Pre-calculated hash for a native Rust function with no parameter types.
pub native: u64, pub native: u64,
} }
@ -1809,11 +1810,11 @@ pub struct FnCallHashes {
impl fmt::Debug for FnCallHashes { impl fmt::Debug for FnCallHashes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
if let Some(script) = self.script { if self.script != 0 {
return if script == self.native { return if self.script == self.native {
fmt::Debug::fmt(&self.native, f) fmt::Debug::fmt(&self.native, f)
} else { } else {
write!(f, "({}, {})", script, self.native) write!(f, "({}, {})", self.script, self.native)
}; };
} }
@ -1824,9 +1825,11 @@ impl fmt::Debug for FnCallHashes {
impl From<u64> for FnCallHashes { impl From<u64> for FnCallHashes {
#[inline(always)] #[inline(always)]
fn from(hash: u64) -> Self { fn from(hash: u64) -> Self {
let hash = if hash == 0 { DEFAULT_HASH } else { hash };
Self { Self {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
script: Some(hash), script: hash,
native: hash, native: hash,
} }
} }
@ -1839,8 +1842,8 @@ impl FnCallHashes {
pub const fn from_native(hash: u64) -> Self { pub const fn from_native(hash: u64) -> Self {
Self { Self {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
script: None, script: 0,
native: hash, native: if hash == 0 { DEFAULT_HASH } else { hash },
} }
} }
/// Create a [`FnCallHashes`] with both native Rust and script function hashes. /// Create a [`FnCallHashes`] with both native Rust and script function hashes.
@ -1849,8 +1852,8 @@ impl FnCallHashes {
pub const fn from_all(#[cfg(not(feature = "no_function"))] script: u64, native: u64) -> Self { pub const fn from_all(#[cfg(not(feature = "no_function"))] script: u64, native: u64) -> Self {
Self { Self {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
script: Some(script), script: if script == 0 { DEFAULT_HASH } else { script },
native, native: if native == 0 { DEFAULT_HASH } else { native },
} }
} }
/// Is this [`FnCallHashes`] native Rust only? /// Is this [`FnCallHashes`] native Rust only?
@ -1858,7 +1861,7 @@ impl FnCallHashes {
#[must_use] #[must_use]
pub const fn is_native_only(&self) -> bool { pub const fn is_native_only(&self) -> bool {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
return self.script.is_none(); return self.script == 0;
#[cfg(feature = "no_function")] #[cfg(feature = "no_function")]
return true; return true;

View File

@ -177,6 +177,10 @@ impl Engine {
allow_dynamic: bool, allow_dynamic: bool,
is_op_assignment: bool, is_op_assignment: bool,
) -> Option<&'s FnResolutionCacheEntry> { ) -> Option<&'s FnResolutionCacheEntry> {
if hash_script == 0 {
return None;
}
let fn_name = fn_name.as_ref(); let fn_name = fn_name.as_ref();
let mut hash = args.as_ref().map_or(hash_script, |args| { let mut hash = args.as_ref().map_or(hash_script, |args| {
@ -562,13 +566,10 @@ impl Engine {
// Scripted function call? // Scripted function call?
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
let hash_script = hashes.script; if let Some(FnResolutionCacheEntry { func, source }) = self
.resolve_fn(mods, state, lib, fn_name, hashes.script, None, false, false)
#[cfg(not(feature = "no_function"))]
if let Some(FnResolutionCacheEntry { func, source }) = hash_script.and_then(|hash| {
self.resolve_fn(mods, state, lib, fn_name, hash, None, false, false)
.cloned() .cloned()
}) { {
// Script function call // Script function call
assert!(func.is_script()); assert!(func.is_script());

View File

@ -8,7 +8,13 @@ use std::{
iter::empty, iter::empty,
}; };
/// A hasher that only takes one single [`u64`] and returns it as a hash key. pub const DEFAULT_HASH: u64 = 42;
/// A hasher that only takes one single [`u64`] and returns it as a non-zero hash key.
///
/// # Zeros
///
/// If the value is zero, then it is mapped to `DEFAULT_HASH`.
/// ///
/// # Panics /// # Panics
/// ///
@ -29,6 +35,10 @@ impl Hasher for StraightHasher {
key.copy_from_slice(bytes); key.copy_from_slice(bytes);
self.0 = u64::from_ne_bytes(key); self.0 = u64::from_ne_bytes(key);
if self.0 == 0 {
self.0 = DEFAULT_HASH
}
} }
} }
@ -41,7 +51,7 @@ impl BuildHasher for StraightHasherBuilder {
#[inline(always)] #[inline(always)]
fn build_hasher(&self) -> Self::Hasher { fn build_hasher(&self) -> Self::Hasher {
StraightHasher(42) StraightHasher(DEFAULT_HASH)
} }
} }
@ -52,11 +62,15 @@ pub fn get_hasher() -> ahash::AHasher {
ahash::AHasher::default() ahash::AHasher::default()
} }
/// Calculate a [`u64`] hash key from a namespace-qualified variable name. /// Calculate a non-zero [`u64`] hash key from a namespace-qualified variable name.
/// ///
/// Module names are passed in via `&str` references from an iterator. /// Module names are passed in via `&str` references from an iterator.
/// Parameter types are passed in via [`TypeId`] values from an iterator. /// Parameter types are passed in via [`TypeId`] values from an iterator.
/// ///
/// # Zeros
///
/// If the hash happens to be zero, it is mapped to `DEFAULT_HASH`.
///
/// # Note /// # Note
/// ///
/// The first module name is skipped. Hashing starts from the _second_ module in the chain. /// The first module name is skipped. Hashing starts from the _second_ module in the chain.
@ -76,15 +90,23 @@ pub fn calc_qualified_var_hash<'a>(
.for_each(|m| m.as_ref().hash(s)); .for_each(|m| m.as_ref().hash(s));
len.hash(s); len.hash(s);
var_name.as_ref().hash(s); var_name.as_ref().hash(s);
s.finish()
match s.finish() {
0 => DEFAULT_HASH,
r => r,
}
} }
/// Calculate a [`u64`] hash key from a namespace-qualified function name /// Calculate a non-zero [`u64`] hash key from a namespace-qualified function name
/// and the number of parameters, but no parameter types. /// and the number of parameters, but no parameter types.
/// ///
/// Module names are passed in via `&str` references from an iterator. /// Module names are passed in via `&str` references from an iterator.
/// Parameter types are passed in via [`TypeId`] values from an iterator. /// Parameter types are passed in via [`TypeId`] values from an iterator.
/// ///
/// # Zeros
///
/// If the hash happens to be zero, it is mapped to `DEFAULT_HASH`.
///
/// # Note /// # Note
/// ///
/// The first module name is skipped. Hashing starts from the _second_ module in the chain. /// The first module name is skipped. Hashing starts from the _second_ module in the chain.
@ -106,22 +128,34 @@ pub fn calc_qualified_fn_hash(
len.hash(s); len.hash(s);
fn_name.as_ref().hash(s); fn_name.as_ref().hash(s);
num.hash(s); num.hash(s);
s.finish()
match s.finish() {
0 => DEFAULT_HASH,
r => r,
}
} }
/// Calculate a [`u64`] hash key from a non-namespace-qualified function name /// Calculate a non-zero [`u64`] hash key from a non-namespace-qualified function name
/// and the number of parameters, but no parameter types. /// and the number of parameters, but no parameter types.
/// ///
/// Parameter types are passed in via [`TypeId`] values from an iterator. /// Parameter types are passed in via [`TypeId`] values from an iterator.
///
/// # Zeros
///
/// If the hash happens to be zero, it is mapped to `DEFAULT_HASH`.
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn calc_fn_hash(fn_name: impl AsRef<str>, num: usize) -> u64 { pub fn calc_fn_hash(fn_name: impl AsRef<str>, num: usize) -> u64 {
calc_qualified_fn_hash(empty::<&str>(), fn_name, num) calc_qualified_fn_hash(empty::<&str>(), fn_name, num)
} }
/// Calculate a [`u64`] hash key from a list of parameter types. /// Calculate a non-zero [`u64`] hash key from a list of parameter types.
/// ///
/// Parameter types are passed in via [`TypeId`] values from an iterator. /// Parameter types are passed in via [`TypeId`] values from an iterator.
///
/// # Zeros
///
/// If the hash happens to be zero, it is mapped to `DEFAULT_HASH`.
#[inline] #[inline]
#[must_use] #[must_use]
pub fn calc_fn_params_hash(params: impl Iterator<Item = TypeId>) -> u64 { pub fn calc_fn_params_hash(params: impl Iterator<Item = TypeId>) -> u64 {
@ -129,12 +163,23 @@ pub fn calc_fn_params_hash(params: impl Iterator<Item = TypeId>) -> u64 {
let mut len = 0; let mut len = 0;
params.inspect(|_| len += 1).for_each(|t| t.hash(s)); params.inspect(|_| len += 1).for_each(|t| t.hash(s));
len.hash(s); len.hash(s);
s.finish()
match s.finish() {
0 => DEFAULT_HASH,
r => r,
}
} }
/// Combine two [`u64`] hashes by taking the XOR of them. /// Combine two [`u64`] hashes by taking the XOR of them.
///
/// # Zeros
///
/// If the hash happens to be zero, it is mapped to `DEFAULT_HASH`.
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub const fn combine_hashes(a: u64, b: u64) -> u64 { pub const fn combine_hashes(a: u64, b: u64) -> u64 {
a ^ b match a ^ b {
0 => DEFAULT_HASH,
r => r,
}
} }