diff --git a/Cargo.toml b/Cargo.toml index 54331e51..80ab0cf2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ rhai_codegen = { version = "1.4.1", path = "codegen", default-features = false } no-std-compat = { version = "0.4", default-features = false, features = ["alloc"], optional = true } libm = { version = "0.2", default-features = false, optional = true } +hashbrown = { version = "0.12", optional = true } core-error = { version = "0.0", default-features = false, features = ["alloc"], optional = true } serde = { version = "1.0", default-features = false, features = ["derive", "alloc"], optional = true } serde_json = { version = "1.0", default-features = false, features = ["alloc"], optional = true } @@ -63,7 +64,7 @@ debugging = ["internals"] # enable debugging serde = ["dep:serde", "smartstring/serde", "smallvec/serde"] # implement serde for rhai types # compiling for no-std -no_std = ["no-std-compat", "num-traits/libm", "core-error", "libm", "ahash/compile-time-rng"] +no_std = ["no-std-compat", "num-traits/libm", "core-error", "libm", "ahash/compile-time-rng", "hashbrown"] # compiling for WASM wasm-bindgen = ["instant/wasm-bindgen"] diff --git a/src/eval/cache.rs b/src/eval/cache.rs index 492550cf..72be605b 100644 --- a/src/eval/cache.rs +++ b/src/eval/cache.rs @@ -1,10 +1,10 @@ //! System caches. -use crate::func::CallableFunction; +use crate::func::{CallableFunction, StraightHashMap}; use crate::{Identifier, StaticVec}; +use std::marker::PhantomData; #[cfg(feature = "no_std")] use std::prelude::v1::*; -use std::{collections::BTreeMap, marker::PhantomData}; /// _(internals)_ An entry in a function resolution cache. /// Exported under the `internals` feature only. @@ -21,7 +21,7 @@ pub struct FnResolutionCacheEntry { /// /// [`FnResolutionCacheEntry`] is [`Box`]ed in order to pack as many entries inside a single B-Tree /// level as possible. -pub type FnResolutionCache = BTreeMap>; +pub type FnResolutionCache = StraightHashMap>; /// _(internals)_ A type containing system-wide caches. /// Exported under the `internals` feature only. @@ -66,7 +66,7 @@ impl Caches<'_> { #[allow(dead_code)] #[inline(always)] pub fn push_fn_resolution_cache(&mut self) { - self.fn_resolution.push(BTreeMap::new()); + self.fn_resolution.push(StraightHashMap::default()); } /// Rewind the function resolution caches stack to a particular size. #[inline(always)] diff --git a/src/eval/expr.rs b/src/eval/expr.rs index 82f5745a..a0411410 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -11,8 +11,12 @@ use crate::func::{ use crate::types::dynamic::AccessMode; use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR}; #[cfg(feature = "no_std")] +use hashbrown::hash_map::Entry; +#[cfg(not(feature = "no_std"))] +use std::collections::hash_map::Entry; +use std::num::NonZeroUsize; +#[cfg(feature = "no_std")] use std::prelude::v1::*; -use std::{collections::btree_map::Entry, num::NonZeroUsize}; impl Engine { /// Search for a module within an imports stack. diff --git a/src/func/hashing.rs b/src/func/hashing.rs index 108b1035..75331f66 100644 --- a/src/func/hashing.rs +++ b/src/func/hashing.rs @@ -7,6 +7,16 @@ use std::{ hash::{BuildHasher, Hash, Hasher}, }; +#[cfg(feature = "no_std")] +pub type StraightHashMap = hashbrown::HashMap; +#[cfg(feature = "no_std")] +pub type StraightHashSet = hashbrown::HashSet; + +#[cfg(not(feature = "no_std"))] +pub type StraightHashMap = std::collections::HashMap; +#[cfg(not(feature = "no_std"))] +pub type StraightHashSet = std::collections::HashSet; + /// Dummy hash value to map zeros to. This value can be anything. /// /// # Notes @@ -30,7 +40,7 @@ pub const ALT_ZERO_HASH: u64 = 42; /// /// Panics when hashing any data type other than a [`u64`]. #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] -struct StraightHasher(u64); +pub struct StraightHasher(u64); impl Hasher for StraightHasher { #[inline(always)] @@ -38,23 +48,22 @@ impl Hasher for StraightHasher { self.0 } #[inline] - fn write(&mut self, bytes: &[u8]) { - assert_eq!(bytes.len(), 8, "StraightHasher can only hash u64 values"); + fn write(&mut self, _bytes: &[u8]) { + panic!("StraightHasher can only hash u64 values"); + } - let mut key = [0_u8; 8]; - key.copy_from_slice(bytes); - - self.0 = u64::from_ne_bytes(key); - - if self.0 == 0 { + fn write_u64(&mut self, i: u64) { + if i == 0 { self.0 = ALT_ZERO_HASH; + } else { + self.0 = i; } } } /// A hash builder for `StraightHasher`. #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Default)] -struct StraightHasherBuilder; +pub struct StraightHasherBuilder; impl BuildHasher for StraightHasherBuilder { type Hasher = StraightHasher; diff --git a/src/func/mod.rs b/src/func/mod.rs index d3887638..51eb1e1c 100644 --- a/src/func/mod.rs +++ b/src/func/mod.rs @@ -21,7 +21,7 @@ pub use callable_function::CallableFunction; pub use func::Func; pub use hashing::{ calc_fn_hash, calc_fn_params_hash, calc_qualified_fn_hash, calc_qualified_var_hash, - combine_hashes, get_hasher, + combine_hashes, get_hasher, StraightHashMap, StraightHashSet, }; pub use native::{ locked_read, locked_write, shared_get_mut, shared_make_mut, shared_take, shared_take_or_clone, diff --git a/src/module/mod.rs b/src/module/mod.rs index 672e2cf6..51e1d0ea 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -23,6 +23,8 @@ use std::{ #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] use crate::func::register::Mut; +use crate::func::{StraightHashMap, StraightHashSet}; + /// A type representing the namespace of a function. #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] #[cfg_attr(feature = "metadata", derive(serde::Serialize))] @@ -267,18 +269,18 @@ pub struct Module { /// [`Module`] variables. variables: BTreeMap, /// Flattened collection of all [`Module`] variables, including those in sub-modules. - all_variables: BTreeMap, + all_variables: StraightHashMap, /// Functions (both native Rust and scripted). - functions: BTreeMap>, + functions: StraightHashMap>, /// Flattened collection of all functions, native Rust and scripted. /// including those in sub-modules. - all_functions: BTreeMap, + all_functions: StraightHashMap, /// Native Rust functions (in scripted hash format) that contain [`Dynamic`] parameters. - dynamic_functions: BTreeSet, + dynamic_functions: StraightHashSet, /// Iterator functions, keyed by the type producing the iterator. - type_iterators: BTreeMap>, + type_iterators: StraightHashMap>, /// Flattened collection of iterator functions, including those in sub-modules. - all_type_iterators: BTreeMap>, + all_type_iterators: StraightHashMap>, /// Is the [`Module`] indexed? indexed: bool, /// Does the [`Module`] contain indexed functions that have been exposed to the global namespace? @@ -372,12 +374,12 @@ impl Module { custom_types: CustomTypesCollection::new(), modules: BTreeMap::new(), variables: BTreeMap::new(), - all_variables: BTreeMap::new(), - functions: BTreeMap::new(), - all_functions: BTreeMap::new(), - dynamic_functions: BTreeSet::new(), - type_iterators: BTreeMap::new(), - all_type_iterators: BTreeMap::new(), + all_variables: StraightHashMap::default(), + functions: StraightHashMap::default(), + all_functions: StraightHashMap::default(), + dynamic_functions: StraightHashSet::default(), + type_iterators: StraightHashMap::default(), + all_type_iterators: StraightHashMap::default(), indexed: true, contains_indexed_global_functions: false, } @@ -2137,9 +2139,9 @@ impl Module { fn index_module<'a>( module: &'a Module, path: &mut Vec<&'a str>, - variables: &mut BTreeMap, - functions: &mut BTreeMap, - type_iterators: &mut BTreeMap>, + variables: &mut StraightHashMap, + functions: &mut StraightHashMap, + type_iterators: &mut StraightHashMap>, ) -> bool { let mut contains_indexed_global_functions = false; @@ -2201,9 +2203,9 @@ impl Module { if !self.indexed { let mut path = Vec::with_capacity(4); - let mut variables = BTreeMap::new(); - let mut functions = BTreeMap::new(); - let mut type_iterators = BTreeMap::new(); + let mut variables = StraightHashMap::default(); + let mut functions = StraightHashMap::default(); + let mut type_iterators = StraightHashMap::default(); path.push(""); diff --git a/src/parser.rs b/src/parser.rs index 23da89d0..97c19f08 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -9,7 +9,7 @@ use crate::ast::{ }; use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS}; use crate::eval::GlobalRuntimeState; -use crate::func::hashing::get_hasher; +use crate::func::{hashing::get_hasher, StraightHashMap}; use crate::tokenizer::{ is_keyword_function, is_valid_function_name, is_valid_identifier, Token, TokenStream, TokenizerControl, @@ -32,7 +32,7 @@ use std::{ pub type ParseResult = Result; -type FnLib = BTreeMap>; +type FnLib = StraightHashMap>; const KEYWORD_SEMICOLON: &str = Token::SemiColon.literal_syntax(); @@ -3790,7 +3790,7 @@ impl Engine { state: &mut ParseState, _optimization_level: OptimizationLevel, ) -> ParseResult { - let mut functions = BTreeMap::new(); + let mut functions = StraightHashMap::default(); let mut options = self.options; options.remove(LangOptions::STMT_EXPR); @@ -3850,7 +3850,7 @@ impl Engine { state: &mut ParseState, ) -> ParseResult<(StmtBlockContainer, StaticVec>)> { let mut statements = StmtBlockContainer::new_const(); - let mut functions = BTreeMap::new(); + let mut functions = StraightHashMap::default(); while !input.peek().expect(NEVER_ENDS).0.is_eof() { let settings = ParseSettings { diff --git a/src/types/interner.rs b/src/types/interner.rs index 40df1982..2b8bada1 100644 --- a/src/types/interner.rs +++ b/src/types/interner.rs @@ -1,10 +1,9 @@ -use crate::func::hashing::get_hasher; +use crate::func::{hashing::get_hasher, StraightHashMap}; use crate::ImmutableString; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ - collections::BTreeMap, fmt, hash::{Hash, Hasher}, marker::PhantomData, @@ -21,14 +20,14 @@ pub const MAX_STRING_LEN: usize = 24; /// Exported under the `internals` feature only. /// /// Normal identifiers, property getters and setters are interned separately. -#[derive(Clone, Hash)] +#[derive(Clone)] pub struct StringsInterner<'a> { /// Maximum number of strings interned. pub capacity: usize, /// Maximum string length. pub max_string_len: usize, /// Normal strings. - strings: BTreeMap, + strings: StraightHashMap, /// Take care of the lifetime parameter. dummy: PhantomData<&'a ()>, } @@ -55,7 +54,7 @@ impl StringsInterner<'_> { Self { capacity: MAX_INTERNED_STRINGS, max_string_len: MAX_STRING_LEN, - strings: BTreeMap::new(), + strings: StraightHashMap::default(), dummy: PhantomData, } }