From ef920e2b8dafd7ab3bf2da74ec17c10bae13ac41 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 25 Nov 2022 12:14:37 +0800 Subject: [PATCH] Factor out metadata in FuncInfo. --- src/api/definitions/mod.rs | 48 ++++--- src/module/mod.rs | 253 +++++++++++++++++++++++-------------- src/serde/metadata.rs | 17 +-- 3 files changed, 196 insertions(+), 122 deletions(-) diff --git a/src/api/definitions/mod.rs b/src/api/definitions/mod.rs index e09159ef..86ce3f69 100644 --- a/src/api/definitions/mod.rs +++ b/src/api/definitions/mod.rs @@ -430,10 +430,11 @@ impl Module { } let mut func_infos = self.iter_fn().collect::>(); - 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())), + func_infos.sort_by(|a, b| match a.metadata.name.cmp(&b.metadata.name) { + Ordering::Equal => match a.metadata.num_params.cmp(&b.metadata.num_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())), o => o, }, o => o, @@ -445,8 +446,9 @@ impl Module { } first = false; - if f.access != FnAccess::Private { - let operator = !f.name.contains('$') && !is_valid_function_name(&f.name); + if f.metadata.access != FnAccess::Private { + let operator = + !f.metadata.name.contains('$') && !is_valid_function_name(&f.metadata.name); #[cfg(not(feature = "no_custom_syntax"))] let operator = operator @@ -454,7 +456,7 @@ impl Module { .engine .custom_keywords .as_ref() - .map_or(false, |m| m.contains_key(f.name.as_str())); + .map_or(false, |m| m.contains_key(f.metadata.name.as_str())); f.write_definition(writer, def, operator)?; } @@ -472,7 +474,7 @@ impl FuncInfo { def: &Definitions, operator: bool, ) -> fmt::Result { - for comment in &*self.comments { + for comment in &*self.metadata.comments { writeln!(writer, "{comment}")?; } @@ -482,29 +484,33 @@ impl FuncInfo { writer.write_str("fn ")?; } - if let Some(name) = self.name.strip_prefix("get$") { + if let Some(name) = self.metadata.name.strip_prefix("get$") { write!(writer, "get {name}(")?; - } else if let Some(name) = self.name.strip_prefix("set$") { + } else if let Some(name) = self.metadata.name.strip_prefix("set$") { write!(writer, "set {name}(")?; } else { - write!(writer, "{}(", self.name)?; + write!(writer, "{}(", self.metadata.name)?; } let mut first = true; - for i in 0..self.num_params { + for i in 0..self.metadata.num_params { if !first { writer.write_str(", ")?; } first = false; - 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)), - ) - }); + 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)), + ) + }); if operator { write!(writer, "{param_type}")?; @@ -516,7 +522,7 @@ impl FuncInfo { write!( writer, ") -> {};", - def_type_name(&self.return_type, def.engine) + def_type_name(&self.metadata.return_type, def.engine) )?; Ok(()) diff --git a/src/module/mod.rs b/src/module/mod.rs index 5a639d45..ea03c293 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -25,6 +25,9 @@ use std::{ #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] use crate::func::register::Mut; +/// Initial capacity of the hashmap for functions. +const FN_MAP_SIZE: usize = 16; + /// A type representing the namespace of a function. #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] #[cfg_attr(feature = "metadata", derive(serde::Serialize))] @@ -60,12 +63,10 @@ impl FnNamespace { } } -/// A type containing a single registered function. +/// A type containing the metadata of a single registered function. #[derive(Debug, Clone)] #[non_exhaustive] -pub struct FuncInfo { - /// Function instance. - pub func: CallableFunction, +pub struct FuncInfoMetadata { /// Function namespace. pub namespace: FnNamespace, /// Function access mode. @@ -87,25 +88,35 @@ pub struct FuncInfo { pub comments: Box<[Box]>, } +/// A type containing a single registered function. +#[derive(Debug, Clone)] +pub struct FuncInfo { + /// Function instance. + pub func: CallableFunction, + /// Function metadata. + pub metadata: Box, +} + 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 signature = format!("{}(", self.name); + let mut signature = format!("{}(", self.metadata.name); - let return_type = format_type(&self.return_type, true); + let return_type = format_type(&self.metadata.return_type, true); - if self.params_info.is_empty() { - for x in 0..self.num_params { + if self.metadata.params_info.is_empty() { + for x in 0..self.metadata.num_params { signature.push('_'); - if x < self.num_params - 1 { + if x < self.metadata.num_params - 1 { signature.push_str(", "); } } } else { let params: StaticVec<_> = self + .metadata .params_info .iter() .map(|param| { @@ -177,7 +188,7 @@ pub struct Module { id: Option, /// Module documentation. #[cfg(feature = "metadata")] - doc: Option, + doc: Option>, /// Custom types. custom_types: Option>, /// Sub-modules. @@ -187,12 +198,12 @@ pub struct Module { /// Flattened collection of all [`Module`] variables, including those in sub-modules. all_variables: Option>>, /// Functions (both native Rust and scripted). - functions: Option>>, + functions: Option>, /// Flattened collection of all functions, native Rust and scripted. /// including those in sub-modules. all_functions: Option>>, /// Bloom filter on native Rust functions (in scripted hash format) that contain [`Dynamic`] parameters. - dynamic_functions_filter: BloomFilterU64, + dynamic_functions_filter: Option>, /// Iterator functions, keyed by the type producing the iterator. type_iterators: Option>>>, /// Flattened collection of iterator functions, including those in sub-modules. @@ -293,7 +304,7 @@ impl Module { all_variables: None, functions: None, all_functions: None, - dynamic_functions_filter: BloomFilterU64::new(), + dynamic_functions_filter: None, type_iterators: None, all_type_iterators: None, flags: ModuleFlags::INDEXED, @@ -400,7 +411,7 @@ impl Module { #[cfg(feature = "metadata")] #[inline(always)] pub fn set_doc(&mut self, doc: impl Into) -> &mut Self { - self.doc = Some(doc.into()); + self.doc = Some(Box::new(doc.into())); self } @@ -436,7 +447,7 @@ impl Module { self.all_variables = None; self.functions = None; self.all_functions = None; - self.dynamic_functions_filter.clear(); + self.dynamic_functions_filter = None; self.type_iterators = None; self.all_type_iterators = None; self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS; @@ -575,7 +586,7 @@ impl Module { #[inline] pub fn gen_fn_signatures(&self) -> impl Iterator + '_ { self.iter_fn() - .filter(|&f| match f.access { + .filter(|&f| match f.metadata.access { FnAccess::Public => true, FnAccess::Private => false, }) @@ -687,23 +698,30 @@ impl Module { let hash_script = crate::calc_fn_hash(None, &fn_def.name, num_params); #[cfg(feature = "metadata")] let params_info = fn_def.params.iter().map(Into::into).collect(); - self.functions.get_or_insert_with(Default::default).insert( - hash_script, - FuncInfo { - 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(), - }, - ); + self.functions + .get_or_insert_with(|| { + StraightHashMap::with_capacity_and_hasher(FN_MAP_SIZE, Default::default()) + }) + .insert( + hash_script, + FuncInfo { + metadata: FuncInfoMetadata { + 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(), + } + .into(), + func: fn_def.into(), + }, + ); self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS; hash_script } @@ -722,7 +740,7 @@ impl Module { let name = name.as_ref(); lib.values() - .find(|&f| f.num_params == num_params && f.name == name) + .find(|&f| f.metadata.num_params == num_params && f.metadata.name == name) .and_then(|f| f.func.get_script_fn_def()) }) } @@ -860,14 +878,14 @@ impl Module { .collect(); if let Some(f) = self.functions.as_mut().and_then(|m| m.get_mut(&hash_fn)) { - let (param_names, return_type_name) = if param_names.len() > f.num_params { + let (param_names, return_type_name) = if param_names.len() > f.metadata.num_params { let return_type = param_names.pop().unwrap(); (param_names, return_type) } else { (param_names, crate::SmartString::new_const()) }; - f.params_info = param_names; - f.return_type = return_type_name; + f.metadata.params_info = param_names; + f.metadata.return_type = return_type_name; } self @@ -913,7 +931,7 @@ impl Module { .as_mut() .and_then(|m| m.get_mut(&hash_fn)) .unwrap(); - f.comments = comments.iter().map(|s| s.as_ref().into()).collect(); + f.metadata.comments = comments.iter().map(|s| s.as_ref().into()).collect(); } self @@ -925,7 +943,7 @@ impl Module { #[inline] pub fn update_fn_namespace(&mut self, hash_fn: u64, namespace: FnNamespace) -> &mut Self { if let Some(f) = self.functions.as_mut().and_then(|m| m.get_mut(&hash_fn)) { - f.namespace = namespace; + f.metadata.namespace = namespace; self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS; } self @@ -1013,28 +1031,33 @@ impl Module { let hash_fn = calc_fn_hash_full(hash_script, param_types.iter().copied()); if is_dynamic { - self.dynamic_functions_filter.mark(hash_script); + self.dynamic_functions_filter + .get_or_insert_with(Default::default) + .mark(hash_script); } self.functions .get_or_insert_with(|| { - StraightHashMap::with_capacity_and_hasher(16, Default::default()).into() + StraightHashMap::with_capacity_and_hasher(FN_MAP_SIZE, Default::default()) }) .insert( hash_fn, FuncInfo { 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(), + metadata: FuncInfoMetadata { + 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(), }, ); @@ -1087,7 +1110,7 @@ impl Module { if !comments.is_empty() { let f = self.functions.as_mut().unwrap().get_mut(&hash).unwrap(); - f.comments = comments.iter().map(|s| s.as_ref().into()).collect(); + f.metadata.comments = comments.iter().map(|s| s.as_ref().into()).collect(); } hash @@ -1533,10 +1556,13 @@ impl 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)] + #[inline] #[must_use] pub(crate) fn may_contain_dynamic_fn(&self, hash_script: u64) -> bool { - !self.dynamic_functions_filter.is_absent(hash_script) + !self + .dynamic_functions_filter + .as_ref() + .map_or(false, |f| f.is_absent(hash_script)) } /// Does the particular namespace-qualified function exist in the [`Module`]? @@ -1587,7 +1613,13 @@ impl Module { Some(_) => (), None => self.functions = other.functions, } - self.dynamic_functions_filter += &other.dynamic_functions_filter; + match self.dynamic_functions_filter { + Some(ref mut m) if other.dynamic_functions_filter.is_some() => { + **m += &**other.dynamic_functions_filter.as_ref().unwrap() + } + Some(_) => (), + None => self.dynamic_functions_filter = other.dynamic_functions_filter, + } match self.type_iterators { Some(ref mut m) if other.type_iterators.is_some() => { m.extend(other.type_iterators.unwrap().into_iter()) @@ -1637,7 +1669,13 @@ impl Module { Some(_) => (), None => self.functions = other.functions, } - self.dynamic_functions_filter += &other.dynamic_functions_filter; + match self.dynamic_functions_filter { + Some(ref mut m) if other.dynamic_functions_filter.is_some() => { + **m += &**other.dynamic_functions_filter.as_ref().unwrap() + } + Some(_) => (), + None => self.dynamic_functions_filter = other.dynamic_functions_filter, + } match self.type_iterators { Some(ref mut m) if other.type_iterators.is_some() => { m.extend(other.type_iterators.unwrap().into_iter()) @@ -1686,15 +1724,23 @@ impl Module { } } if let Some(ref functions) = other.functions { - for (k, f) in functions.iter() { - let map = self.functions.get_or_insert_with(Default::default); + let others_len = functions.len(); - if !map.contains_key(k) { - map.insert(*k, f.clone()); - } + for (&k, f) in functions.iter() { + let map = self.functions.get_or_insert_with(|| { + StraightHashMap::with_capacity_and_hasher(others_len, Default::default()) + }); + map.reserve(others_len); + map.entry(k).or_insert_with(|| f.clone()); } } - self.dynamic_functions_filter += &other.dynamic_functions_filter; + match self.dynamic_functions_filter { + Some(ref mut m) if other.dynamic_functions_filter.is_some() => { + **m += &**other.dynamic_functions_filter.as_ref().unwrap() + } + Some(_) => (), + None => self.dynamic_functions_filter = other.dynamic_functions_filter.clone(), + } if let Some(ref type_iterators) = other.type_iterators { let t = self.type_iterators.get_or_insert_with(Default::default); @@ -1759,11 +1805,11 @@ impl Module { .iter() .filter(|&(.., f)| { _filter( - f.namespace, - f.access, + f.metadata.namespace, + f.metadata.access, f.func.is_script(), - f.name.as_str(), - f.num_params, + f.metadata.name.as_str(), + f.metadata.num_params, ) }) .map(|(&k, f)| (k, f.clone())), @@ -1771,7 +1817,13 @@ impl Module { None => self.functions = other.functions.clone(), } } - self.dynamic_functions_filter += &other.dynamic_functions_filter; + match self.dynamic_functions_filter { + Some(ref mut m) if other.dynamic_functions_filter.is_some() => { + **m += &**other.dynamic_functions_filter.as_ref().unwrap() + } + Some(_) => (), + None => self.dynamic_functions_filter = other.dynamic_functions_filter.clone(), + } if let Some(ref type_iterators) = other.type_iterators { match self.type_iterators { @@ -1805,20 +1857,23 @@ impl Module { filter: impl Fn(FnNamespace, FnAccess, &str, usize) -> bool, ) -> &mut Self { self.functions = std::mem::take(&mut self.functions).map(|m| { - Box::new( - m.into_iter() - .filter(|(.., f)| { - if f.func.is_script() { - filter(f.namespace, f.access, f.name.as_str(), f.num_params) - } else { - false - } - }) - .collect(), - ) + m.into_iter() + .filter(|(.., f)| { + if f.func.is_script() { + filter( + f.metadata.namespace, + f.metadata.access, + f.metadata.name.as_str(), + f.metadata.num_params, + ) + } else { + false + } + }) + .collect() }); - self.dynamic_functions_filter.clear(); + self.dynamic_functions_filter = None; self.all_functions = None; self.all_variables = None; self.all_type_iterators = None; @@ -1883,10 +1938,10 @@ impl Module { > + '_ { self.iter_fn().filter(|&f| f.func.is_script()).map(|f| { ( - f.namespace, - f.access, - f.name.as_str(), - f.num_params, + f.metadata.namespace, + f.metadata.access, + f.metadata.name.as_str(), + f.metadata.num_params, f.func.get_script_fn_def().expect("script-defined function"), ) }) @@ -2080,7 +2135,7 @@ impl Module { ast.shared_lib() .iter_fn() - .filter(|&f| match f.access { + .filter(|&f| match f.metadata.access { FnAccess::Public => true, FnAccess::Private => false, }) @@ -2164,7 +2219,7 @@ impl Module { // Index all Rust functions for (&hash, f) in module.functions.iter().flat_map(|m| m.iter()) { - match f.namespace { + match f.metadata.namespace { FnNamespace::Global => { // Flatten all functions with global namespace functions.insert(hash, f.func.clone()); @@ -2172,18 +2227,24 @@ impl Module { } FnNamespace::Internal => (), } - match f.access { + match f.metadata.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.name.as_str(), &f.param_types); + let hash_qualified_fn = calc_native_fn_hash( + path.iter().copied(), + f.metadata.name.as_str(), + &f.metadata.param_types, + ); functions.insert(hash_qualified_fn, f.func.clone()); } else if cfg!(not(feature = "no_function")) { - let hash_qualified_script = - crate::calc_fn_hash(path.iter().copied(), &f.name, f.num_params); + let hash_qualified_script = crate::calc_fn_hash( + path.iter().copied(), + &f.metadata.name, + f.metadata.num_params, + ); functions.insert(hash_qualified_script, f.func.clone()); } } @@ -2193,8 +2254,14 @@ impl Module { if !self.is_indexed() { let mut path = Vec::with_capacity(4); - let mut variables = StraightHashMap::default(); - let mut functions = StraightHashMap::default(); + let mut variables = StraightHashMap::with_capacity_and_hasher( + self.variables.as_ref().map_or(0, |m| m.len()), + Default::default(), + ); + let mut functions = StraightHashMap::with_capacity_and_hasher( + self.functions.as_ref().map_or(0, |m| m.len()), + Default::default(), + ); let mut type_iterators = BTreeMap::new(); path.push(""); diff --git a/src/serde/metadata.rs b/src/serde/metadata.rs index 50c76d86..7249f1bd 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(None, &info.name, info.num_params); + let base_hash = calc_fn_hash(None, &info.metadata.name, info.metadata.num_params); let (typ, full_hash) = if info.func.is_script() { (FnType::Script, base_hash) } else { ( FnType::Native, - calc_native_fn_hash(None, &info.name, &info.param_types), + calc_native_fn_hash(None, &info.metadata.name, &info.metadata.param_types), ) }; @@ -80,12 +80,13 @@ impl<'a> From<&'a FuncInfo> for FnMetadata<'a> { base_hash, full_hash, #[cfg(not(feature = "no_module"))] - namespace: info.namespace, - access: info.access, - name: &info.name, + namespace: info.metadata.namespace, + access: info.metadata.access, + name: &info.metadata.name, typ, - num_params: info.num_params, + num_params: info.metadata.num_params, params: info + .metadata .params_info .iter() .map(|s| { @@ -99,7 +100,7 @@ impl<'a> From<&'a FuncInfo> for FnMetadata<'a> { }) .collect(), _dummy: None, - return_type: format_type(&info.return_type, true), + return_type: format_type(&info.metadata.return_type, true), signature: info.gen_signature().into(), doc_comments: if info.func.is_script() { #[cfg(feature = "no_function")] @@ -114,7 +115,7 @@ impl<'a> From<&'a FuncInfo> for FnMetadata<'a> { .map(<_>::as_ref) .collect() } else { - info.comments.iter().map(<_>::as_ref).collect() + info.metadata.comments.iter().map(<_>::as_ref).collect() }, } }