Fix empty strings.
This commit is contained in:
parent
3557db88e8
commit
a130960627
@ -19,6 +19,7 @@ Enhancements
|
|||||||
* `Engine::consume_XXX` methods are renamed to `Engine::run_XXX` to make meanings clearer. The `consume_XXX` API is deprecated.
|
* `Engine::consume_XXX` methods are renamed to `Engine::run_XXX` to make meanings clearer. The `consume_XXX` API is deprecated.
|
||||||
* `Engine::register_type_XXX` are now available even under `no_object`.
|
* `Engine::register_type_XXX` are now available even under `no_object`.
|
||||||
* Added `Engine::on_parse_token` to allow remapping certain tokens during parsing.
|
* Added `Engine::on_parse_token` to allow remapping certain tokens during parsing.
|
||||||
|
* Added `Engine::const_empty_string` to merge empty strings into a single instance.
|
||||||
|
|
||||||
### Custom Syntax
|
### Custom Syntax
|
||||||
|
|
||||||
|
@ -1092,9 +1092,12 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get an empty [`ImmutableString`].
|
/// Get an empty [`ImmutableString`].
|
||||||
|
///
|
||||||
|
/// [`Engine`] keeps a single instance of an empty [`ImmutableString`] and uses this to create
|
||||||
|
/// shared instances for subsequent uses. This minimizes unnecessary allocations for empty strings.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn empty_string(&self) -> ImmutableString {
|
pub fn const_empty_string(&self) -> ImmutableString {
|
||||||
self.constants.empty_string.clone()
|
self.constants.empty_string.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2087,7 +2090,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.const_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)?;
|
||||||
@ -3042,7 +3045,7 @@ impl Engine {
|
|||||||
// Concentrate all empty strings into one instance to save memory
|
// Concentrate all empty strings into one instance to save memory
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Ok(r) if !r.is_shared() && r.as_str_ref().map_or(false, &str::is_empty) => {
|
Ok(r) if !r.is_shared() && r.as_str_ref().map_or(false, &str::is_empty) => {
|
||||||
Ok(self.empty_string().into())
|
Ok(self.const_empty_string().into())
|
||||||
}
|
}
|
||||||
// Check data sizes
|
// Check data sizes
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
@ -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.const_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().const_empty_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
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().const_empty_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
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().const_empty_string();
|
||||||
} 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().const_empty_string();
|
||||||
} 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().const_empty_string()
|
||||||
} 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().const_empty_string().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().const_empty_string().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