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"))]
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,

View File

@ -58,12 +58,21 @@ use crate::stdlib::mem;
#[cfg(not(feature = "no_index"))]
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.
///
/// Not available under the `no_object` feature.
#[cfg(not(feature = "no_object"))]
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.
/// Exported under the `internals` feature only.
///
@ -597,7 +606,7 @@ pub struct Engine {
/// A hashset containing symbols to disable.
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>>,
/// Custom syntax.
pub(crate) custom_syntax: HashMap<ImmutableString, CustomSyntax>,
@ -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::<Result<_, _>>()?,
)))),
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::<Result<HashMap<_, _>, _>>()?,
)))),
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() => {

View File

@ -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<PackageLibrary>) -> &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.

View File

@ -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<String, Shared<Module>>,
@ -69,15 +69,29 @@ pub struct Module {
all_variables: HashMap<u64, Dynamic, StraightHasherBuilder>,
/// External Rust functions.
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,
/// including those in sub-modules.
all_functions: HashMap<u64, CallableFunction, StraightHasherBuilder>,
/// Iterator functions, keyed by the type producing the iterator.
type_iterators: HashMap<TypeId, IteratorFn>,
/// 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");

View File

@ -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"

View File

@ -56,7 +56,9 @@ pub(crate) struct PackagesCollection(StaticVec<PackageLibrary>);
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);
}

View File

@ -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<ImmutableString>,
modules: StaticVec<ImmutableString>,
/// 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<u64, Stmt, StraightHasherBuilder> = 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"))]