Fix dropping issues with StaticVec and use it everywhere.
This commit is contained in:
parent
0cb781c1aa
commit
a2c50879fe
@ -20,7 +20,7 @@ Rhai's current features set:
|
|||||||
* Easily [call a script-defined function](#calling-rhai-functions-from-rust) from Rust
|
* Easily [call a script-defined function](#calling-rhai-functions-from-rust) from Rust
|
||||||
* Low compile-time overhead (~0.6 sec debug/~3 sec release for `rhai_runner` sample app)
|
* Low compile-time overhead (~0.6 sec debug/~3 sec release for `rhai_runner` sample app)
|
||||||
* Fairly efficient evaluation (1 million iterations in 0.75 sec on my 5 year old laptop)
|
* Fairly efficient evaluation (1 million iterations in 0.75 sec on my 5 year old laptop)
|
||||||
* Relatively little `unsafe` code (yes there are some for performance reasons, and all `unsafe` code is limited to
|
* Relatively little `unsafe` code (yes there are some for performance reasons, and most `unsafe` code is limited to
|
||||||
one single source file, all with names starting with `"unsafe_"`)
|
one single source file, all with names starting with `"unsafe_"`)
|
||||||
* Sand-boxed (the scripting [`Engine`] can be declared immutable which cannot mutate the containing environment
|
* Sand-boxed (the scripting [`Engine`] can be declared immutable which cannot mutate the containing environment
|
||||||
unless explicitly allowed via `RefCell` etc.)
|
unless explicitly allowed via `RefCell` etc.)
|
||||||
|
@ -8,7 +8,7 @@ use crate::module::Module;
|
|||||||
use crate::optimize::OptimizationLevel;
|
use crate::optimize::OptimizationLevel;
|
||||||
use crate::packages::{CorePackage, Package, PackageLibrary, PackagesCollection, StandardPackage};
|
use crate::packages::{CorePackage, Package, PackageLibrary, PackagesCollection, StandardPackage};
|
||||||
use crate::parser::{Expr, FnAccess, FnDef, ReturnType, SharedFnDef, Stmt, AST};
|
use crate::parser::{Expr, FnAccess, FnDef, ReturnType, SharedFnDef, Stmt, AST};
|
||||||
use crate::r#unsafe::unsafe_cast_var_name;
|
use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
||||||
use crate::token::Position;
|
use crate::token::Position;
|
||||||
@ -756,7 +756,8 @@ impl Engine {
|
|||||||
args.into_iter().map(|v| mem::take(*v)),
|
args.into_iter().map(|v| mem::take(*v)),
|
||||||
)
|
)
|
||||||
.map(|(name, value)| {
|
.map(|(name, value)| {
|
||||||
let var_name = unsafe_cast_var_name(name.as_str(), &mut state);
|
let var_name =
|
||||||
|
unsafe_cast_var_name_to_lifetime(name.as_str(), &mut state);
|
||||||
(var_name, ScopeEntryType::Normal, value)
|
(var_name, ScopeEntryType::Normal, value)
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -1172,11 +1173,10 @@ impl Engine {
|
|||||||
) -> Result<(), Box<EvalAltResult>> {
|
) -> Result<(), Box<EvalAltResult>> {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::FnCall(x) if x.1.is_none() => {
|
Expr::FnCall(x) if x.1.is_none() => {
|
||||||
let mut arg_values = StaticVec::<Dynamic>::new();
|
let arg_values =
|
||||||
|
x.3.iter()
|
||||||
for arg_expr in x.3.iter() {
|
.map(|arg_expr| self.eval_expr(scope, state, arg_expr, level))
|
||||||
arg_values.push(self.eval_expr(scope, state, arg_expr, level)?);
|
.collect::<Result<StaticVec<Dynamic>, _>>()?;
|
||||||
}
|
|
||||||
|
|
||||||
idx_values.push(Dynamic::from(arg_values));
|
idx_values.push(Dynamic::from(arg_values));
|
||||||
}
|
}
|
||||||
@ -1471,14 +1471,10 @@ impl Engine {
|
|||||||
if !self.has_override(state, (hash_fn, *hash_fn_def)) {
|
if !self.has_override(state, (hash_fn, *hash_fn_def)) {
|
||||||
// eval - only in function call style
|
// eval - only in function call style
|
||||||
let prev_len = scope.len();
|
let prev_len = scope.len();
|
||||||
|
let pos = args_expr.get_ref(0).position();
|
||||||
|
|
||||||
// Evaluate the text string as a script
|
// Evaluate the text string as a script
|
||||||
let result = self.eval_script_expr(
|
let result = self.eval_script_expr(scope, state, args.pop(), pos);
|
||||||
scope,
|
|
||||||
state,
|
|
||||||
args.pop(),
|
|
||||||
args_expr[0].position(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if scope.len() != prev_len {
|
if scope.len() != prev_len {
|
||||||
// IMPORTANT! If the eval defines new variables in the current scope,
|
// IMPORTANT! If the eval defines new variables in the current scope,
|
||||||
@ -1709,7 +1705,7 @@ impl Engine {
|
|||||||
.or_else(|| self.packages.get_iter(tid))
|
.or_else(|| self.packages.get_iter(tid))
|
||||||
{
|
{
|
||||||
// Add the loop variable
|
// Add the loop variable
|
||||||
let var_name = unsafe_cast_var_name(name, &state);
|
let var_name = unsafe_cast_var_name_to_lifetime(name, &state);
|
||||||
scope.push(var_name, ());
|
scope.push(var_name, ());
|
||||||
let index = scope.len() - 1;
|
let index = scope.len() - 1;
|
||||||
state.scope_level += 1;
|
state.scope_level += 1;
|
||||||
@ -1775,7 +1771,7 @@ impl Engine {
|
|||||||
Stmt::Let(x) if x.1.is_some() => {
|
Stmt::Let(x) if x.1.is_some() => {
|
||||||
let ((var_name, pos), expr) = x.as_ref();
|
let ((var_name, pos), expr) = x.as_ref();
|
||||||
let val = self.eval_expr(scope, state, expr.as_ref().unwrap(), level)?;
|
let val = self.eval_expr(scope, state, expr.as_ref().unwrap(), level)?;
|
||||||
let var_name = unsafe_cast_var_name(var_name, &state);
|
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
|
||||||
scope.push_dynamic_value(var_name, ScopeEntryType::Normal, val, false);
|
scope.push_dynamic_value(var_name, ScopeEntryType::Normal, val, false);
|
||||||
self.inc_operations(state, *pos)?;
|
self.inc_operations(state, *pos)?;
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
@ -1783,7 +1779,7 @@ impl Engine {
|
|||||||
|
|
||||||
Stmt::Let(x) => {
|
Stmt::Let(x) => {
|
||||||
let ((var_name, pos), _) = x.as_ref();
|
let ((var_name, pos), _) = x.as_ref();
|
||||||
let var_name = unsafe_cast_var_name(var_name, &state);
|
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
|
||||||
scope.push(var_name, ());
|
scope.push(var_name, ());
|
||||||
self.inc_operations(state, *pos)?;
|
self.inc_operations(state, *pos)?;
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
@ -1793,7 +1789,7 @@ impl Engine {
|
|||||||
Stmt::Const(x) if x.1.is_constant() => {
|
Stmt::Const(x) if x.1.is_constant() => {
|
||||||
let ((var_name, pos), expr) = x.as_ref();
|
let ((var_name, pos), expr) = x.as_ref();
|
||||||
let val = self.eval_expr(scope, state, &expr, level)?;
|
let val = self.eval_expr(scope, state, &expr, level)?;
|
||||||
let var_name = unsafe_cast_var_name(var_name, &state);
|
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
|
||||||
scope.push_dynamic_value(var_name, ScopeEntryType::Constant, val, true);
|
scope.push_dynamic_value(var_name, ScopeEntryType::Constant, val, true);
|
||||||
self.inc_operations(state, *pos)?;
|
self.inc_operations(state, *pos)?;
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
@ -1827,7 +1823,7 @@ impl Engine {
|
|||||||
let module =
|
let module =
|
||||||
resolver.resolve(self, Scope::new(), &path, expr.position())?;
|
resolver.resolve(self, Scope::new(), &path, expr.position())?;
|
||||||
|
|
||||||
let mod_name = unsafe_cast_var_name(name, &state);
|
let mod_name = unsafe_cast_var_name_to_lifetime(name, &state);
|
||||||
scope.push_module(mod_name, module);
|
scope.push_module(mod_name, module);
|
||||||
|
|
||||||
state.modules += 1;
|
state.modules += 1;
|
||||||
@ -1848,7 +1844,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Export statement
|
// Export statement
|
||||||
Stmt::Export(list) => {
|
Stmt::Export(list) => {
|
||||||
for ((id, id_pos), rename) in list.as_ref() {
|
for ((id, id_pos), rename) in list.iter() {
|
||||||
// Mark scope variables as public
|
// Mark scope variables as public
|
||||||
if let Some(index) = scope
|
if let Some(index) = scope
|
||||||
.get_index(id)
|
.get_index(id)
|
||||||
|
@ -9,7 +9,7 @@ use crate::utils::StaticVec;
|
|||||||
/// Any data type that can be converted into a `Vec<Dynamic>` can be used
|
/// Any data type that can be converted into a `Vec<Dynamic>` can be used
|
||||||
/// as arguments to a function call.
|
/// as arguments to a function call.
|
||||||
pub trait FuncArgs {
|
pub trait FuncArgs {
|
||||||
/// Convert to a `Vec<Dynamic>` of the function call arguments.
|
/// Convert to a `StaticVec<Dynamic>` of the function call arguments.
|
||||||
fn into_vec(self) -> StaticVec<Dynamic>;
|
fn into_vec(self) -> StaticVec<Dynamic>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +91,6 @@ mod utils;
|
|||||||
pub use any::Dynamic;
|
pub use any::Dynamic;
|
||||||
pub use engine::Engine;
|
pub use engine::Engine;
|
||||||
pub use error::{ParseError, ParseErrorType};
|
pub use error::{ParseError, ParseErrorType};
|
||||||
pub use fn_call::FuncArgs;
|
|
||||||
pub use fn_native::NativeCallable;
|
pub use fn_native::NativeCallable;
|
||||||
pub use fn_register::{RegisterDynamicFn, RegisterFn, RegisterResultFn};
|
pub use fn_register::{RegisterDynamicFn, RegisterFn, RegisterResultFn};
|
||||||
pub use module::Module;
|
pub use module::Module;
|
||||||
|
@ -716,7 +716,7 @@ impl Module {
|
|||||||
///
|
///
|
||||||
/// A `StaticVec` is used because most module-level access contains only one level,
|
/// A `StaticVec` is used because most module-level access contains only one level,
|
||||||
/// and it is wasteful to always allocate a `Vec` with one element.
|
/// and it is wasteful to always allocate a `Vec` with one element.
|
||||||
#[derive(Clone, Eq, PartialEq, Hash, Default)]
|
#[derive(Clone, Eq, PartialEq, Default)]
|
||||||
pub struct ModuleRef(StaticVec<(String, Position)>, Option<NonZeroUsize>);
|
pub struct ModuleRef(StaticVec<(String, Position)>, Option<NonZeroUsize>);
|
||||||
|
|
||||||
impl fmt::Debug for ModuleRef {
|
impl fmt::Debug for ModuleRef {
|
||||||
|
@ -10,10 +10,12 @@ use crate::parser::{map_dynamic_to_expr, Expr, FnDef, ReturnType, Stmt, AST};
|
|||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
|
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
|
||||||
use crate::token::Position;
|
use crate::token::Position;
|
||||||
|
use crate::utils::StaticVec;
|
||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
iter::empty,
|
iter::empty,
|
||||||
|
mem,
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
vec,
|
vec,
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
@ -140,7 +142,11 @@ fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -
|
|||||||
|
|
||||||
if preserve_result {
|
if preserve_result {
|
||||||
// -> { expr, Noop }
|
// -> { expr, Noop }
|
||||||
Stmt::Block(Box::new((vec![Stmt::Expr(Box::new(expr)), x.1], pos)))
|
let mut statements = StaticVec::new();
|
||||||
|
statements.push(Stmt::Expr(Box::new(expr)));
|
||||||
|
statements.push(x.1);
|
||||||
|
|
||||||
|
Stmt::Block(Box::new((statements, pos)))
|
||||||
} else {
|
} else {
|
||||||
// -> expr
|
// -> expr
|
||||||
Stmt::Expr(Box::new(expr))
|
Stmt::Expr(Box::new(expr))
|
||||||
@ -193,7 +199,8 @@ fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -
|
|||||||
Stmt::Break(pos) => {
|
Stmt::Break(pos) => {
|
||||||
// Only a single break statement - turn into running the guard expression once
|
// Only a single break statement - turn into running the guard expression once
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
let mut statements = vec![Stmt::Expr(Box::new(optimize_expr(expr, state)))];
|
let mut statements = StaticVec::new();
|
||||||
|
statements.push(Stmt::Expr(Box::new(optimize_expr(expr, state))));
|
||||||
if preserve_result {
|
if preserve_result {
|
||||||
statements.push(Stmt::Noop(pos))
|
statements.push(Stmt::Noop(pos))
|
||||||
}
|
}
|
||||||
@ -229,24 +236,24 @@ fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -
|
|||||||
// import expr as id;
|
// import expr as id;
|
||||||
Stmt::Import(x) => Stmt::Import(Box::new((optimize_expr(x.0, state), x.1))),
|
Stmt::Import(x) => Stmt::Import(Box::new((optimize_expr(x.0, state), x.1))),
|
||||||
// { block }
|
// { block }
|
||||||
Stmt::Block(x) => {
|
Stmt::Block(mut x) => {
|
||||||
let orig_len = x.0.len(); // Original number of statements in the block, for change detection
|
let orig_len = x.0.len(); // Original number of statements in the block, for change detection
|
||||||
let orig_constants_len = state.constants.len(); // Original number of constants in the state, for restore later
|
let orig_constants_len = state.constants.len(); // Original number of constants in the state, for restore later
|
||||||
let pos = x.1;
|
let pos = x.1;
|
||||||
|
|
||||||
// Optimize each statement in the block
|
// Optimize each statement in the block
|
||||||
let mut result: Vec<_> =
|
let mut result: Vec<_> =
|
||||||
x.0.into_iter()
|
x.0.iter_mut()
|
||||||
.map(|stmt| match stmt {
|
.map(|stmt| match stmt {
|
||||||
// Add constant into the state
|
// Add constant into the state
|
||||||
Stmt::Const(v) => {
|
Stmt::Const(v) => {
|
||||||
let ((name, pos), expr) = *v;
|
let ((name, pos), expr) = v.as_mut();
|
||||||
state.push_constant(&name, expr);
|
state.push_constant(name, mem::take(expr));
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
Stmt::Noop(pos) // No need to keep constants
|
Stmt::Noop(*pos) // No need to keep constants
|
||||||
}
|
}
|
||||||
// Optimize the statement
|
// Optimize the statement
|
||||||
_ => optimize_stmt(stmt, state, preserve_result),
|
_ => optimize_stmt(mem::take(stmt), state, preserve_result),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@ -324,13 +331,13 @@ fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -
|
|||||||
Stmt::Noop(pos)
|
Stmt::Noop(pos)
|
||||||
}
|
}
|
||||||
// Only one let/import statement - leave it alone
|
// Only one let/import statement - leave it alone
|
||||||
[Stmt::Let(_)] | [Stmt::Import(_)] => Stmt::Block(Box::new((result, pos))),
|
[Stmt::Let(_)] | [Stmt::Import(_)] => Stmt::Block(Box::new((result.into(), pos))),
|
||||||
// Only one statement - promote
|
// Only one statement - promote
|
||||||
[_] => {
|
[_] => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
result.remove(0)
|
result.remove(0)
|
||||||
}
|
}
|
||||||
_ => Stmt::Block(Box::new((result, pos))),
|
_ => Stmt::Block(Box::new((result.into(), pos))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// expr;
|
// expr;
|
||||||
@ -392,14 +399,14 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Dot(x) => match (x.0, x.1) {
|
Expr::Dot(x) => match (x.0, x.1) {
|
||||||
// map.string
|
// map.string
|
||||||
(Expr::Map(m), Expr::Property(p)) if m.0.iter().all(|(_, x)| x.is_pure()) => {
|
(Expr::Map(mut m), Expr::Property(p)) if m.0.iter().all(|(_, x)| x.is_pure()) => {
|
||||||
let ((prop, _, _), _) = p.as_ref();
|
let ((prop, _, _), _) = p.as_ref();
|
||||||
// Map literal where everything is pure - promote the indexed item.
|
// Map literal where everything is pure - promote the indexed item.
|
||||||
// All other items can be thrown away.
|
// All other items can be thrown away.
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
let pos = m.1;
|
let pos = m.1;
|
||||||
m.0.into_iter().find(|((name, _), _)| name == prop)
|
m.0.iter_mut().find(|((name, _), _)| name == prop)
|
||||||
.map(|(_, expr)| expr.set_position(pos))
|
.map(|(_, expr)| mem::take(expr).set_position(pos))
|
||||||
.unwrap_or_else(|| Expr::Unit(pos))
|
.unwrap_or_else(|| Expr::Unit(pos))
|
||||||
}
|
}
|
||||||
// lhs.rhs
|
// lhs.rhs
|
||||||
@ -416,16 +423,16 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
// Array literal where everything is pure - promote the indexed item.
|
// Array literal where everything is pure - promote the indexed item.
|
||||||
// All other items can be thrown away.
|
// All other items can be thrown away.
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
a.0.remove(i.0 as usize).set_position(a.1)
|
a.0.get(i.0 as usize).set_position(a.1)
|
||||||
}
|
}
|
||||||
// map[string]
|
// map[string]
|
||||||
(Expr::Map(m), Expr::StringConstant(s)) if m.0.iter().all(|(_, x)| x.is_pure()) => {
|
(Expr::Map(mut m), Expr::StringConstant(s)) if m.0.iter().all(|(_, x)| x.is_pure()) => {
|
||||||
// Map literal where everything is pure - promote the indexed item.
|
// Map literal where everything is pure - promote the indexed item.
|
||||||
// All other items can be thrown away.
|
// All other items can be thrown away.
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
let pos = m.1;
|
let pos = m.1;
|
||||||
m.0.into_iter().find(|((name, _), _)| name == &s.0)
|
m.0.iter_mut().find(|((name, _), _)| name == &s.0)
|
||||||
.map(|(_, expr)| expr.set_position(pos))
|
.map(|(_, expr)| mem::take(expr).set_position(pos))
|
||||||
.unwrap_or_else(|| Expr::Unit(pos))
|
.unwrap_or_else(|| Expr::Unit(pos))
|
||||||
}
|
}
|
||||||
// string[int]
|
// string[int]
|
||||||
@ -439,15 +446,13 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
},
|
},
|
||||||
// [ items .. ]
|
// [ items .. ]
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Array(a) => Expr::Array(Box::new((a.0
|
Expr::Array(mut a) => Expr::Array(Box::new((a.0
|
||||||
.into_iter()
|
.iter_mut().map(|expr| optimize_expr(mem::take(expr), state))
|
||||||
.map(|expr| optimize_expr(expr, state))
|
.collect(), a.1))),
|
||||||
.collect(), a.1))),
|
|
||||||
// [ items .. ]
|
// [ items .. ]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Map(m) => Expr::Map(Box::new((m.0
|
Expr::Map(mut m) => Expr::Map(Box::new((m.0
|
||||||
.into_iter()
|
.iter_mut().map(|((key, pos), expr)| ((mem::take(key), *pos), optimize_expr(mem::take(expr), state)))
|
||||||
.map(|((key, pos), expr)| ((key, pos), optimize_expr(expr, state)))
|
|
||||||
.collect(), m.1))),
|
.collect(), m.1))),
|
||||||
// lhs in rhs
|
// lhs in rhs
|
||||||
Expr::In(x) => match (x.0, x.1) {
|
Expr::In(x) => match (x.0, x.1) {
|
||||||
@ -527,7 +532,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
|
|
||||||
// Do not call some special keywords
|
// Do not call some special keywords
|
||||||
Expr::FnCall(mut x) if DONT_EVAL_KEYWORDS.contains(&(x.0).0.as_ref())=> {
|
Expr::FnCall(mut x) if DONT_EVAL_KEYWORDS.contains(&(x.0).0.as_ref())=> {
|
||||||
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
|
x.3 = x.3.iter_mut().map(|a| optimize_expr(mem::take(a), state)).collect();
|
||||||
Expr::FnCall(x)
|
Expr::FnCall(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -542,7 +547,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
// First search in script-defined functions (can override built-in)
|
// First search in script-defined functions (can override built-in)
|
||||||
if state.fn_lib.iter().find(|(id, len)| *id == name && *len == args.len()).is_some() {
|
if state.fn_lib.iter().find(|(id, len)| *id == name && *len == args.len()).is_some() {
|
||||||
// A script-defined function overrides the built-in function - do not make the call
|
// A script-defined function overrides the built-in function - do not make the call
|
||||||
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
|
x.3 = x.3.iter_mut().map(|a| optimize_expr(mem::take(a), state)).collect();
|
||||||
return Expr::FnCall(x);
|
return Expr::FnCall(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -574,14 +579,14 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
})
|
})
|
||||||
).unwrap_or_else(|| {
|
).unwrap_or_else(|| {
|
||||||
// Optimize function call arguments
|
// Optimize function call arguments
|
||||||
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
|
x.3 = x.3.iter_mut().map(|a| optimize_expr(mem::take(a), state)).collect();
|
||||||
Expr::FnCall(x)
|
Expr::FnCall(x)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// id(args ..) -> optimize function call arguments
|
// id(args ..) -> optimize function call arguments
|
||||||
Expr::FnCall(mut x) => {
|
Expr::FnCall(mut x) => {
|
||||||
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
|
x.3 = x.3.iter_mut().map(|a| optimize_expr(mem::take(a), state)).collect();
|
||||||
Expr::FnCall(x)
|
Expr::FnCall(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use crate::fn_native::{NativeCallable, SharedIteratorFunction};
|
use crate::fn_native::{NativeCallable, SharedIteratorFunction};
|
||||||
use crate::module::Module;
|
use crate::module::Module;
|
||||||
|
use crate::utils::StaticVec;
|
||||||
|
|
||||||
use crate::stdlib::{any::TypeId, boxed::Box, collections::HashMap, rc::Rc, sync::Arc, vec::Vec};
|
use crate::stdlib::{any::TypeId, boxed::Box, collections::HashMap, rc::Rc, sync::Arc, vec::Vec};
|
||||||
|
|
||||||
@ -54,7 +55,7 @@ pub type PackageLibrary = Arc<Module>;
|
|||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub(crate) struct PackagesCollection {
|
pub(crate) struct PackagesCollection {
|
||||||
/// Collection of `PackageLibrary` instances.
|
/// Collection of `PackageLibrary` instances.
|
||||||
packages: Vec<PackageLibrary>,
|
packages: StaticVec<PackageLibrary>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PackagesCollection {
|
impl PackagesCollection {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::def_package;
|
use crate::def_package;
|
||||||
use crate::module::FuncReturn;
|
use crate::module::FuncReturn;
|
||||||
use crate::parser::INT;
|
use crate::parser::INT;
|
||||||
|
use crate::utils::StaticVec;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
use crate::engine::Array;
|
use crate::engine::Array;
|
||||||
@ -29,7 +30,7 @@ fn sub_string(s: &mut String, start: INT, len: INT) -> FuncReturn<String> {
|
|||||||
start as usize
|
start as usize
|
||||||
};
|
};
|
||||||
|
|
||||||
let chars: Vec<_> = s.chars().collect();
|
let chars: StaticVec<_> = s.chars().collect();
|
||||||
|
|
||||||
let len = if offset + (len as usize) > chars.len() {
|
let len = if offset + (len as usize) > chars.len() {
|
||||||
chars.len() - offset
|
chars.len() - offset
|
||||||
@ -37,7 +38,7 @@ fn sub_string(s: &mut String, start: INT, len: INT) -> FuncReturn<String> {
|
|||||||
len as usize
|
len as usize
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(chars[offset..][..len].into_iter().collect())
|
Ok(chars.iter().skip(offset).take(len).cloned().collect())
|
||||||
}
|
}
|
||||||
fn crop_string(s: &mut String, start: INT, len: INT) -> FuncReturn<()> {
|
fn crop_string(s: &mut String, start: INT, len: INT) -> FuncReturn<()> {
|
||||||
let offset = if s.is_empty() || len <= 0 {
|
let offset = if s.is_empty() || len <= 0 {
|
||||||
@ -52,7 +53,7 @@ fn crop_string(s: &mut String, start: INT, len: INT) -> FuncReturn<()> {
|
|||||||
start as usize
|
start as usize
|
||||||
};
|
};
|
||||||
|
|
||||||
let chars: Vec<_> = s.chars().collect();
|
let chars: StaticVec<_> = s.chars().collect();
|
||||||
|
|
||||||
let len = if offset + (len as usize) > chars.len() {
|
let len = if offset + (len as usize) > chars.len() {
|
||||||
chars.len() - offset
|
chars.len() - offset
|
||||||
@ -62,8 +63,10 @@ fn crop_string(s: &mut String, start: INT, len: INT) -> FuncReturn<()> {
|
|||||||
|
|
||||||
s.clear();
|
s.clear();
|
||||||
|
|
||||||
chars[offset..][..len]
|
chars
|
||||||
.into_iter()
|
.iter()
|
||||||
|
.skip(offset)
|
||||||
|
.take(len)
|
||||||
.for_each(|&ch| s.push(ch));
|
.for_each(|&ch| s.push(ch));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -189,9 +192,9 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str
|
|||||||
"truncate",
|
"truncate",
|
||||||
|s: &mut String, len: INT| {
|
|s: &mut String, len: INT| {
|
||||||
if len >= 0 {
|
if len >= 0 {
|
||||||
let chars: Vec<_> = s.chars().take(len as usize).collect();
|
let chars: StaticVec<_> = s.chars().take(len as usize).collect();
|
||||||
s.clear();
|
s.clear();
|
||||||
chars.into_iter().for_each(|ch| s.push(ch));
|
chars.iter().for_each(|&ch| s.push(ch));
|
||||||
} else {
|
} else {
|
||||||
s.clear();
|
s.clear();
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ use crate::error::{LexError, ParseError, ParseErrorType};
|
|||||||
use crate::optimize::{optimize_into_ast, OptimizationLevel};
|
use crate::optimize::{optimize_into_ast, OptimizationLevel};
|
||||||
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
||||||
use crate::token::{Position, Token, TokenIterator};
|
use crate::token::{Position, Token, TokenIterator};
|
||||||
use crate::utils::EMPTY_TYPE_ID;
|
use crate::utils::{StaticVec, EMPTY_TYPE_ID};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
use crate::module::ModuleRef;
|
use crate::module::ModuleRef;
|
||||||
@ -195,7 +195,7 @@ pub struct FnDef {
|
|||||||
/// Function access mode.
|
/// Function access mode.
|
||||||
pub access: FnAccess,
|
pub access: FnAccess,
|
||||||
/// Names of function parameters.
|
/// Names of function parameters.
|
||||||
pub params: Vec<String>,
|
pub params: StaticVec<String>,
|
||||||
/// Function body.
|
/// Function body.
|
||||||
pub body: Stmt,
|
pub body: Stmt,
|
||||||
/// Position of the function definition.
|
/// Position of the function definition.
|
||||||
@ -294,7 +294,7 @@ pub enum Stmt {
|
|||||||
/// const id = expr
|
/// const id = expr
|
||||||
Const(Box<((String, Position), Expr)>),
|
Const(Box<((String, Position), Expr)>),
|
||||||
/// { stmt; ... }
|
/// { stmt; ... }
|
||||||
Block(Box<(Vec<Stmt>, Position)>),
|
Block(Box<(StaticVec<Stmt>, Position)>),
|
||||||
/// { stmt }
|
/// { stmt }
|
||||||
Expr(Box<Expr>),
|
Expr(Box<Expr>),
|
||||||
/// continue
|
/// continue
|
||||||
@ -306,7 +306,13 @@ pub enum Stmt {
|
|||||||
/// import expr as module
|
/// import expr as module
|
||||||
Import(Box<(Expr, (String, Position))>),
|
Import(Box<(Expr, (String, Position))>),
|
||||||
/// expr id as name, ...
|
/// expr id as name, ...
|
||||||
Export(Box<Vec<((String, Position), Option<(String, Position)>)>>),
|
Export(Box<StaticVec<((String, Position), Option<(String, Position)>)>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Stmt {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Noop(Default::default())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stmt {
|
impl Stmt {
|
||||||
@ -324,7 +330,7 @@ impl Stmt {
|
|||||||
Stmt::Loop(x) => x.position(),
|
Stmt::Loop(x) => x.position(),
|
||||||
Stmt::For(x) => x.2.position(),
|
Stmt::For(x) => x.2.position(),
|
||||||
Stmt::Import(x) => (x.1).1,
|
Stmt::Import(x) => (x.1).1,
|
||||||
Stmt::Export(x) => (x.get(0).unwrap().0).1,
|
Stmt::Export(x) => (x.get_ref(0).0).1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,7 +412,7 @@ pub enum Expr {
|
|||||||
(Cow<'static, str>, Position),
|
(Cow<'static, str>, Position),
|
||||||
MRef,
|
MRef,
|
||||||
u64,
|
u64,
|
||||||
Vec<Expr>,
|
StaticVec<Expr>,
|
||||||
Option<Dynamic>,
|
Option<Dynamic>,
|
||||||
)>,
|
)>,
|
||||||
),
|
),
|
||||||
@ -417,9 +423,9 @@ pub enum Expr {
|
|||||||
/// expr[expr]
|
/// expr[expr]
|
||||||
Index(Box<(Expr, Expr, Position)>),
|
Index(Box<(Expr, Expr, Position)>),
|
||||||
/// [ expr, ... ]
|
/// [ expr, ... ]
|
||||||
Array(Box<(Vec<Expr>, Position)>),
|
Array(Box<(StaticVec<Expr>, Position)>),
|
||||||
/// #{ name:expr, ... }
|
/// #{ name:expr, ... }
|
||||||
Map(Box<(Vec<((String, Position), Expr)>, Position)>),
|
Map(Box<(StaticVec<((String, Position), Expr)>, Position)>),
|
||||||
/// lhs in rhs
|
/// lhs in rhs
|
||||||
In(Box<(Expr, Expr, Position)>),
|
In(Box<(Expr, Expr, Position)>),
|
||||||
/// lhs && rhs
|
/// lhs && rhs
|
||||||
@ -434,6 +440,12 @@ pub enum Expr {
|
|||||||
Unit(Position),
|
Unit(Position),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Expr {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Unit(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Expr {
|
impl Expr {
|
||||||
/// Get the `Dynamic` value of a constant expression.
|
/// Get the `Dynamic` value of a constant expression.
|
||||||
///
|
///
|
||||||
@ -713,7 +725,7 @@ fn parse_call_expr<'a>(
|
|||||||
begin: Position,
|
begin: Position,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Expr, Box<ParseError>> {
|
) -> Result<Expr, Box<ParseError>> {
|
||||||
let mut args = Vec::new();
|
let mut args = StaticVec::new();
|
||||||
|
|
||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
// id <EOF>
|
// id <EOF>
|
||||||
@ -1013,7 +1025,7 @@ fn parse_array_literal<'a>(
|
|||||||
pos: Position,
|
pos: Position,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Expr, Box<ParseError>> {
|
) -> Result<Expr, Box<ParseError>> {
|
||||||
let mut arr = Vec::new();
|
let mut arr = StaticVec::new();
|
||||||
|
|
||||||
if !match_token(input, Token::RightBracket)? {
|
if !match_token(input, Token::RightBracket)? {
|
||||||
while !input.peek().unwrap().0.is_eof() {
|
while !input.peek().unwrap().0.is_eof() {
|
||||||
@ -1056,7 +1068,7 @@ fn parse_map_literal<'a>(
|
|||||||
pos: Position,
|
pos: Position,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Expr, Box<ParseError>> {
|
) -> Result<Expr, Box<ParseError>> {
|
||||||
let mut map = Vec::new();
|
let mut map = StaticVec::new();
|
||||||
|
|
||||||
if !match_token(input, Token::RightBrace)? {
|
if !match_token(input, Token::RightBrace)? {
|
||||||
while !input.peek().unwrap().0.is_eof() {
|
while !input.peek().unwrap().0.is_eof() {
|
||||||
@ -1296,15 +1308,17 @@ fn parse_unary<'a>(
|
|||||||
Expr::FloatConstant(x) => Ok(Expr::FloatConstant(Box::new((-x.0, x.1)))),
|
Expr::FloatConstant(x) => Ok(Expr::FloatConstant(Box::new((-x.0, x.1)))),
|
||||||
|
|
||||||
// Call negative function
|
// Call negative function
|
||||||
e => {
|
expr => {
|
||||||
let op = "-";
|
let op = "-";
|
||||||
let hash = calc_fn_hash(empty(), op, repeat(EMPTY_TYPE_ID()).take(2));
|
let hash = calc_fn_hash(empty(), op, repeat(EMPTY_TYPE_ID()).take(2));
|
||||||
|
let mut args = StaticVec::new();
|
||||||
|
args.push(expr);
|
||||||
|
|
||||||
Ok(Expr::FnCall(Box::new((
|
Ok(Expr::FnCall(Box::new((
|
||||||
(op.into(), pos),
|
(op.into(), pos),
|
||||||
None,
|
None,
|
||||||
hash,
|
hash,
|
||||||
vec![e],
|
args,
|
||||||
None,
|
None,
|
||||||
))))
|
))))
|
||||||
}
|
}
|
||||||
@ -1318,7 +1332,8 @@ fn parse_unary<'a>(
|
|||||||
// !expr
|
// !expr
|
||||||
(Token::Bang, _) => {
|
(Token::Bang, _) => {
|
||||||
let pos = eat_token(input, Token::Bang);
|
let pos = eat_token(input, Token::Bang);
|
||||||
let expr = vec![parse_primary(input, stack, allow_stmt_expr)?];
|
let mut args = StaticVec::new();
|
||||||
|
args.push(parse_primary(input, stack, allow_stmt_expr)?);
|
||||||
|
|
||||||
let op = "!";
|
let op = "!";
|
||||||
let hash = calc_fn_hash(empty(), op, repeat(EMPTY_TYPE_ID()).take(2));
|
let hash = calc_fn_hash(empty(), op, repeat(EMPTY_TYPE_ID()).take(2));
|
||||||
@ -1327,7 +1342,7 @@ fn parse_unary<'a>(
|
|||||||
(op.into(), pos),
|
(op.into(), pos),
|
||||||
None,
|
None,
|
||||||
hash,
|
hash,
|
||||||
expr,
|
args,
|
||||||
Some(false.into()), // NOT operator, when operating on invalid operand, defaults to false
|
Some(false.into()), // NOT operator, when operating on invalid operand, defaults to false
|
||||||
))))
|
))))
|
||||||
}
|
}
|
||||||
@ -1412,9 +1427,13 @@ fn parse_op_assignment_stmt<'a>(
|
|||||||
let rhs = parse_expr(input, stack, allow_stmt_expr)?;
|
let rhs = parse_expr(input, stack, allow_stmt_expr)?;
|
||||||
|
|
||||||
// lhs op= rhs -> lhs = op(lhs, rhs)
|
// lhs op= rhs -> lhs = op(lhs, rhs)
|
||||||
let args = vec![lhs_copy, rhs];
|
let mut args = StaticVec::new();
|
||||||
|
args.push(lhs_copy);
|
||||||
|
args.push(rhs);
|
||||||
|
|
||||||
let hash = calc_fn_hash(empty(), op, repeat(EMPTY_TYPE_ID()).take(args.len()));
|
let hash = calc_fn_hash(empty(), op, repeat(EMPTY_TYPE_ID()).take(args.len()));
|
||||||
let rhs_expr = Expr::FnCall(Box::new(((op.into(), pos), None, hash, args, None)));
|
let rhs_expr = Expr::FnCall(Box::new(((op.into(), pos), None, hash, args, None)));
|
||||||
|
|
||||||
make_assignment_stmt(stack, lhs, rhs_expr, pos)
|
make_assignment_stmt(stack, lhs, rhs_expr, pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1695,7 +1714,10 @@ fn parse_binary_op<'a>(
|
|||||||
let cmp_def = Some(false.into());
|
let cmp_def = Some(false.into());
|
||||||
let op = op_token.syntax();
|
let op = op_token.syntax();
|
||||||
let hash = calc_fn_hash(empty(), &op, repeat(EMPTY_TYPE_ID()).take(2));
|
let hash = calc_fn_hash(empty(), &op, repeat(EMPTY_TYPE_ID()).take(2));
|
||||||
let mut args = vec![current_lhs, rhs];
|
|
||||||
|
let mut args = StaticVec::new();
|
||||||
|
args.push(current_lhs);
|
||||||
|
args.push(rhs);
|
||||||
|
|
||||||
current_lhs = match op_token {
|
current_lhs = match op_token {
|
||||||
Token::Plus => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))),
|
Token::Plus => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))),
|
||||||
@ -1721,13 +1743,13 @@ fn parse_binary_op<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Token::Or => {
|
Token::Or => {
|
||||||
let rhs = args.pop().unwrap();
|
let rhs = args.pop();
|
||||||
let current_lhs = args.pop().unwrap();
|
let current_lhs = args.pop();
|
||||||
Expr::Or(Box::new((current_lhs, rhs, pos)))
|
Expr::Or(Box::new((current_lhs, rhs, pos)))
|
||||||
}
|
}
|
||||||
Token::And => {
|
Token::And => {
|
||||||
let rhs = args.pop().unwrap();
|
let rhs = args.pop();
|
||||||
let current_lhs = args.pop().unwrap();
|
let current_lhs = args.pop();
|
||||||
Expr::And(Box::new((current_lhs, rhs, pos)))
|
Expr::And(Box::new((current_lhs, rhs, pos)))
|
||||||
}
|
}
|
||||||
Token::Ampersand => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))),
|
Token::Ampersand => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))),
|
||||||
@ -1735,15 +1757,15 @@ fn parse_binary_op<'a>(
|
|||||||
Token::XOr => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))),
|
Token::XOr => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))),
|
||||||
|
|
||||||
Token::In => {
|
Token::In => {
|
||||||
let rhs = args.pop().unwrap();
|
let rhs = args.pop();
|
||||||
let current_lhs = args.pop().unwrap();
|
let current_lhs = args.pop();
|
||||||
make_in_expr(current_lhs, rhs, pos)?
|
make_in_expr(current_lhs, rhs, pos)?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Token::Period => {
|
Token::Period => {
|
||||||
let mut rhs = args.pop().unwrap();
|
let mut rhs = args.pop();
|
||||||
let current_lhs = args.pop().unwrap();
|
let current_lhs = args.pop();
|
||||||
|
|
||||||
match &mut rhs {
|
match &mut rhs {
|
||||||
// current_lhs.rhs(...) - method call
|
// current_lhs.rhs(...) - method call
|
||||||
@ -2025,7 +2047,7 @@ fn parse_import<'a>(
|
|||||||
fn parse_export<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, Box<ParseError>> {
|
fn parse_export<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, Box<ParseError>> {
|
||||||
eat_token(input, Token::Export);
|
eat_token(input, Token::Export);
|
||||||
|
|
||||||
let mut exports = Vec::new();
|
let mut exports = StaticVec::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let (id, id_pos) = match input.next().unwrap() {
|
let (id, id_pos) = match input.next().unwrap() {
|
||||||
@ -2098,7 +2120,7 @@ fn parse_block<'a>(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut statements = Vec::new();
|
let mut statements = StaticVec::new();
|
||||||
let prev_len = stack.len();
|
let prev_len = stack.len();
|
||||||
|
|
||||||
while !match_token(input, Token::RightBrace)? {
|
while !match_token(input, Token::RightBrace)? {
|
||||||
|
@ -47,9 +47,9 @@ pub fn unsafe_cast_box<X: Variant, T: Variant>(item: Box<X>) -> Result<Box<T>, B
|
|||||||
/// current `Scope` without cloning the variable name. Doing this is safe because all local
|
/// current `Scope` without cloning the variable name. Doing this is safe because all local
|
||||||
/// variables in the `Scope` are cleared out before existing the block.
|
/// variables in the `Scope` are cleared out before existing the block.
|
||||||
///
|
///
|
||||||
/// Force-casting a local variable lifetime to the current `Scope`'s larger lifetime saves
|
/// Force-casting a local variable's lifetime to the current `Scope`'s larger lifetime saves
|
||||||
/// on allocations and string cloning, thus avoids us having to maintain a chain of `Scope`'s.
|
/// on allocations and string cloning, thus avoids us having to maintain a chain of `Scope`'s.
|
||||||
pub fn unsafe_cast_var_name<'s>(name: &str, state: &State) -> Cow<'s, str> {
|
pub fn unsafe_cast_var_name_to_lifetime<'s>(name: &str, state: &State) -> Cow<'s, str> {
|
||||||
// If not at global level, we can force-cast
|
// If not at global level, we can force-cast
|
||||||
if state.scope_level > 0 {
|
if state.scope_level > 0 {
|
||||||
// WARNING - force-cast the variable name into the scope's lifetime to avoid cloning it
|
// WARNING - force-cast the variable name into the scope's lifetime to avoid cloning it
|
||||||
@ -62,7 +62,7 @@ pub fn unsafe_cast_var_name<'s>(name: &str, state: &State) -> Cow<'s, str> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provide a type instance that is memory-zeroed.
|
/// Provide a type instance that is uninitialized.
|
||||||
pub fn unsafe_zeroed<T>() -> T {
|
pub fn unsafe_uninit<T>() -> T {
|
||||||
unsafe { mem::MaybeUninit::zeroed().assume_init() }
|
unsafe { mem::MaybeUninit::uninit().assume_init() }
|
||||||
}
|
}
|
||||||
|
250
src/utils.rs
250
src/utils.rs
@ -1,6 +1,10 @@
|
|||||||
//! Module containing various utility types and functions.
|
//! Module containing various utility types and functions.
|
||||||
|
//!
|
||||||
|
//! # Safety
|
||||||
|
//!
|
||||||
|
//! The `StaticVec` type has some `unsafe` blocks.
|
||||||
|
|
||||||
use crate::r#unsafe::unsafe_zeroed;
|
use crate::r#unsafe::unsafe_uninit;
|
||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
@ -8,6 +12,7 @@ use crate::stdlib::{
|
|||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
iter::FromIterator,
|
iter::FromIterator,
|
||||||
mem,
|
mem,
|
||||||
|
ops::Drop,
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -47,6 +52,8 @@ pub fn calc_fn_spec<'a>(
|
|||||||
s.finish()
|
s.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MAX_STATIC_VEC: usize = 4;
|
||||||
|
|
||||||
/// A type to hold a number of values in static storage for speed, and any spill-overs in a `Vec`.
|
/// A type to hold a number of values in static storage for speed, and any spill-overs in a `Vec`.
|
||||||
///
|
///
|
||||||
/// This is essentially a knock-off of the [`staticvec`](https://crates.io/crates/staticvec) crate.
|
/// This is essentially a knock-off of the [`staticvec`](https://crates.io/crates/staticvec) crate.
|
||||||
@ -54,22 +61,57 @@ pub fn calc_fn_spec<'a>(
|
|||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// This type uses some unsafe code (mainly to zero out unused array slots) for efficiency.
|
/// This type uses some unsafe code (mainly for uninitialized/unused array slots) for efficiency.
|
||||||
//
|
//
|
||||||
// TODO - remove unsafe code
|
// TODO - remove unsafe code
|
||||||
#[derive(Clone, Hash)]
|
|
||||||
pub struct StaticVec<T> {
|
pub struct StaticVec<T> {
|
||||||
/// Total number of values held.
|
/// Total number of values held.
|
||||||
len: usize,
|
len: usize,
|
||||||
/// Static storage. 4 slots should be enough for most cases - i.e. four levels of indirection.
|
/// Static storage. 4 slots should be enough for most cases - i.e. four items of fast, no-allocation access.
|
||||||
list: [T; 4],
|
list: [mem::MaybeUninit<T>; MAX_STATIC_VEC],
|
||||||
/// Dynamic storage. For spill-overs.
|
/// Dynamic storage. For spill-overs.
|
||||||
more: Vec<T>,
|
more: Vec<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for StaticVec<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Default for StaticVec<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
len: 0,
|
||||||
|
list: unsafe_uninit(),
|
||||||
|
more: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: PartialEq> PartialEq for StaticVec<T> {
|
impl<T: PartialEq> PartialEq for StaticVec<T> {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.len == other.len && self.list == other.list && self.more == other.more
|
self.len == other.len
|
||||||
|
//&& self.list[0..self.len] == other.list[0..self.len]
|
||||||
|
&& self.more == other.more
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone> Clone for StaticVec<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
let mut value: Self = Default::default();
|
||||||
|
value.len = self.len;
|
||||||
|
|
||||||
|
if self.len <= self.list.len() {
|
||||||
|
for x in 0..self.len {
|
||||||
|
let item: &T = unsafe { mem::transmute(self.list.get(x).unwrap()) };
|
||||||
|
value.list[x] = mem::MaybeUninit::new(item.clone());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value.more = self.more.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,35 +129,75 @@ impl<T> FromIterator<T> for StaticVec<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Default for StaticVec<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
len: 0,
|
|
||||||
list: unsafe_zeroed(),
|
|
||||||
more: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> StaticVec<T> {
|
impl<T> StaticVec<T> {
|
||||||
|
fn extract(value: mem::MaybeUninit<T>) -> T {
|
||||||
|
unsafe { value.assume_init() }
|
||||||
|
}
|
||||||
/// Create a new `StaticVec`.
|
/// Create a new `StaticVec`.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
/// Empty the `StaticVec`.
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
if self.len <= self.list.len() {
|
||||||
|
for x in 0..self.len {
|
||||||
|
Self::extract(mem::replace(
|
||||||
|
self.list.get_mut(x).unwrap(),
|
||||||
|
mem::MaybeUninit::uninit(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.more.clear();
|
||||||
|
}
|
||||||
|
self.len = 0;
|
||||||
|
}
|
||||||
/// Push a new value to the end of this `StaticVec`.
|
/// Push a new value to the end of this `StaticVec`.
|
||||||
pub fn push<X: Into<T>>(&mut self, value: X) {
|
pub fn push<X: Into<T>>(&mut self, value: X) {
|
||||||
if self.len == self.list.len() {
|
if self.len == self.list.len() {
|
||||||
// Move the fixed list to the Vec
|
// Move the fixed list to the Vec
|
||||||
for x in 0..self.list.len() {
|
self.more.extend(
|
||||||
let def_val: T = unsafe_zeroed();
|
self.list
|
||||||
self.more
|
.iter_mut()
|
||||||
.push(mem::replace(self.list.get_mut(x).unwrap(), def_val));
|
.map(|v| mem::replace(v, mem::MaybeUninit::uninit()))
|
||||||
}
|
.map(Self::extract),
|
||||||
self.more.push(value.into());
|
);
|
||||||
} else if self.len > self.list.len() {
|
|
||||||
self.more.push(value.into());
|
self.more.push(value.into());
|
||||||
|
} else if self.len < self.list.len() {
|
||||||
|
mem::replace(
|
||||||
|
self.list.get_mut(self.len).unwrap(),
|
||||||
|
mem::MaybeUninit::new(value.into()),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
self.list[self.len] = value.into();
|
self.more.push(value.into());
|
||||||
|
}
|
||||||
|
self.len += 1;
|
||||||
|
}
|
||||||
|
/// Insert a new value to this `StaticVec` at a particular position.
|
||||||
|
pub fn insert<X: Into<T>>(&mut self, index: usize, value: X) {
|
||||||
|
if index > self.len {
|
||||||
|
panic!("index OOB in StaticVec");
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.len == self.list.len() {
|
||||||
|
// Move the fixed list to the Vec
|
||||||
|
self.more.extend(
|
||||||
|
self.list
|
||||||
|
.iter_mut()
|
||||||
|
.map(|v| mem::replace(v, mem::MaybeUninit::uninit()))
|
||||||
|
.map(Self::extract),
|
||||||
|
);
|
||||||
|
self.more.insert(index, value.into());
|
||||||
|
} else if self.len < self.list.len() {
|
||||||
|
for x in (index..self.len).rev() {
|
||||||
|
let temp = mem::replace(self.list.get_mut(x).unwrap(), mem::MaybeUninit::uninit());
|
||||||
|
mem::replace(self.list.get_mut(x + 1).unwrap(), temp);
|
||||||
|
}
|
||||||
|
mem::replace(
|
||||||
|
self.list.get_mut(index).unwrap(),
|
||||||
|
mem::MaybeUninit::new(value.into()),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
self.more.insert(index, value.into());
|
||||||
}
|
}
|
||||||
self.len += 1;
|
self.len += 1;
|
||||||
}
|
}
|
||||||
@ -125,18 +207,25 @@ impl<T> StaticVec<T> {
|
|||||||
///
|
///
|
||||||
/// Panics if the `StaticVec` is empty.
|
/// Panics if the `StaticVec` is empty.
|
||||||
pub fn pop(&mut self) -> T {
|
pub fn pop(&mut self) -> T {
|
||||||
let result = if self.len <= 0 {
|
if self.len <= 0 {
|
||||||
panic!("nothing to pop!")
|
panic!("nothing to pop!");
|
||||||
} else if self.len <= self.list.len() {
|
}
|
||||||
let def_val: T = unsafe_zeroed();
|
|
||||||
mem::replace(self.list.get_mut(self.len - 1).unwrap(), def_val)
|
let result = if self.len <= self.list.len() {
|
||||||
|
Self::extract(mem::replace(
|
||||||
|
self.list.get_mut(self.len - 1).unwrap(),
|
||||||
|
mem::MaybeUninit::uninit(),
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
let r = self.more.pop().unwrap();
|
let r = self.more.pop().unwrap();
|
||||||
|
|
||||||
// Move back to the fixed list
|
// Move back to the fixed list
|
||||||
if self.more.len() == self.list.len() {
|
if self.more.len() == self.list.len() {
|
||||||
for x in 0..self.list.len() {
|
for index in (0..self.list.len()).rev() {
|
||||||
self.list[self.list.len() - 1 - x] = self.more.pop().unwrap();
|
mem::replace(
|
||||||
|
self.list.get_mut(index).unwrap(),
|
||||||
|
mem::MaybeUninit::new(self.more.pop().unwrap()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,6 +240,10 @@ impl<T> StaticVec<T> {
|
|||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.len
|
self.len
|
||||||
}
|
}
|
||||||
|
/// Is this `StaticVec` empty?
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.len == 0
|
||||||
|
}
|
||||||
/// Get a reference to the item at a particular index.
|
/// Get a reference to the item at a particular index.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
@ -161,8 +254,10 @@ impl<T> StaticVec<T> {
|
|||||||
panic!("index OOB in StaticVec");
|
panic!("index OOB in StaticVec");
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.len < self.list.len() {
|
let list: &[T; MAX_STATIC_VEC] = unsafe { mem::transmute(&self.list) };
|
||||||
self.list.get(index).unwrap()
|
|
||||||
|
if self.len <= list.len() {
|
||||||
|
list.get(index).unwrap()
|
||||||
} else {
|
} else {
|
||||||
self.more.get(index).unwrap()
|
self.more.get(index).unwrap()
|
||||||
}
|
}
|
||||||
@ -177,46 +272,52 @@ impl<T> StaticVec<T> {
|
|||||||
panic!("index OOB in StaticVec");
|
panic!("index OOB in StaticVec");
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.len < self.list.len() {
|
let list: &mut [T; MAX_STATIC_VEC] = unsafe { mem::transmute(&mut self.list) };
|
||||||
self.list.get_mut(index).unwrap()
|
|
||||||
|
if self.len <= list.len() {
|
||||||
|
list.get_mut(index).unwrap()
|
||||||
} else {
|
} else {
|
||||||
self.more.get_mut(index).unwrap()
|
self.more.get_mut(index).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get an iterator to entries in the `StaticVec`.
|
/// Get an iterator to entries in the `StaticVec`.
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
||||||
if self.len > self.list.len() {
|
let list: &[T; MAX_STATIC_VEC] = unsafe { mem::transmute(&self.list) };
|
||||||
self.more.iter()
|
|
||||||
|
if self.len <= list.len() {
|
||||||
|
list[..self.len].iter()
|
||||||
} else {
|
} else {
|
||||||
self.list[..self.len].iter()
|
self.more.iter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get a mutable iterator to entries in the `StaticVec`.
|
/// Get a mutable iterator to entries in the `StaticVec`.
|
||||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
|
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
|
||||||
if self.len > self.list.len() {
|
let list: &mut [T; MAX_STATIC_VEC] = unsafe { mem::transmute(&mut self.list) };
|
||||||
self.more.iter_mut()
|
|
||||||
|
if self.len <= list.len() {
|
||||||
|
list[..self.len].iter_mut()
|
||||||
} else {
|
} else {
|
||||||
self.list[..self.len].iter_mut()
|
self.more.iter_mut()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Copy> StaticVec<T> {
|
impl<T: Default> StaticVec<T> {
|
||||||
/// Get the item at a particular index.
|
/// Get the item at a particular index, replacing it with the default.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if the index is out of bounds.
|
/// Panics if the index is out of bounds.
|
||||||
pub fn get(&self, index: usize) -> T {
|
pub fn get(&mut self, index: usize) -> T {
|
||||||
if index >= self.len {
|
if index >= self.len {
|
||||||
panic!("index OOB in StaticVec");
|
panic!("index OOB in StaticVec");
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.len < self.list.len() {
|
mem::take(if self.len <= self.list.len() {
|
||||||
*self.list.get(index).unwrap()
|
unsafe { mem::transmute(self.list.get_mut(index).unwrap()) }
|
||||||
} else {
|
} else {
|
||||||
*self.more.get(index).unwrap()
|
self.more.get_mut(index).unwrap()
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,20 +331,61 @@ impl<T: fmt::Debug> fmt::Debug for StaticVec<T> {
|
|||||||
|
|
||||||
impl<T> AsRef<[T]> for StaticVec<T> {
|
impl<T> AsRef<[T]> for StaticVec<T> {
|
||||||
fn as_ref(&self) -> &[T] {
|
fn as_ref(&self) -> &[T] {
|
||||||
if self.len > self.list.len() {
|
let list: &[T; MAX_STATIC_VEC] = unsafe { mem::transmute(&self.list) };
|
||||||
&self.more[..]
|
|
||||||
|
if self.len <= list.len() {
|
||||||
|
&list[..self.len]
|
||||||
} else {
|
} else {
|
||||||
&self.list[..self.len]
|
&self.more[..]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> AsMut<[T]> for StaticVec<T> {
|
impl<T> AsMut<[T]> for StaticVec<T> {
|
||||||
fn as_mut(&mut self) -> &mut [T] {
|
fn as_mut(&mut self) -> &mut [T] {
|
||||||
if self.len > self.list.len() {
|
let list: &mut [T; MAX_STATIC_VEC] = unsafe { mem::transmute(&mut self.list) };
|
||||||
&mut self.more[..]
|
|
||||||
|
if self.len <= list.len() {
|
||||||
|
&mut list[..self.len]
|
||||||
} else {
|
} else {
|
||||||
&mut self.list[..self.len]
|
&mut self.more[..]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> From<StaticVec<T>> for Vec<T> {
|
||||||
|
fn from(mut value: StaticVec<T>) -> Self {
|
||||||
|
if value.len <= value.list.len() {
|
||||||
|
value
|
||||||
|
.list
|
||||||
|
.iter_mut()
|
||||||
|
.map(|v| mem::replace(v, mem::MaybeUninit::uninit()))
|
||||||
|
.map(StaticVec::extract)
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
let mut arr = Self::new();
|
||||||
|
arr.append(&mut value.more);
|
||||||
|
arr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<Vec<T>> for StaticVec<T> {
|
||||||
|
fn from(mut value: Vec<T>) -> Self {
|
||||||
|
let mut arr: Self = Default::default();
|
||||||
|
arr.len = value.len();
|
||||||
|
|
||||||
|
if arr.len <= arr.list.len() {
|
||||||
|
for x in (0..arr.len).rev() {
|
||||||
|
mem::replace(
|
||||||
|
arr.list.get_mut(x).unwrap(),
|
||||||
|
mem::MaybeUninit::new(value.pop().unwrap()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
arr.more = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
arr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user