diff --git a/src/ast.rs b/src/ast.rs index c1d44fe9..44970d9c 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -15,6 +15,12 @@ use crate::FLOAT; #[cfg(not(feature = "no_module"))] use crate::engine::Imports; +#[cfg(not(feature = "no_index"))] +use crate::engine::TYPICAL_ARRAY_SIZE; + +#[cfg(not(feature = "no_object"))] +use crate::engine::TYPICAL_MAP_SIZE; + use crate::stdlib::{ borrow::Cow, boxed::Box, @@ -31,6 +37,9 @@ use crate::stdlib::{ #[cfg(not(feature = "no_closure"))] use crate::stdlib::collections::HashSet; +#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] +use crate::stdlib::cmp::max; + /// A type representing the access mode of a scripted function. #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] pub enum FnAccess { @@ -927,17 +936,20 @@ impl Expr { Self::Unit(_) => ().into(), #[cfg(not(feature = "no_index"))] - Self::Array(x, _) if x.iter().all(Self::is_constant) => Dynamic(Union::Array( - Box::new(x.iter().map(|v| v.get_constant_value().unwrap()).collect()), - )), + Self::Array(x, _) if self.is_constant() => { + let mut arr = Vec::with_capacity(max(TYPICAL_ARRAY_SIZE, x.len())); + arr.extend(x.iter().map(|v| v.get_constant_value().unwrap())); + Dynamic(Union::Array(Box::new(arr))) + } #[cfg(not(feature = "no_object"))] - Self::Map(x, _) if x.iter().all(|(_, v)| v.is_constant()) => { - Dynamic(Union::Map(Box::new( + Self::Map(x, _) if self.is_constant() => { + let mut map = HashMap::with_capacity(max(TYPICAL_MAP_SIZE, x.len())); + map.extend( x.iter() - .map(|(k, v)| (k.name.clone(), v.get_constant_value().unwrap())) - .collect(), - ))) + .map(|(k, v)| (k.name.clone(), v.get_constant_value().unwrap())), + ); + Dynamic(Union::Map(Box::new(map))) } _ => return None, diff --git a/src/engine.rs b/src/engine.rs index 8da32270..06a249af 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -58,12 +58,21 @@ use crate::stdlib::mem; #[cfg(not(feature = "no_index"))] pub type Array = crate::stdlib::vec::Vec; +#[cfg(not(feature = "no_index"))] +pub const TYPICAL_ARRAY_SIZE: usize = 8; // Small arrays are typical + +#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] +use crate::stdlib::cmp::max; + /// Hash map of `Dynamic` values with `ImmutableString` keys. /// /// Not available under the `no_object` feature. #[cfg(not(feature = "no_object"))] pub type Map = HashMap; +#[cfg(not(feature = "no_object"))] +pub const TYPICAL_MAP_SIZE: usize = 8; // Small maps are typical + /// _[INTERNALS]_ A stack of imported modules. /// Exported under the `internals` feature only. /// @@ -597,7 +606,7 @@ pub struct Engine { /// A hashset containing symbols to disable. pub(crate) disabled_symbols: HashSet, - /// A hashset containing custom keywords and precedence to recognize. + /// A hashmap containing custom keywords and precedence to recognize. pub(crate) custom_keywords: HashMap>, /// Custom syntax. pub(crate) custom_syntax: HashMap, @@ -711,10 +720,10 @@ impl Engine { #[cfg(any(feature = "no_std", target_arch = "wasm32",))] module_resolver: None, - type_names: Default::default(), - disabled_symbols: Default::default(), - custom_keywords: Default::default(), - custom_syntax: Default::default(), + type_names: HashMap::with_capacity(8), + disabled_symbols: HashSet::with_capacity(4), + custom_keywords: HashMap::with_capacity(4), + custom_syntax: HashMap::with_capacity(4), // variable resolver resolve_var: None, @@ -768,10 +777,10 @@ impl Engine { #[cfg(not(feature = "no_module"))] module_resolver: None, - type_names: Default::default(), - disabled_symbols: Default::default(), - custom_keywords: Default::default(), - custom_syntax: Default::default(), + type_names: HashMap::with_capacity(8), + disabled_symbols: HashSet::with_capacity(4), + custom_keywords: HashMap::with_capacity(4), + custom_syntax: HashMap::with_capacity(4), resolve_var: None, @@ -1716,21 +1725,25 @@ impl Engine { } #[cfg(not(feature = "no_index"))] - Expr::Array(x, _) => Ok(Dynamic(Union::Array(Box::new( - x.iter() - .map(|item| self.eval_expr(scope, mods, state, lib, this_ptr, item, level)) - .collect::>()?, - )))), + Expr::Array(x, _) => { + let mut arr = Vec::with_capacity(max(TYPICAL_ARRAY_SIZE, x.len())); + for item in x.as_ref() { + arr.push(self.eval_expr(scope, mods, state, lib, this_ptr, item, level)?); + } + Ok(Dynamic(Union::Array(Box::new(arr)))) + } #[cfg(not(feature = "no_object"))] - Expr::Map(x, _) => Ok(Dynamic(Union::Map(Box::new( - x.iter() - .map(|(key, expr)| { - self.eval_expr(scope, mods, state, lib, this_ptr, expr, level) - .map(|val| (key.name.clone(), val)) - }) - .collect::, _>>()?, - )))), + Expr::Map(x, _) => { + let mut map = HashMap::with_capacity(max(TYPICAL_MAP_SIZE, x.len())); + for (IdentX { name: key, .. }, expr) in x.as_ref() { + map.insert( + key.clone(), + self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?, + ); + } + Ok(Dynamic(Union::Map(Box::new(map)))) + } // Normal function call Expr::FnCall(x, pos) if x.namespace.is_none() => { diff --git a/src/engine_settings.rs b/src/engine_settings.rs index aebcfdb3..8add94b8 100644 --- a/src/engine_settings.rs +++ b/src/engine_settings.rs @@ -23,8 +23,7 @@ impl Engine { /// In other words, loaded packages are searched in reverse order. #[inline(always)] pub fn load_package(&mut self, package: impl Into) -> &mut Self { - // Push the package to the top - packages are searched in reverse order - self.packages.push(package.into()); + self.packages.add(package.into()); self } /// Control whether and how the `Engine` will optimize an AST after compilation. diff --git a/src/module/mod.rs b/src/module/mod.rs index 9df9aec5..53882fa8 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -59,7 +59,7 @@ pub struct FuncInfo { /// and/or script-defined functions. /// /// Not available under the `no_module` feature. -#[derive(Default, Clone)] +#[derive(Clone)] pub struct Module { /// Sub-modules. modules: HashMap>, @@ -69,15 +69,29 @@ pub struct Module { all_variables: HashMap, /// External Rust functions. functions: HashMap, - /// Iterator functions, keyed by the type producing the iterator. - type_iterators: HashMap, /// Flattened collection of all external Rust functions, native or scripted, /// including those in sub-modules. all_functions: HashMap, + /// Iterator functions, keyed by the type producing the iterator. + type_iterators: HashMap, /// Is the module indexed? indexed: bool, } +impl Default for Module { + fn default() -> Self { + Self { + modules: HashMap::with_capacity(4), + variables: HashMap::with_capacity(4), + all_variables: HashMap::with_capacity_and_hasher(32, StraightHasherBuilder), + functions: HashMap::with_capacity_and_hasher(64, StraightHasherBuilder), + all_functions: HashMap::with_capacity_and_hasher(256, StraightHasherBuilder), + type_iterators: HashMap::with_capacity(4), + indexed: false, + } + } +} + impl fmt::Debug for Module { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( @@ -1462,9 +1476,9 @@ impl Module { } if !self.indexed { - let mut qualifiers: Vec<_> = Default::default(); - let mut variables: Vec<_> = Default::default(); - let mut functions: Vec<_> = Default::default(); + let mut qualifiers = Vec::with_capacity(4); + let mut variables = Vec::with_capacity(8); + let mut functions = Vec::with_capacity(16); qualifiers.push("root"); diff --git a/src/optimize.rs b/src/optimize.rs index 501e1132..03b2bd3b 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -17,12 +17,6 @@ use crate::{calc_native_fn_hash, StaticVec}; #[cfg(not(feature = "no_function"))] use crate::ast::ReturnType; -#[cfg(not(feature = "no_index"))] -use crate::Array; - -#[cfg(not(feature = "no_object"))] -use crate::Map; - use crate::stdlib::{ boxed::Box, hash::{Hash, Hasher}, @@ -566,26 +560,22 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) { }, // [ constant .. ] #[cfg(not(feature = "no_index"))] - Expr::Array(a, pos) if a.iter().all(Expr::is_constant) => { + Expr::Array(_, _) if expr.is_constant() => { state.set_dirty(); - let mut arr: Array = Default::default(); - arr.extend(mem::take(a).into_iter().map(|expr| expr.get_constant_value().unwrap())); - *expr = Expr::DynamicConstant(Box::new(arr.into()), *pos); + *expr = Expr::DynamicConstant(Box::new(expr.get_constant_value().unwrap()), expr.position()); } // [ items .. ] #[cfg(not(feature = "no_index"))] - Expr::Array(a, _) => a.iter_mut().for_each(|expr| optimize_expr(expr, state)), + Expr::Array(x, _) => x.iter_mut().for_each(|expr| optimize_expr(expr, state)), // #{ key:constant, .. } #[cfg(not(feature = "no_object"))] - Expr::Map(m, pos) if m.iter().all(|(_, expr)| expr.is_constant()) => { + Expr::Map(_, _) if expr.is_constant()=> { state.set_dirty(); - let mut map: Map = Default::default(); - map.extend(mem::take(m).into_iter().map(|(key, expr)| (key.name, expr.get_constant_value().unwrap()))); - *expr = Expr::DynamicConstant(Box::new(map.into()), *pos); + *expr = Expr::DynamicConstant(Box::new(expr.get_constant_value().unwrap()), expr.position()); } // #{ key:value, .. } #[cfg(not(feature = "no_object"))] - Expr::Map(m, _) => m.iter_mut().for_each(|(_, expr)| optimize_expr(expr, state)), + Expr::Map(x, _) => x.iter_mut().for_each(|(_, expr)| optimize_expr(expr, state)), // lhs in rhs Expr::In(x, _) => match (&mut x.lhs, &mut x.rhs) { // "xxx" in "xxxxx" diff --git a/src/packages/mod.rs b/src/packages/mod.rs index 072de190..dae31183 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -56,7 +56,9 @@ pub(crate) struct PackagesCollection(StaticVec); impl PackagesCollection { /// Add a `PackageLibrary` into the `PackagesCollection`. - pub fn push(&mut self, package: PackageLibrary) { + /// + /// Packages are searched in reverse order. + pub fn add(&mut self, package: PackageLibrary) { // Later packages override previous ones. self.0.insert(0, package); } diff --git a/src/parser.rs b/src/parser.rs index 11e79a17..8897232f 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -69,7 +69,7 @@ struct ParseState<'e> { allow_capture: bool, /// Encapsulates a local stack with imported module names. #[cfg(not(feature = "no_module"))] - modules: Vec, + modules: StaticVec, /// Maximum levels of expression nesting. #[cfg(not(feature = "unchecked"))] max_expr_depth: usize, @@ -99,11 +99,11 @@ impl<'e> ParseState<'e> { #[cfg(not(feature = "no_function"))] max_function_expr_depth, #[cfg(not(feature = "no_closure"))] - externals: Default::default(), + externals: HashMap::with_capacity(8), #[cfg(not(feature = "no_closure"))] allow_capture: true, - strings: Default::default(), - stack: Default::default(), + strings: HashMap::with_capacity(64), + stack: Vec::with_capacity(16), #[cfg(not(feature = "no_module"))] modules: Default::default(), } @@ -818,7 +818,7 @@ fn parse_switch( } } - let mut table: HashMap = Default::default(); + let mut table = HashMap::with_capacity_and_hasher(16, StraightHasherBuilder); let mut def_stmt = None; loop { @@ -1137,17 +1137,15 @@ fn parse_unary( match token { // If statement is allowed to act as expressions - Token::If if settings.allow_if_expr => { - let mut block: StaticVec<_> = Default::default(); - block.push(parse_if(input, state, lib, settings.level_up())?); - Ok(Expr::Stmt(Box::new(block), settings.pos)) - } + Token::If if settings.allow_if_expr => Ok(Expr::Stmt( + Box::new(vec![parse_if(input, state, lib, settings.level_up())?].into()), + settings.pos, + )), // Switch statement is allowed to act as expressions - Token::Switch if settings.allow_switch_expr => { - let mut block: StaticVec<_> = Default::default(); - block.push(parse_switch(input, state, lib, settings.level_up())?); - Ok(Expr::Stmt(Box::new(block), settings.pos)) - } + Token::Switch if settings.allow_switch_expr => Ok(Expr::Stmt( + Box::new(vec![parse_switch(input, state, lib, settings.level_up())?].into()), + settings.pos, + )), // -expr Token::UnaryMinus => { let pos = eat_token(input, Token::UnaryMinus); @@ -2635,7 +2633,7 @@ fn parse_fn( let params: StaticVec<_> = params.into_iter().map(|(p, _)| p).collect(); #[cfg(not(feature = "no_closure"))] - let externals: HashSet<_> = state + let externals = state .externals .iter() .map(|(name, _)| name) @@ -2802,7 +2800,7 @@ fn parse_anon_fn( access: FnAccess::Public, params, #[cfg(not(feature = "no_closure"))] - externals: Default::default(), + externals: HashSet::with_capacity(4), body, lib: None, #[cfg(not(feature = "no_module"))]