//! Module containing various utility types and functions. use crate::fn_native::{shared_make_mut, shared_take}; use crate::stdlib::{ any::TypeId, borrow::Borrow, boxed::Box, cmp::Ordering, collections::HashMap, fmt, fmt::{Debug, Display}, hash::{BuildHasher, Hash, Hasher}, iter::{empty, FromIterator}, num::NonZeroU64, ops::{Add, AddAssign, Deref, DerefMut}, str::FromStr, string::{String, ToString}, vec::Vec, }; use crate::Shared; /// A hasher that only takes one single [`NonZeroU64`] and returns it as a hash key. /// /// # Panics /// /// Panics when hashing any data type other than a [`NonZeroU64`]. #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct StraightHasher(NonZeroU64); impl Hasher for StraightHasher { #[inline(always)] fn finish(&self) -> u64 { self.0.get() } #[inline(always)] fn write(&mut self, bytes: &[u8]) { let mut key = [0_u8; 8]; key.copy_from_slice(&bytes[..8]); // Panics if fewer than 8 bytes // HACK - If it so happens to hash directly to zero (OMG!) then change it to 42... self.0 = NonZeroU64::new(u64::from_le_bytes(key)) .unwrap_or_else(|| NonZeroU64::new(42).unwrap()); } } /// A hash builder for `StraightHasher`. #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Default)] pub struct StraightHasherBuilder; impl BuildHasher for StraightHasherBuilder { type Hasher = StraightHasher; #[inline(always)] fn build_hasher(&self) -> Self::Hasher { StraightHasher(NonZeroU64::new(42).unwrap()) } } /// Create an instance of the default hasher. #[inline(always)] pub fn get_hasher() -> impl Hasher { let s: ahash::AHasher = Default::default(); s } /// _(INTERNALS)_ Calculate a [`NonZeroU64`] hash key from a namespace-qualified function name and /// parameter types. /// Exported under the `internals` feature only. /// /// Module names are passed in via `&str` references from an iterator. /// Parameter types are passed in via [`TypeId`] values from an iterator. /// /// # Note /// /// The first module name is skipped. Hashing starts from the _second_ module in the chain. #[inline(always)] pub fn calc_native_fn_hash<'a>( modules: impl Iterator, fn_name: &str, params: impl Iterator, ) -> Option { calc_fn_hash(modules, fn_name, None, params) } /// _(INTERNALS)_ Calculate a [`NonZeroU64`] hash key from a namespace-qualified function name /// and the number of parameters, but no parameter types. /// Exported under the `internals` feature only. /// /// Module names are passed in via `&str` references from an iterator. /// Parameter types are passed in via [`TypeId`] values from an iterator. /// /// # Note /// /// The first module name is skipped. Hashing starts from the _second_ module in the chain. #[inline(always)] pub fn calc_script_fn_hash<'a>( modules: impl Iterator, fn_name: &str, num: usize, ) -> Option { calc_fn_hash(modules, fn_name, Some(num), empty()) } /// Calculate a [`NonZeroU64`] hash key from a namespace-qualified function name and parameter types. /// /// Module names are passed in via `&str` references from an iterator. /// Parameter types are passed in via [`TypeId`] values from an iterator. /// /// # Note /// /// The first module name is skipped. Hashing starts from the _second_ module in the chain. #[inline(always)] fn calc_fn_hash<'a>( mut modules: impl Iterator, fn_name: &str, num: Option, params: impl Iterator, ) -> Option { let s = &mut get_hasher(); // Hash a boolean indicating whether the hash is namespace-qualified. modules.next().is_some().hash(s); // We always skip the first module modules.for_each(|m| m.hash(s)); fn_name.hash(s); if let Some(num) = num { num.hash(s); } else { params.for_each(|t| t.hash(s)); } // HACK - If it so happens to hash directly to zero (OMG!) then change it to 42... NonZeroU64::new(s.finish()).or_else(|| NonZeroU64::new(42)) } /// Combine two [`NonZeroU64`] hashes by taking the XOR of them. #[inline(always)] pub(crate) fn combine_hashes(a: NonZeroU64, b: NonZeroU64) -> NonZeroU64 { // HACK - If it so happens to hash directly to zero (OMG!) then change it to 42... NonZeroU64::new(a.get() ^ b.get()).unwrap_or_else(|| NonZeroU64::new(42).unwrap()) } /// _(INTERNALS)_ A type that wraps a [`HashMap`] and implements [`Hash`]. /// Exported under the `internals` feature only. #[derive(Clone, Default)] pub struct HashableHashMap(HashMap); impl From> for HashableHashMap { fn from(value: HashMap) -> Self { Self(value) } } impl AsRef> for HashableHashMap { fn as_ref(&self) -> &HashMap { &self.0 } } impl AsMut> for HashableHashMap { fn as_mut(&mut self) -> &mut HashMap { &mut self.0 } } impl Deref for HashableHashMap { type Target = HashMap; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for HashableHashMap { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } impl Debug for HashableHashMap { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } impl Hash for HashableHashMap { fn hash(&self, state: &mut B) { let mut keys: Vec<_> = self.0.keys().collect(); keys.sort(); keys.into_iter().for_each(|key| { key.hash(state); self.0.get(&key).unwrap().hash(state); }); } } /// The system immutable string type. /// /// An [`ImmutableString`] wraps an [`Rc`][std::rc::Rc]`<`[`String`]`>` /// (or [`Arc`][std::sync::Arc]`<`[`String`]`>` under the `sync` feature) /// so that it can be simply shared and not cloned. /// /// # Example /// /// ``` /// use rhai::ImmutableString; /// /// let s1: ImmutableString = "hello".into(); /// /// // No actual cloning of the string is involved below. /// let s2 = s1.clone(); /// let s3 = s2.clone(); /// /// assert_eq!(s1, s2); /// /// // Clones the underlying string (because it is already shared) and extracts it. /// let mut s: String = s1.into_owned(); /// /// // Changing the clone has no impact on the previously shared version. /// s.push_str(", world!"); /// /// // The old version still exists. /// assert_eq!(s2, s3); /// assert_eq!(s2.as_str(), "hello"); /// /// // Not equals! /// assert_ne!(s2.as_str(), s.as_str()); /// assert_eq!(s, "hello, world!"); /// ``` #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)] pub struct ImmutableString(Shared); impl Deref for ImmutableString { type Target = String; #[inline(always)] fn deref(&self) -> &Self::Target { &self.0 } } impl AsRef for ImmutableString { #[inline(always)] fn as_ref(&self) -> &String { &self.0 } } impl Borrow for ImmutableString { #[inline(always)] fn borrow(&self) -> &String { &self.0 } } impl Borrow for ImmutableString { #[inline(always)] fn borrow(&self) -> &str { self.0.as_str() } } impl From<&str> for ImmutableString { #[inline(always)] fn from(value: &str) -> Self { Self(value.to_string().into()) } } impl From<&String> for ImmutableString { #[inline(always)] fn from(value: &String) -> Self { Self(value.to_string().into()) } } impl From for ImmutableString { #[inline(always)] fn from(value: String) -> Self { Self(value.into()) } } impl From> for ImmutableString { #[inline(always)] fn from(value: Box) -> Self { Self(value.into()) } } impl From for String { #[inline(always)] fn from(value: ImmutableString) -> Self { value.into_owned() } } impl FromStr for ImmutableString { type Err = (); #[inline(always)] fn from_str(s: &str) -> Result { Ok(Self(s.to_string().into())) } } impl FromIterator for ImmutableString { #[inline(always)] fn from_iter>(iter: T) -> Self { Self(iter.into_iter().collect::().into()) } } impl<'a> FromIterator<&'a char> for ImmutableString { #[inline(always)] fn from_iter>(iter: T) -> Self { Self(iter.into_iter().cloned().collect::().into()) } } impl<'a> FromIterator<&'a str> for ImmutableString { #[inline(always)] fn from_iter>(iter: T) -> Self { Self(iter.into_iter().collect::().into()) } } impl<'a> FromIterator for ImmutableString { #[inline(always)] fn from_iter>(iter: T) -> Self { Self(iter.into_iter().collect::().into()) } } impl Display for ImmutableString { #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Display::fmt(self.0.as_str(), f) } } impl Debug for ImmutableString { #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Debug::fmt(self.0.as_str(), f) } } impl Add for ImmutableString { type Output = Self; #[inline] fn add(mut self, rhs: Self) -> Self::Output { if rhs.is_empty() { self } else if self.is_empty() { rhs } else { self.make_mut().push_str(rhs.0.as_str()); self } } } impl Add for &ImmutableString { type Output = ImmutableString; #[inline] fn add(self, rhs: Self) -> Self::Output { if rhs.is_empty() { self.clone() } else if self.is_empty() { rhs.clone() } else { let mut s = self.clone(); s.make_mut().push_str(rhs.0.as_str()); s } } } impl AddAssign<&ImmutableString> for ImmutableString { #[inline] fn add_assign(&mut self, rhs: &ImmutableString) { if !rhs.is_empty() { if self.is_empty() { self.0 = rhs.0.clone(); } else { self.make_mut().push_str(rhs.0.as_str()); } } } } impl AddAssign for ImmutableString { #[inline] fn add_assign(&mut self, rhs: ImmutableString) { if !rhs.is_empty() { if self.is_empty() { self.0 = rhs.0; } else { self.make_mut().push_str(rhs.0.as_str()); } } } } impl Add<&str> for ImmutableString { type Output = Self; #[inline] fn add(mut self, rhs: &str) -> Self::Output { if rhs.is_empty() { self } else { self.make_mut().push_str(rhs); self } } } impl Add<&str> for &ImmutableString { type Output = ImmutableString; #[inline] fn add(self, rhs: &str) -> Self::Output { if rhs.is_empty() { self.clone() } else { let mut s = self.clone(); s.make_mut().push_str(rhs); s } } } impl AddAssign<&str> for ImmutableString { #[inline(always)] fn add_assign(&mut self, rhs: &str) { if !rhs.is_empty() { self.make_mut().push_str(rhs); } } } impl Add for ImmutableString { type Output = Self; #[inline] fn add(mut self, rhs: String) -> Self::Output { if rhs.is_empty() { self } else if self.is_empty() { rhs.into() } else { self.make_mut().push_str(&rhs); self } } } impl Add for &ImmutableString { type Output = ImmutableString; #[inline] fn add(self, rhs: String) -> Self::Output { if rhs.is_empty() { self.clone() } else if self.is_empty() { rhs.into() } else { let mut s = self.clone(); s.make_mut().push_str(&rhs); s } } } impl Add for ImmutableString { type Output = Self; #[inline(always)] fn add(mut self, rhs: char) -> Self::Output { self.make_mut().push(rhs); self } } impl Add for &ImmutableString { type Output = ImmutableString; #[inline(always)] fn add(self, rhs: char) -> Self::Output { let mut s = self.clone(); s.make_mut().push(rhs); s } } impl AddAssign for ImmutableString { #[inline(always)] fn add_assign(&mut self, rhs: char) { self.make_mut().push(rhs); } } impl> PartialEq for ImmutableString { #[inline(always)] fn eq(&self, other: &S) -> bool { self.as_str().eq(other.as_ref()) } } impl PartialEq for str { #[inline(always)] fn eq(&self, other: &ImmutableString) -> bool { self.eq(other.as_str()) } } impl PartialEq for String { #[inline(always)] fn eq(&self, other: &ImmutableString) -> bool { self.eq(other.as_str()) } } impl> PartialOrd for ImmutableString { #[inline(always)] fn partial_cmp(&self, other: &S) -> Option { self.as_str().partial_cmp(other.as_ref()) } } impl PartialOrd for str { #[inline(always)] fn partial_cmp(&self, other: &ImmutableString) -> Option { self.partial_cmp(other.as_str()) } } impl PartialOrd for String { #[inline(always)] fn partial_cmp(&self, other: &ImmutableString) -> Option { self.as_str().partial_cmp(other.as_str()) } } impl ImmutableString { /// Consume the [`ImmutableString`] and convert it into a [`String`]. /// If there are other references to the same string, a cloned copy is returned. #[inline(always)] pub fn into_owned(mut self) -> String { self.make_mut(); // Make sure it is unique reference shared_take(self.0) // Should succeed } /// Make sure that the [`ImmutableString`] is unique (i.e. no other outstanding references). /// Then return a mutable reference to the [`String`]. #[inline(always)] pub fn make_mut(&mut self) -> &mut String { shared_make_mut(&mut self.0) } }