Set capacity of hash maps.

This commit is contained in:
Stephen Chung 2020-11-15 12:07:35 +08:00
parent a63f14b59c
commit bde8917ed4
7 changed files with 100 additions and 72 deletions

View File

@ -15,6 +15,12 @@ use crate::FLOAT;
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
use crate::engine::Imports; 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::{ use crate::stdlib::{
borrow::Cow, borrow::Cow,
boxed::Box, boxed::Box,
@ -31,6 +37,9 @@ use crate::stdlib::{
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
use crate::stdlib::collections::HashSet; 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. /// A type representing the access mode of a scripted function.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum FnAccess { pub enum FnAccess {
@ -927,17 +936,20 @@ impl Expr {
Self::Unit(_) => ().into(), Self::Unit(_) => ().into(),
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Self::Array(x, _) if x.iter().all(Self::is_constant) => Dynamic(Union::Array( Self::Array(x, _) if self.is_constant() => {
Box::new(x.iter().map(|v| v.get_constant_value().unwrap()).collect()), 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"))] #[cfg(not(feature = "no_object"))]
Self::Map(x, _) if x.iter().all(|(_, v)| v.is_constant()) => { Self::Map(x, _) if self.is_constant() => {
Dynamic(Union::Map(Box::new( let mut map = HashMap::with_capacity(max(TYPICAL_MAP_SIZE, x.len()));
map.extend(
x.iter() x.iter()
.map(|(k, v)| (k.name.clone(), v.get_constant_value().unwrap())) .map(|(k, v)| (k.name.clone(), v.get_constant_value().unwrap())),
.collect(), );
))) Dynamic(Union::Map(Box::new(map)))
} }
_ => return None, _ => return None,

View File

@ -58,12 +58,21 @@ use crate::stdlib::mem;
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
pub type Array = crate::stdlib::vec::Vec<Dynamic>; pub type Array = crate::stdlib::vec::Vec<Dynamic>;
#[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. /// Hash map of `Dynamic` values with `ImmutableString` keys.
/// ///
/// Not available under the `no_object` feature. /// Not available under the `no_object` feature.
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
pub type Map = HashMap<ImmutableString, Dynamic>; pub type Map = HashMap<ImmutableString, Dynamic>;
#[cfg(not(feature = "no_object"))]
pub const TYPICAL_MAP_SIZE: usize = 8; // Small maps are typical
/// _[INTERNALS]_ A stack of imported modules. /// _[INTERNALS]_ A stack of imported modules.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
/// ///
@ -597,7 +606,7 @@ pub struct Engine {
/// A hashset containing symbols to disable. /// A hashset containing symbols to disable.
pub(crate) disabled_symbols: HashSet<String>, pub(crate) disabled_symbols: HashSet<String>,
/// A hashset containing custom keywords and precedence to recognize. /// A hashmap containing custom keywords and precedence to recognize.
pub(crate) custom_keywords: HashMap<String, Option<u8>>, pub(crate) custom_keywords: HashMap<String, Option<u8>>,
/// Custom syntax. /// Custom syntax.
pub(crate) custom_syntax: HashMap<ImmutableString, CustomSyntax>, pub(crate) custom_syntax: HashMap<ImmutableString, CustomSyntax>,
@ -711,10 +720,10 @@ impl Engine {
#[cfg(any(feature = "no_std", target_arch = "wasm32",))] #[cfg(any(feature = "no_std", target_arch = "wasm32",))]
module_resolver: None, module_resolver: None,
type_names: Default::default(), type_names: HashMap::with_capacity(8),
disabled_symbols: Default::default(), disabled_symbols: HashSet::with_capacity(4),
custom_keywords: Default::default(), custom_keywords: HashMap::with_capacity(4),
custom_syntax: Default::default(), custom_syntax: HashMap::with_capacity(4),
// variable resolver // variable resolver
resolve_var: None, resolve_var: None,
@ -768,10 +777,10 @@ impl Engine {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
module_resolver: None, module_resolver: None,
type_names: Default::default(), type_names: HashMap::with_capacity(8),
disabled_symbols: Default::default(), disabled_symbols: HashSet::with_capacity(4),
custom_keywords: Default::default(), custom_keywords: HashMap::with_capacity(4),
custom_syntax: Default::default(), custom_syntax: HashMap::with_capacity(4),
resolve_var: None, resolve_var: None,
@ -1716,21 +1725,25 @@ impl Engine {
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Array(x, _) => Ok(Dynamic(Union::Array(Box::new( Expr::Array(x, _) => {
x.iter() let mut arr = Vec::with_capacity(max(TYPICAL_ARRAY_SIZE, x.len()));
.map(|item| self.eval_expr(scope, mods, state, lib, this_ptr, item, level)) for item in x.as_ref() {
.collect::<Result<_, _>>()?, 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"))] #[cfg(not(feature = "no_object"))]
Expr::Map(x, _) => Ok(Dynamic(Union::Map(Box::new( Expr::Map(x, _) => {
x.iter() let mut map = HashMap::with_capacity(max(TYPICAL_MAP_SIZE, x.len()));
.map(|(key, expr)| { for (IdentX { name: key, .. }, expr) in x.as_ref() {
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level) map.insert(
.map(|val| (key.name.clone(), val)) key.clone(),
}) self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?,
.collect::<Result<HashMap<_, _>, _>>()?, );
)))), }
Ok(Dynamic(Union::Map(Box::new(map))))
}
// Normal function call // Normal function call
Expr::FnCall(x, pos) if x.namespace.is_none() => { Expr::FnCall(x, pos) if x.namespace.is_none() => {

View File

@ -23,8 +23,7 @@ impl Engine {
/// In other words, loaded packages are searched in reverse order. /// In other words, loaded packages are searched in reverse order.
#[inline(always)] #[inline(always)]
pub fn load_package(&mut self, package: impl Into<PackageLibrary>) -> &mut Self { pub fn load_package(&mut self, package: impl Into<PackageLibrary>) -> &mut Self {
// Push the package to the top - packages are searched in reverse order self.packages.add(package.into());
self.packages.push(package.into());
self self
} }
/// Control whether and how the `Engine` will optimize an AST after compilation. /// Control whether and how the `Engine` will optimize an AST after compilation.

View File

@ -59,7 +59,7 @@ pub struct FuncInfo {
/// and/or script-defined functions. /// and/or script-defined functions.
/// ///
/// Not available under the `no_module` feature. /// Not available under the `no_module` feature.
#[derive(Default, Clone)] #[derive(Clone)]
pub struct Module { pub struct Module {
/// Sub-modules. /// Sub-modules.
modules: HashMap<String, Shared<Module>>, modules: HashMap<String, Shared<Module>>,
@ -69,15 +69,29 @@ pub struct Module {
all_variables: HashMap<u64, Dynamic, StraightHasherBuilder>, all_variables: HashMap<u64, Dynamic, StraightHasherBuilder>,
/// External Rust functions. /// External Rust functions.
functions: HashMap<u64, FuncInfo, StraightHasherBuilder>, functions: HashMap<u64, FuncInfo, StraightHasherBuilder>,
/// Iterator functions, keyed by the type producing the iterator.
type_iterators: HashMap<TypeId, IteratorFn>,
/// Flattened collection of all external Rust functions, native or scripted, /// Flattened collection of all external Rust functions, native or scripted,
/// including those in sub-modules. /// including those in sub-modules.
all_functions: HashMap<u64, CallableFunction, StraightHasherBuilder>, all_functions: HashMap<u64, CallableFunction, StraightHasherBuilder>,
/// Iterator functions, keyed by the type producing the iterator.
type_iterators: HashMap<TypeId, IteratorFn>,
/// Is the module indexed? /// Is the module indexed?
indexed: bool, 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 { impl fmt::Debug for Module {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!( write!(
@ -1462,9 +1476,9 @@ impl Module {
} }
if !self.indexed { if !self.indexed {
let mut qualifiers: Vec<_> = Default::default(); let mut qualifiers = Vec::with_capacity(4);
let mut variables: Vec<_> = Default::default(); let mut variables = Vec::with_capacity(8);
let mut functions: Vec<_> = Default::default(); let mut functions = Vec::with_capacity(16);
qualifiers.push("root"); qualifiers.push("root");

View File

@ -17,12 +17,6 @@ use crate::{calc_native_fn_hash, StaticVec};
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
use crate::ast::ReturnType; use crate::ast::ReturnType;
#[cfg(not(feature = "no_index"))]
use crate::Array;
#[cfg(not(feature = "no_object"))]
use crate::Map;
use crate::stdlib::{ use crate::stdlib::{
boxed::Box, boxed::Box,
hash::{Hash, Hasher}, hash::{Hash, Hasher},
@ -566,26 +560,22 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
}, },
// [ constant .. ] // [ constant .. ]
#[cfg(not(feature = "no_index"))] #[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(); state.set_dirty();
let mut arr: Array = Default::default(); *expr = Expr::DynamicConstant(Box::new(expr.get_constant_value().unwrap()), expr.position());
arr.extend(mem::take(a).into_iter().map(|expr| expr.get_constant_value().unwrap()));
*expr = Expr::DynamicConstant(Box::new(arr.into()), *pos);
} }
// [ items .. ] // [ items .. ]
#[cfg(not(feature = "no_index"))] #[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, .. } // #{ key:constant, .. }
#[cfg(not(feature = "no_object"))] #[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(); state.set_dirty();
let mut map: Map = Default::default(); *expr = Expr::DynamicConstant(Box::new(expr.get_constant_value().unwrap()), expr.position());
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);
} }
// #{ key:value, .. } // #{ key:value, .. }
#[cfg(not(feature = "no_object"))] #[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 // lhs in rhs
Expr::In(x, _) => match (&mut x.lhs, &mut x.rhs) { Expr::In(x, _) => match (&mut x.lhs, &mut x.rhs) {
// "xxx" in "xxxxx" // "xxx" in "xxxxx"

View File

@ -56,7 +56,9 @@ pub(crate) struct PackagesCollection(StaticVec<PackageLibrary>);
impl PackagesCollection { impl PackagesCollection {
/// Add a `PackageLibrary` into the `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. // Later packages override previous ones.
self.0.insert(0, package); self.0.insert(0, package);
} }

View File

@ -69,7 +69,7 @@ struct ParseState<'e> {
allow_capture: bool, allow_capture: bool,
/// Encapsulates a local stack with imported module names. /// Encapsulates a local stack with imported module names.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
modules: Vec<ImmutableString>, modules: StaticVec<ImmutableString>,
/// Maximum levels of expression nesting. /// Maximum levels of expression nesting.
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
max_expr_depth: usize, max_expr_depth: usize,
@ -99,11 +99,11 @@ impl<'e> ParseState<'e> {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
max_function_expr_depth, max_function_expr_depth,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
externals: Default::default(), externals: HashMap::with_capacity(8),
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
allow_capture: true, allow_capture: true,
strings: Default::default(), strings: HashMap::with_capacity(64),
stack: Default::default(), stack: Vec::with_capacity(16),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
modules: Default::default(), modules: Default::default(),
} }
@ -818,7 +818,7 @@ fn parse_switch(
} }
} }
let mut table: HashMap<u64, Stmt, StraightHasherBuilder> = Default::default(); let mut table = HashMap::with_capacity_and_hasher(16, StraightHasherBuilder);
let mut def_stmt = None; let mut def_stmt = None;
loop { loop {
@ -1137,17 +1137,15 @@ fn parse_unary(
match token { match token {
// If statement is allowed to act as expressions // If statement is allowed to act as expressions
Token::If if settings.allow_if_expr => { Token::If if settings.allow_if_expr => Ok(Expr::Stmt(
let mut block: StaticVec<_> = Default::default(); Box::new(vec![parse_if(input, state, lib, settings.level_up())?].into()),
block.push(parse_if(input, state, lib, settings.level_up())?); settings.pos,
Ok(Expr::Stmt(Box::new(block), settings.pos)) )),
}
// Switch statement is allowed to act as expressions // Switch statement is allowed to act as expressions
Token::Switch if settings.allow_switch_expr => { Token::Switch if settings.allow_switch_expr => Ok(Expr::Stmt(
let mut block: StaticVec<_> = Default::default(); Box::new(vec![parse_switch(input, state, lib, settings.level_up())?].into()),
block.push(parse_switch(input, state, lib, settings.level_up())?); settings.pos,
Ok(Expr::Stmt(Box::new(block), settings.pos)) )),
}
// -expr // -expr
Token::UnaryMinus => { Token::UnaryMinus => {
let pos = eat_token(input, 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(); let params: StaticVec<_> = params.into_iter().map(|(p, _)| p).collect();
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
let externals: HashSet<_> = state let externals = state
.externals .externals
.iter() .iter()
.map(|(name, _)| name) .map(|(name, _)| name)
@ -2802,7 +2800,7 @@ fn parse_anon_fn(
access: FnAccess::Public, access: FnAccess::Public,
params, params,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
externals: Default::default(), externals: HashSet::with_capacity(4),
body, body,
lib: None, lib: None,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]