Merge empty strings.
This commit is contained in:
parent
65a1c24d7b
commit
3557db88e8
@ -1877,7 +1877,7 @@ impl Dynamic {
|
|||||||
_ => Err(self.type_name()),
|
_ => Err(self.type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Cast the [`Dynamic`] as an [`ImmutableString`] and return it.
|
/// Cast the [`Dynamic`] as an [`ImmutableString`] and return it as a string slice.
|
||||||
/// Returns the name of the actual type if the cast fails.
|
/// Returns the name of the actual type if the cast fails.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
@ -1888,7 +1888,7 @@ impl Dynamic {
|
|||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Str(ref s, _, _) => Ok(s),
|
Union::Str(ref s, _, _) => Ok(s),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(_, _, _) => panic!("as_str() cannot be called on shared values"),
|
Union::Shared(_, _, _) => panic!("as_str_ref() cannot be called on shared values"),
|
||||||
_ => Err(self.type_name()),
|
_ => Err(self.type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
use crate::ast::{Expr, FnCallExpr, Ident, OpAssignment, Stmt, AST_OPTION_FLAGS::*};
|
use crate::ast::{Expr, FnCallExpr, Ident, OpAssignment, Stmt, AST_OPTION_FLAGS::*};
|
||||||
use crate::custom_syntax::CustomSyntax;
|
use crate::custom_syntax::CustomSyntax;
|
||||||
use crate::dynamic::{map_std_type_name, AccessMode, Union, Variant};
|
use crate::dynamic::{map_std_type_name, AccessMode, Union, Variant};
|
||||||
use crate::fn_hash::get_hasher;
|
use crate::fn_hash::{calc_fn_hash, get_hasher};
|
||||||
use crate::fn_native::{
|
use crate::fn_native::{
|
||||||
CallableFunction, IteratorFn, OnDebugCallback, OnParseTokenCallback, OnPrintCallback,
|
CallableFunction, IteratorFn, OnDebugCallback, OnParseTokenCallback, OnPrintCallback,
|
||||||
OnVarCallback,
|
OnVarCallback,
|
||||||
@ -790,6 +790,27 @@ impl Default for Limits {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A type containing useful constants for the [`Engine`].
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct GlobalConstants {
|
||||||
|
/// An empty [`ImmutableString`] for cloning purposes.
|
||||||
|
pub(crate) empty_string: ImmutableString,
|
||||||
|
/// Function call hash to FN_IDX_GET
|
||||||
|
pub(crate) fn_hash_idx_get: u64,
|
||||||
|
/// Function call hash to FN_IDX_SET
|
||||||
|
pub(crate) fn_hash_idx_set: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for GlobalConstants {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
empty_string: Default::default(),
|
||||||
|
fn_hash_idx_get: calc_fn_hash(FN_IDX_GET, 2),
|
||||||
|
fn_hash_idx_set: calc_fn_hash(FN_IDX_SET, 3),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Context of a script evaluation process.
|
/// Context of a script evaluation process.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct EvalContext<'a, 'x, 'px, 'm, 's, 'b, 't, 'pt> {
|
pub struct EvalContext<'a, 'x, 'px, 'm, 's, 'b, 't, 'pt> {
|
||||||
@ -911,8 +932,8 @@ pub struct Engine {
|
|||||||
/// A map mapping type names to pretty-print names.
|
/// A map mapping type names to pretty-print names.
|
||||||
pub(crate) type_names: BTreeMap<Identifier, Box<Identifier>>,
|
pub(crate) type_names: BTreeMap<Identifier, Box<Identifier>>,
|
||||||
|
|
||||||
/// An empty [`ImmutableString`] for cloning purposes.
|
/// Useful constants
|
||||||
pub(crate) empty_string: ImmutableString,
|
pub(crate) constants: GlobalConstants,
|
||||||
|
|
||||||
/// A set of symbols to disable.
|
/// A set of symbols to disable.
|
||||||
pub(crate) disabled_symbols: BTreeSet<Identifier>,
|
pub(crate) disabled_symbols: BTreeSet<Identifier>,
|
||||||
@ -1042,7 +1063,7 @@ impl Engine {
|
|||||||
module_resolver: None,
|
module_resolver: None,
|
||||||
|
|
||||||
type_names: Default::default(),
|
type_names: Default::default(),
|
||||||
empty_string: Default::default(),
|
constants: Default::default(),
|
||||||
disabled_symbols: Default::default(),
|
disabled_symbols: Default::default(),
|
||||||
custom_keywords: Default::default(),
|
custom_keywords: Default::default(),
|
||||||
custom_syntax: Default::default(),
|
custom_syntax: Default::default(),
|
||||||
@ -1070,6 +1091,13 @@ impl Engine {
|
|||||||
engine
|
engine
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get an empty [`ImmutableString`].
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn empty_string(&self) -> ImmutableString {
|
||||||
|
self.constants.empty_string.clone()
|
||||||
|
}
|
||||||
|
|
||||||
/// Search for a module within an imports stack.
|
/// Search for a module within an imports stack.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -1294,7 +1322,7 @@ impl Engine {
|
|||||||
if let Some(mut new_val) = try_setter {
|
if let Some(mut new_val) = try_setter {
|
||||||
// Try to call index setter
|
// Try to call index setter
|
||||||
let hash_set =
|
let hash_set =
|
||||||
FnCallHashes::from_native(crate::calc_fn_hash(FN_IDX_SET, 3));
|
FnCallHashes::from_native(self.constants.fn_hash_idx_set);
|
||||||
let args = &mut [target, &mut idx_val_for_setter, &mut new_val];
|
let args = &mut [target, &mut idx_val_for_setter, &mut new_val];
|
||||||
let pos = Position::NONE;
|
let pos = Position::NONE;
|
||||||
|
|
||||||
@ -1427,7 +1455,7 @@ impl Engine {
|
|||||||
EvalAltResult::ErrorDotExpr(_, _) => {
|
EvalAltResult::ErrorDotExpr(_, _) => {
|
||||||
let args = &mut [target, &mut name.into(), &mut new_val];
|
let args = &mut [target, &mut name.into(), &mut new_val];
|
||||||
let hash_set =
|
let hash_set =
|
||||||
FnCallHashes::from_native(crate::calc_fn_hash(FN_IDX_SET, 3));
|
FnCallHashes::from_native(self.constants.fn_hash_idx_set);
|
||||||
let pos = Position::NONE;
|
let pos = Position::NONE;
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
@ -1583,7 +1611,7 @@ impl Engine {
|
|||||||
let args =
|
let args =
|
||||||
&mut [target.as_mut(), &mut name.into(), val];
|
&mut [target.as_mut(), &mut name.into(), val];
|
||||||
let hash_set = FnCallHashes::from_native(
|
let hash_set = FnCallHashes::from_native(
|
||||||
crate::calc_fn_hash(FN_IDX_SET, 3),
|
self.constants.fn_hash_idx_set,
|
||||||
);
|
);
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, FN_IDX_SET, hash_set, args,
|
mods, state, lib, FN_IDX_SET, hash_set, args,
|
||||||
@ -1987,7 +2015,7 @@ impl Engine {
|
|||||||
|
|
||||||
_ if use_indexers => {
|
_ if use_indexers => {
|
||||||
let args = &mut [target, &mut idx];
|
let args = &mut [target, &mut idx];
|
||||||
let hash_get = FnCallHashes::from_native(crate::calc_fn_hash(FN_IDX_GET, 2));
|
let hash_get = FnCallHashes::from_native(self.constants.fn_hash_idx_get);
|
||||||
let idx_pos = Position::NONE;
|
let idx_pos = Position::NONE;
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
@ -2059,7 +2087,7 @@ impl Engine {
|
|||||||
// `... ${...} ...`
|
// `... ${...} ...`
|
||||||
Expr::InterpolatedString(x, pos) => {
|
Expr::InterpolatedString(x, pos) => {
|
||||||
let mut pos = *pos;
|
let mut pos = *pos;
|
||||||
let mut result: Dynamic = self.empty_string.clone().into();
|
let mut result: Dynamic = self.empty_string().clone().into();
|
||||||
|
|
||||||
for expr in x.iter() {
|
for expr in x.iter() {
|
||||||
let item = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
let item = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
||||||
@ -3006,17 +3034,22 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Check a result to ensure that the data size is within allowable limit.
|
/// Check a result to ensure that the data size is within allowable limit.
|
||||||
#[cfg(feature = "unchecked")]
|
|
||||||
#[inline(always)]
|
|
||||||
fn check_return_value(&self, result: RhaiResult) -> RhaiResult {
|
fn check_return_value(&self, result: RhaiResult) -> RhaiResult {
|
||||||
result
|
match result {
|
||||||
|
// Concentrate all empty strings into one instance to save memory
|
||||||
|
#[cfg(feature = "no_closure")]
|
||||||
|
Ok(r) if r.as_str_ref().map_or(false, &str::is_empty) => Ok(self.empty_string().into()),
|
||||||
|
// Concentrate all empty strings into one instance to save memory
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
Ok(r) if !r.is_shared() && r.as_str_ref().map_or(false, &str::is_empty) => {
|
||||||
|
Ok(self.empty_string().into())
|
||||||
}
|
}
|
||||||
|
// Check data sizes
|
||||||
/// Check a result to ensure that the data size is within allowable limit.
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
#[inline(always)]
|
Ok(r) => self.check_data_size(&r).map(|_| r),
|
||||||
fn check_return_value(&self, result: RhaiResult) -> RhaiResult {
|
// Return all other results
|
||||||
result.and_then(|r| self.check_data_size(&r).map(|_| r))
|
_ => result,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "unchecked")]
|
#[cfg(feature = "unchecked")]
|
||||||
|
@ -44,9 +44,7 @@ pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
|
|||||||
if TypeId::of::<T>() == TypeId::of::<&str>() {
|
if TypeId::of::<T>() == TypeId::of::<&str>() {
|
||||||
// If T is `&str`, data must be `ImmutableString`, so map directly to it
|
// If T is `&str`, data must be `ImmutableString`, so map directly to it
|
||||||
data.flatten_in_place();
|
data.flatten_in_place();
|
||||||
let ref_str = data
|
let ref_str = data.as_str_ref().expect("argument type is &str");
|
||||||
.as_str_ref()
|
|
||||||
.expect("argument passed by value is not shared");
|
|
||||||
let ref_t = unsafe { mem::transmute::<_, &T>(&ref_str) };
|
let ref_t = unsafe { mem::transmute::<_, &T>(&ref_str) };
|
||||||
ref_t.clone()
|
ref_t.clone()
|
||||||
} else if TypeId::of::<T>() == TypeId::of::<String>() {
|
} else if TypeId::of::<T>() == TypeId::of::<String>() {
|
||||||
|
@ -822,7 +822,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
|
|||||||
// ``
|
// ``
|
||||||
Expr::InterpolatedString(x, pos) if x.is_empty() => {
|
Expr::InterpolatedString(x, pos) if x.is_empty() => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*expr = Expr::StringConstant(state.engine.empty_string.clone(), *pos);
|
*expr = Expr::StringConstant(state.engine.empty_string().clone(), *pos);
|
||||||
}
|
}
|
||||||
// `...`
|
// `...`
|
||||||
Expr::InterpolatedString(x, _) if x.len() == 1 && matches!(x[0], Expr::StringConstant(_, _)) => {
|
Expr::InterpolatedString(x, _) if x.len() == 1 && matches!(x[0], Expr::StringConstant(_, _)) => {
|
||||||
|
@ -126,7 +126,7 @@ mod string_functions {
|
|||||||
len: INT,
|
len: INT,
|
||||||
) -> ImmutableString {
|
) -> ImmutableString {
|
||||||
if string.is_empty() || len <= 0 {
|
if string.is_empty() || len <= 0 {
|
||||||
return ctx.engine().empty_string.clone();
|
return ctx.engine().empty_string().clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut chars = StaticVec::<char>::with_capacity(len as usize);
|
let mut chars = StaticVec::<char>::with_capacity(len as usize);
|
||||||
@ -305,13 +305,13 @@ mod string_functions {
|
|||||||
len: INT,
|
len: INT,
|
||||||
) -> ImmutableString {
|
) -> ImmutableString {
|
||||||
if string.is_empty() {
|
if string.is_empty() {
|
||||||
return ctx.engine().empty_string.clone();
|
return ctx.engine().empty_string().clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut chars = StaticVec::with_capacity(string.len());
|
let mut chars = StaticVec::with_capacity(string.len());
|
||||||
|
|
||||||
let offset = if string.is_empty() || len <= 0 {
|
let offset = if string.is_empty() || len <= 0 {
|
||||||
return ctx.engine().empty_string.clone();
|
return ctx.engine().empty_string().clone();
|
||||||
} else if start < 0 {
|
} else if start < 0 {
|
||||||
if let Some(n) = start.checked_abs() {
|
if let Some(n) = start.checked_abs() {
|
||||||
chars.extend(string.chars());
|
chars.extend(string.chars());
|
||||||
@ -324,7 +324,7 @@ mod string_functions {
|
|||||||
0
|
0
|
||||||
}
|
}
|
||||||
} else if start as usize >= string.chars().count() {
|
} else if start as usize >= string.chars().count() {
|
||||||
return ctx.engine().empty_string.clone();
|
return ctx.engine().empty_string().clone();
|
||||||
} else {
|
} else {
|
||||||
start as usize
|
start as usize
|
||||||
};
|
};
|
||||||
@ -354,7 +354,7 @@ mod string_functions {
|
|||||||
start: INT,
|
start: INT,
|
||||||
) -> ImmutableString {
|
) -> ImmutableString {
|
||||||
if string.is_empty() {
|
if string.is_empty() {
|
||||||
ctx.engine().empty_string.clone()
|
ctx.engine().empty_string().clone()
|
||||||
} else {
|
} else {
|
||||||
let len = string.len() as INT;
|
let len = string.len() as INT;
|
||||||
sub_string(ctx, string, start, len)
|
sub_string(ctx, string, start, len)
|
||||||
@ -571,14 +571,14 @@ mod string_functions {
|
|||||||
if let Some(n) = start.checked_abs() {
|
if let Some(n) = start.checked_abs() {
|
||||||
let num_chars = string.chars().count();
|
let num_chars = string.chars().count();
|
||||||
if n as usize > num_chars {
|
if n as usize > num_chars {
|
||||||
vec![ctx.engine().empty_string.clone().into(), string.into()]
|
vec![ctx.engine().empty_string().clone().into(), string.into()]
|
||||||
} else {
|
} else {
|
||||||
let prefix: String = string.chars().take(num_chars - n as usize).collect();
|
let prefix: String = string.chars().take(num_chars - n as usize).collect();
|
||||||
let prefix_len = prefix.len();
|
let prefix_len = prefix.len();
|
||||||
vec![prefix.into(), string[prefix_len..].into()]
|
vec![prefix.into(), string[prefix_len..].into()]
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
vec![ctx.engine().empty_string.clone().into(), string.into()]
|
vec![ctx.engine().empty_string().clone().into(), string.into()]
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let prefix: String = string.chars().take(start as usize).collect();
|
let prefix: String = string.chars().take(start as usize).collect();
|
||||||
|
Loading…
Reference in New Issue
Block a user