diff --git a/src/api/definitions/mod.rs b/src/api/definitions/mod.rs index 5bbd42ac..171ca866 100644 --- a/src/api/definitions/mod.rs +++ b/src/api/definitions/mod.rs @@ -424,11 +424,10 @@ impl Module { } let mut func_infos = self.iter_fn().collect::>(); - func_infos.sort_by(|a, b| match a.metadata.name.cmp(&b.metadata.name) { - Ordering::Equal => match a.metadata.params.cmp(&b.metadata.params) { - Ordering::Equal => (a.metadata.params_info.join("") - + a.metadata.return_type.as_str()) - .cmp(&(b.metadata.params_info.join("") + b.metadata.return_type.as_str())), + func_infos.sort_by(|a, b| match a.name.cmp(&b.name) { + Ordering::Equal => match a.num_params.cmp(&b.num_params) { + Ordering::Equal => (a.params_info.join("") + a.return_type.as_str()) + .cmp(&(b.params_info.join("") + b.return_type.as_str())), o => o, }, o => o, @@ -440,17 +439,16 @@ impl Module { } first = false; - if f.metadata.access == FnAccess::Private { + if f.access == FnAccess::Private { continue; } #[cfg(not(feature = "no_custom_syntax"))] - let operator = def.engine.custom_keywords.contains_key(&f.metadata.name) - || (!f.metadata.name.contains('$') && !is_valid_function_name(&f.metadata.name)); + let operator = def.engine.custom_keywords.contains_key(&f.name) + || (!f.name.contains('$') && !is_valid_function_name(&f.name)); #[cfg(feature = "no_custom_syntax")] - let operator = - !f.metadata.name.contains('$') && !is_valid_function_name(&f.metadata.name); + let operator = !f.name.contains('$') && !is_valid_function_name(&f.name); f.write_definition(writer, def, operator)?; } @@ -467,7 +465,7 @@ impl FuncInfo { def: &Definitions, operator: bool, ) -> fmt::Result { - for comment in &*self.metadata.comments { + for comment in &*self.comments { writeln!(writer, "{comment}")?; } @@ -477,33 +475,29 @@ impl FuncInfo { writer.write_str("fn ")?; } - if let Some(name) = self.metadata.name.strip_prefix("get$") { + if let Some(name) = self.name.strip_prefix("get$") { write!(writer, "get {name}(")?; - } else if let Some(name) = self.metadata.name.strip_prefix("set$") { + } else if let Some(name) = self.name.strip_prefix("set$") { write!(writer, "set {name}(")?; } else { - write!(writer, "{}(", self.metadata.name)?; + write!(writer, "{}(", self.name)?; } let mut first = true; - for i in 0..self.metadata.params { + for i in 0..self.num_params { if !first { writer.write_str(", ")?; } first = false; - let (param_name, param_type) = - self.metadata - .params_info - .get(i) - .map_or(("_", "?".into()), |s| { - let mut s = s.splitn(2, ':'); - ( - s.next().unwrap_or("_").split(' ').last().unwrap(), - s.next() - .map_or(Cow::Borrowed("?"), |ty| def_type_name(ty, def.engine)), - ) - }); + let (param_name, param_type) = self.params_info.get(i).map_or(("_", "?".into()), |s| { + let mut s = s.splitn(2, ':'); + ( + s.next().unwrap_or("_").split(' ').last().unwrap(), + s.next() + .map_or(Cow::Borrowed("?"), |ty| def_type_name(ty, def.engine)), + ) + }); if operator { write!(writer, "{param_type}")?; @@ -515,7 +509,7 @@ impl FuncInfo { write!( writer, ") -> {};", - def_type_name(&self.metadata.return_type, def.engine) + def_type_name(&self.return_type, def.engine) )?; Ok(()) diff --git a/src/func/call.rs b/src/func/call.rs index dc76eb4d..fdd6777b 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -239,21 +239,21 @@ impl Engine { // Check `Dynamic` parameters for functions with parameters if allow_dynamic && max_bitmask == 0 && num_args > 0 { - let is_dynamic = lib.iter().any(|&m| m.contains_dynamic_fn(hash_base)) + let is_dynamic = lib.iter().any(|&m| m.may_contain_dynamic_fn(hash_base)) || self .global_modules .iter() - .any(|m| m.contains_dynamic_fn(hash_base)); + .any(|m| m.may_contain_dynamic_fn(hash_base)); #[cfg(not(feature = "no_module"))] let is_dynamic = is_dynamic || _global .iter_imports_raw() - .any(|(_, m)| m.contains_dynamic_fn(hash_base)) + .any(|(_, m)| m.may_contain_dynamic_fn(hash_base)) || self .global_sub_modules .values() - .any(|m| m.contains_dynamic_fn(hash_base)); + .any(|m| m.may_contain_dynamic_fn(hash_base)); // Set maximum bitmask when there are dynamic versions of the function if is_dynamic { diff --git a/src/func/hashing.rs b/src/func/hashing.rs index 2a5f5dcc..b06b08e6 100644 --- a/src/func/hashing.rs +++ b/src/func/hashing.rs @@ -9,13 +9,9 @@ use std::{ #[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. /// diff --git a/src/func/mod.rs b/src/func/mod.rs index 51eb1e1c..d9b9db8c 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, StraightHashMap, StraightHashSet, + combine_hashes, get_hasher, StraightHashMap, }; 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 d97e8ec1..457402ad 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -7,7 +7,7 @@ use crate::func::{ shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, RegisterNativeFunction, SendSync, }; -use crate::types::{dynamic::Variant, CustomTypesCollection}; +use crate::types::{dynamic::Variant, BloomFilterU64, CustomTypesCollection}; use crate::{ calc_fn_hash, calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes, Dynamic, Identifier, ImmutableString, NativeCallContext, RhaiResultOf, Shared, SmartString, StaticVec, @@ -16,7 +16,6 @@ use crate::{ use std::prelude::v1::*; use std::{ any::TypeId, - cmp::Ordering, collections::{BTreeMap, BTreeSet}, fmt, ops::{Add, AddAssign}, @@ -25,7 +24,7 @@ use std::{ #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] use crate::func::register::Mut; -use crate::func::{StraightHashMap, StraightHashSet}; +use crate::func::StraightHashMap; /// A type representing the namespace of a function. #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] @@ -62,10 +61,12 @@ impl FnNamespace { } } -/// A type containing all metadata for a registered function. -#[derive(Debug, Clone, Eq, PartialEq, Hash)] +/// A type containing a single registered function. +#[derive(Debug, Clone)] #[non_exhaustive] -pub struct FnMetadata { +pub struct FuncInfo { + /// Function instance. + pub func: CallableFunction, /// Function namespace. pub namespace: FnNamespace, /// Function access mode. @@ -73,7 +74,9 @@ pub struct FnMetadata { /// Function name. pub name: Identifier, /// Number of parameters. - pub params: usize, + pub num_params: usize, + /// Parameter types (if applicable). + pub param_types: StaticVec, /// Parameter names and types (if available). #[cfg(feature = "metadata")] pub params_info: StaticVec, @@ -85,58 +88,25 @@ pub struct FnMetadata { pub comments: Box<[Box]>, } -impl PartialOrd for FnMetadata { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for FnMetadata { - fn cmp(&self, other: &Self) -> Ordering { - match self.name.cmp(&other.name) { - #[cfg(feature = "metadata")] - Ordering::Equal => match self.params.cmp(&other.params) { - Ordering::Equal => self.params_info.cmp(&other.params_info), - cmp => cmp, - }, - #[cfg(not(feature = "metadata"))] - Ordering::Equal => self.params.cmp(&other.params), - cmp => cmp, - } - } -} - -/// A type containing a single registered function. -#[derive(Debug, Clone)] -pub struct FuncInfo { - /// Function instance. - pub func: CallableFunction, - /// Parameter types (if applicable). - pub param_types: StaticVec, - /// Function metadata. - pub metadata: FnMetadata, -} - impl FuncInfo { /// _(metadata)_ Generate a signature of the function. /// Exported under the `metadata` feature only. #[cfg(feature = "metadata")] #[must_use] pub fn gen_signature(&self) -> String { - let mut sig = format!("{}(", self.metadata.name); + let mut sig = format!("{}(", self.name); - let return_type = format_type(&self.metadata.return_type, true); + let return_type = format_type(&self.return_type, true); - if self.metadata.params_info.is_empty() { - for x in 0..self.metadata.params { + if self.params_info.is_empty() { + for x in 0..self.num_params { sig.push('_'); - if x < self.metadata.params - 1 { + if x < self.num_params - 1 { sig.push_str(", "); } } } else { let params: StaticVec<_> = self - .metadata .params_info .iter() .map(|s| { @@ -213,7 +183,7 @@ pub struct Module { /// including those in sub-modules. all_functions: StraightHashMap, /// Native Rust functions (in scripted hash format) that contain [`Dynamic`] parameters. - dynamic_functions: StraightHashSet, + dynamic_functions: BloomFilterU64, /// Iterator functions, keyed by the type producing the iterator. type_iterators: BTreeMap>, /// Flattened collection of iterator functions, including those in sub-modules. @@ -314,7 +284,7 @@ impl Module { all_variables: StraightHashMap::default(), functions: StraightHashMap::default(), all_functions: StraightHashMap::default(), - dynamic_functions: StraightHashSet::default(), + dynamic_functions: BloomFilterU64::new(), type_iterators: BTreeMap::new(), all_type_iterators: BTreeMap::new(), indexed: true, @@ -587,7 +557,7 @@ impl Module { #[inline] pub fn gen_fn_signatures(&self) -> impl Iterator + '_ { self.iter_fn() - .filter(|&f| match f.metadata.access { + .filter(|&f| match f.access { FnAccess::Public => true, FnAccess::Private => false, }) @@ -706,20 +676,18 @@ impl Module { self.functions.insert( hash_script, FuncInfo { - metadata: FnMetadata { - name: fn_def.name.as_str().into(), - namespace: FnNamespace::Internal, - access: fn_def.access, - params: num_params, - #[cfg(feature = "metadata")] - params_info, - #[cfg(feature = "metadata")] - return_type: "".into(), - #[cfg(feature = "metadata")] - comments: Box::default(), - }, - func: fn_def.into(), + name: fn_def.name.as_str().into(), + namespace: FnNamespace::Internal, + access: fn_def.access, + num_params, param_types: StaticVec::new_const(), + #[cfg(feature = "metadata")] + params_info, + #[cfg(feature = "metadata")] + return_type: "".into(), + #[cfg(feature = "metadata")] + comments: Box::default(), + func: fn_def.into(), } .into(), ); @@ -744,7 +712,7 @@ impl Module { let name = name.as_ref(); self.iter_fn() - .find(|f| f.metadata.params == num_params && f.metadata.name == name) + .find(|f| f.num_params == num_params && f.name == name) .and_then(|f| f.func.get_script_fn_def()) } } @@ -886,14 +854,14 @@ impl Module { .collect(); if let Some(f) = self.functions.get_mut(&hash_fn) { - let (param_names, return_type_name) = if param_names.len() > f.metadata.params { + let (param_names, return_type_name) = if param_names.len() > f.num_params { let return_type = param_names.pop().unwrap(); (param_names, return_type) } else { (param_names, crate::SmartString::new_const()) }; - f.metadata.params_info = param_names; - f.metadata.return_type = return_type_name; + f.params_info = param_names; + f.return_type = return_type_name; } self @@ -935,7 +903,7 @@ impl Module { if !comments.is_empty() { let f = self.functions.get_mut(&hash_fn).unwrap(); - f.metadata.comments = comments.iter().map(|s| s.as_ref().into()).collect(); + f.comments = comments.iter().map(|s| s.as_ref().into()).collect(); } self @@ -947,7 +915,7 @@ impl Module { #[inline] pub fn update_fn_namespace(&mut self, hash_fn: u64, namespace: FnNamespace) -> &mut Self { if let Some(f) = self.functions.get_mut(&hash_fn) { - f.metadata.namespace = namespace; + f.namespace = namespace; self.indexed = false; self.contains_indexed_global_functions = false; } @@ -1036,26 +1004,24 @@ impl Module { if is_dynamic { self.dynamic_functions - .insert(calc_fn_hash(name, param_types.len())); + .mark(calc_fn_hash(name, param_types.len())); } self.functions.insert( hash_fn, FuncInfo { - metadata: FnMetadata { - name: name.into(), - namespace, - access, - params: param_types.len(), - #[cfg(feature = "metadata")] - params_info: param_names, - #[cfg(feature = "metadata")] - return_type: return_type_name, - #[cfg(feature = "metadata")] - comments: Box::default(), - }, func, + name: name.into(), + namespace, + access, + num_params: param_types.len(), param_types, + #[cfg(feature = "metadata")] + params_info: param_names, + #[cfg(feature = "metadata")] + return_type: return_type_name, + #[cfg(feature = "metadata")] + comments: Box::default(), } .into(), ); @@ -1110,7 +1076,7 @@ impl Module { if !comments.is_empty() { let f = self.functions.get_mut(&hash).unwrap(); - f.metadata.comments = comments.iter().map(|s| s.as_ref().into()).collect(); + f.comments = comments.iter().map(|s| s.as_ref().into()).collect(); } hash @@ -1554,15 +1520,13 @@ impl Module { } } - /// Does the particular function with [`Dynamic`] parameter(s) exist in the [`Module`]? + /// Can the particular function with [`Dynamic`] parameter(s) exist in the [`Module`]? + /// + /// A `true` return value does not automatically imply that the function _must_ exist. #[inline(always)] #[must_use] - pub(crate) fn contains_dynamic_fn(&self, hash_script: u64) -> bool { - if self.dynamic_functions.is_empty() { - false - } else { - self.dynamic_functions.contains(&hash_script) - } + pub(crate) fn may_contain_dynamic_fn(&self, hash_script: u64) -> bool { + !self.dynamic_functions.is_absent(hash_script) } /// Does the particular namespace-qualified function exist in the [`Module`]? @@ -1599,8 +1563,7 @@ impl Module { self.modules.extend(other.modules.into_iter()); self.variables.extend(other.variables.into_iter()); self.functions.extend(other.functions.into_iter()); - self.dynamic_functions - .extend(other.dynamic_functions.into_iter()); + self.dynamic_functions += &other.dynamic_functions; self.type_iterators.extend(other.type_iterators.into_iter()); self.all_functions.clear(); self.all_variables.clear(); @@ -1629,8 +1592,7 @@ impl Module { } self.variables.extend(other.variables.into_iter()); self.functions.extend(other.functions.into_iter()); - self.dynamic_functions - .extend(other.dynamic_functions.into_iter()); + self.dynamic_functions += &other.dynamic_functions; self.type_iterators.extend(other.type_iterators.into_iter()); self.all_functions.clear(); self.all_variables.clear(); @@ -1666,8 +1628,7 @@ impl Module { for (&k, v) in &other.functions { self.functions.entry(k).or_insert_with(|| v.clone()); } - self.dynamic_functions - .extend(other.dynamic_functions.iter().copied()); + self.dynamic_functions += &other.dynamic_functions; for (&k, v) in &other.type_iterators { self.type_iterators.entry(k).or_insert_with(|| v.clone()); } @@ -1718,18 +1679,16 @@ impl Module { .iter() .filter(|&(.., f)| { _filter( - f.metadata.namespace, - f.metadata.access, + f.namespace, + f.access, f.func.is_script(), - f.metadata.name.as_str(), - f.metadata.params, + f.name.as_str(), + f.num_params, ) }) .map(|(&k, v)| (k, v.clone())), ); - // This may introduce entries that are superfluous because the function has been filtered away. - self.dynamic_functions - .extend(other.dynamic_functions.iter().copied()); + self.dynamic_functions += &other.dynamic_functions; self.type_iterators .extend(other.type_iterators.iter().map(|(&k, v)| (k, v.clone()))); @@ -1761,12 +1720,7 @@ impl Module { .into_iter() .filter(|(.., f)| { if f.func.is_script() { - filter( - f.metadata.namespace, - f.metadata.access, - f.metadata.name.as_str(), - f.metadata.params, - ) + filter(f.namespace, f.access, f.name.as_str(), f.num_params) } else { false } @@ -1835,10 +1789,10 @@ impl Module { > + '_ { self.iter_fn().filter(|&f| f.func.is_script()).map(|f| { ( - f.metadata.namespace, - f.metadata.access, - f.metadata.name.as_str(), - f.metadata.params, + f.namespace, + f.access, + f.name.as_str(), + f.num_params, f.func.get_script_fn_def().expect("script-defined function"), ) }) @@ -1857,14 +1811,9 @@ impl Module { pub fn iter_script_fn_info( &self, ) -> impl Iterator { - self.iter_fn().filter(|&f| f.func.is_script()).map(|f| { - ( - f.metadata.namespace, - f.metadata.access, - f.metadata.name.as_str(), - f.metadata.params, - ) - }) + self.iter_fn() + .filter(|&f| f.func.is_script()) + .map(|f| (f.namespace, f.access, f.name.as_str(), f.params)) } /// _(internals)_ Get an iterator over all script-defined functions in the [`Module`]. @@ -2026,7 +1975,7 @@ impl Module { ast.shared_lib() .iter_fn() - .filter(|&f| match f.metadata.access { + .filter(|&f| match f.access { FnAccess::Public => true, FnAccess::Private => false, }) @@ -2105,7 +2054,7 @@ impl Module { // Index all Rust functions for (&hash, f) in &module.functions { - match f.metadata.namespace { + match f.namespace { FnNamespace::Global => { // Flatten all functions with global namespace functions.insert(hash, f.func.clone()); @@ -2113,24 +2062,18 @@ impl Module { } FnNamespace::Internal => (), } - match f.metadata.access { + match f.access { FnAccess::Public => (), FnAccess::Private => continue, // Do not index private functions } if !f.func.is_script() { - let hash_qualified_fn = calc_native_fn_hash( - path.iter().copied(), - f.metadata.name.as_str(), - &f.param_types, - ); + let hash_qualified_fn = + calc_native_fn_hash(path.iter().copied(), f.name.as_str(), &f.param_types); functions.insert(hash_qualified_fn, f.func.clone()); } else if cfg!(not(feature = "no_function")) { - let hash_qualified_script = crate::calc_qualified_fn_hash( - path.iter().copied(), - f.metadata.name.as_str(), - f.metadata.params, - ); + let hash_qualified_script = + crate::calc_qualified_fn_hash(path.iter().copied(), &f.name, f.num_params); functions.insert(hash_qualified_script, f.func.clone()); } } diff --git a/src/serde/metadata.rs b/src/serde/metadata.rs index b693c858..47b9d1f0 100644 --- a/src/serde/metadata.rs +++ b/src/serde/metadata.rs @@ -66,13 +66,13 @@ impl Ord for FnMetadata<'_> { impl<'a> From<&'a FuncInfo> for FnMetadata<'a> { fn from(info: &'a FuncInfo) -> Self { - let base_hash = calc_fn_hash(&info.metadata.name, info.metadata.params); + let base_hash = calc_fn_hash(&info.name, info.num_params); let (typ, full_hash) = if info.func.is_script() { (FnType::Script, base_hash) } else { ( FnType::Native, - calc_native_fn_hash(None, &info.metadata.name, &info.param_types), + calc_native_fn_hash(None, &info.name, &info.param_types), ) }; @@ -80,13 +80,12 @@ impl<'a> From<&'a FuncInfo> for FnMetadata<'a> { base_hash, full_hash, #[cfg(not(feature = "no_module"))] - namespace: info.metadata.namespace, - access: info.metadata.access, - name: &info.metadata.name, + namespace: info.namespace, + access: info.access, + name: &info.name, typ, - num_params: info.metadata.params, + num_params: info.num_params, params: info - .metadata .params_info .iter() .map(|s| { @@ -100,7 +99,7 @@ impl<'a> From<&'a FuncInfo> for FnMetadata<'a> { }) .collect(), _dummy: None, - return_type: format_type(&info.metadata.return_type, true), + return_type: format_type(&info.return_type, true), signature: info.gen_signature().into(), doc_comments: if info.func.is_script() { #[cfg(feature = "no_function")] @@ -115,7 +114,7 @@ impl<'a> From<&'a FuncInfo> for FnMetadata<'a> { .map(<_>::as_ref) .collect() } else { - info.metadata.comments.iter().map(<_>::as_ref).collect() + info.comments.iter().map(<_>::as_ref).collect() }, } } diff --git a/src/types/bloom_filter.rs b/src/types/bloom_filter.rs new file mode 100644 index 00000000..a722e2ee --- /dev/null +++ b/src/types/bloom_filter.rs @@ -0,0 +1,105 @@ +//! A simple bloom filter implementation for `u64` hash values only. + +#[cfg(feature = "no_std")] +use std::prelude::v1::*; +use std::{ + mem, + ops::{Add, AddAssign}, +}; + +/// Number of `usize` values required for 256 bits. +const SIZE: usize = (256 / 8) / mem::size_of::(); + +/// A simple bloom filter implementation for `u64` hash values only - i.e., all 64 bits are assumed +/// to be relatively random. +/// +/// For this reason, the implementation is simplistic - it just looks at the least significant byte +/// of the `u64` hash value and sets the corresponding bit in a 256-long bit vector. +/// +/// The rationale of this type is to avoid pulling in another dependent crate. +#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)] +pub struct BloomFilterU64([usize; SIZE]); + +impl BloomFilterU64 { + /// Get the bit position of a `u64` hash value. + #[inline(always)] + const fn hash(value: u64) -> (usize, usize) { + let hash = (value & 0x00ff) as usize; + (hash / 64, 0x01 << (hash % 64)) + } + /// Create a new [`BloomFilterU64`]. + #[inline(always)] + #[must_use] + pub const fn new() -> Self { + Self([0; SIZE]) + } + /// Is this [`BloomFilterU64`] empty? + #[inline(always)] + #[must_use] + pub fn is_empty(&self) -> bool { + self.0.iter().all(|&v| v == 0) + } + /// Clear this [`BloomFilterU64`]. + #[inline(always)] + pub fn clear(&mut self) -> &mut Self { + self.0 = [0; SIZE]; + self + } + /// Mark a `u64` hash into this [`BloomFilterU64`]. + #[inline(always)] + pub fn mark(&mut self, hash: u64) -> &mut Self { + let (offset, mask) = Self::hash(hash); + self.0[offset] |= mask; + self + } + /// Is a `u64` hash definitely absent from this [`BloomFilterU64`]? + #[inline] + pub const fn is_absent(&self, hash: u64) -> bool { + let (offset, mask) = Self::hash(hash); + (self.0[offset] & mask) == 0 + } +} + +impl Add for &BloomFilterU64 { + type Output = BloomFilterU64; + + #[inline] + fn add(self, rhs: Self) -> Self::Output { + let mut buf = [0; SIZE]; + + self.0 + .iter() + .zip(rhs.0.iter()) + .map(|(&a, &b)| a | b) + .zip(buf.iter_mut()) + .for_each(|(v, x)| *x = v); + + BloomFilterU64(buf) + } +} + +impl Add for &BloomFilterU64 { + type Output = BloomFilterU64; + + #[inline(always)] + fn add(self, rhs: BloomFilterU64) -> Self::Output { + self + &rhs + } +} + +impl AddAssign for BloomFilterU64 { + #[inline(always)] + fn add_assign(&mut self, rhs: Self) { + *self += &rhs + } +} + +impl AddAssign<&Self> for BloomFilterU64 { + #[inline] + fn add_assign(&mut self, rhs: &Self) { + self.0 + .iter_mut() + .zip(rhs.0.iter()) + .for_each(|(x, &v)| *x |= v); + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index 6e2843e2..347bc85c 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -1,5 +1,6 @@ //! Module defining Rhai data types. +pub mod bloom_filter; pub mod custom_types; pub mod dynamic; pub mod error; @@ -9,6 +10,7 @@ pub mod interner; pub mod parse_error; pub mod scope; +pub use bloom_filter::BloomFilterU64; pub use custom_types::{CustomTypeInfo, CustomTypesCollection}; pub use dynamic::Dynamic; #[cfg(not(feature = "no_std"))]