Hash functions only once via custom hasher.
This commit is contained in:
parent
439053b153
commit
c9de37e8d1
@ -12,7 +12,7 @@ use crate::r#unsafe::{unsafe_cast_var_name_to_lifetime, unsafe_mut_cast_to_lifet
|
||||
use crate::result::EvalAltResult;
|
||||
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
||||
use crate::token::Position;
|
||||
use crate::utils::StaticVec;
|
||||
use crate::utils::{StaticVec, StraightHasherBuilder};
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::parser::FLOAT;
|
||||
@ -196,7 +196,7 @@ impl State {
|
||||
///
|
||||
/// The key of the `HashMap` is a `u64` hash calculated by the function `calc_fn_hash`.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct FunctionsLib(HashMap<u64, Shared<FnDef>>);
|
||||
pub struct FunctionsLib(HashMap<u64, Shared<FnDef>, StraightHasherBuilder>);
|
||||
|
||||
impl FunctionsLib {
|
||||
/// Create a new `FunctionsLib` from a collection of `FnDef`.
|
||||
@ -261,7 +261,7 @@ impl From<Vec<(u64, Shared<FnDef>)>> for FunctionsLib {
|
||||
}
|
||||
|
||||
impl Deref for FunctionsLib {
|
||||
type Target = HashMap<u64, Shared<FnDef>>;
|
||||
type Target = HashMap<u64, Shared<FnDef>, StraightHasherBuilder>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
@ -269,7 +269,7 @@ impl Deref for FunctionsLib {
|
||||
}
|
||||
|
||||
impl DerefMut for FunctionsLib {
|
||||
fn deref_mut(&mut self) -> &mut HashMap<u64, Shared<FnDef>> {
|
||||
fn deref_mut(&mut self) -> &mut HashMap<u64, Shared<FnDef>, StraightHasherBuilder> {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
@ -952,21 +952,24 @@ impl Engine {
|
||||
let mut idx_val = idx_values.pop();
|
||||
|
||||
if is_index {
|
||||
let pos = rhs.position();
|
||||
|
||||
match rhs {
|
||||
// xxx[idx].dot_rhs... | xxx[idx][dot_rhs]...
|
||||
// xxx[idx].expr... | xxx[idx][expr]...
|
||||
Expr::Dot(x) | Expr::Index(x) => {
|
||||
let (idx, expr, pos) = x.as_ref();
|
||||
let is_idx = matches!(rhs, Expr::Index(_));
|
||||
let pos = x.0.position();
|
||||
let this_ptr = &mut self
|
||||
.get_indexed_mut(state, lib, obj, is_ref, idx_val, pos, op_pos, false)?;
|
||||
let idx_pos = idx.position();
|
||||
let this_ptr = &mut self.get_indexed_mut(
|
||||
state, lib, obj, is_ref, idx_val, idx_pos, op_pos, false,
|
||||
)?;
|
||||
|
||||
self.eval_dot_index_chain_helper(
|
||||
state, lib, this_ptr, &x.1, idx_values, is_idx, x.2, level, new_val,
|
||||
state, lib, this_ptr, expr, idx_values, is_idx, *pos, level, new_val,
|
||||
)
|
||||
}
|
||||
// xxx[rhs] = new_val
|
||||
_ if new_val.is_some() => {
|
||||
let pos = rhs.position();
|
||||
let this_ptr = &mut self
|
||||
.get_indexed_mut(state, lib, obj, is_ref, idx_val, pos, op_pos, true)?;
|
||||
|
||||
@ -975,16 +978,7 @@ impl Engine {
|
||||
}
|
||||
// xxx[rhs]
|
||||
_ => self
|
||||
.get_indexed_mut(
|
||||
state,
|
||||
lib,
|
||||
obj,
|
||||
is_ref,
|
||||
idx_val,
|
||||
rhs.position(),
|
||||
op_pos,
|
||||
false,
|
||||
)
|
||||
.get_indexed_mut(state, lib, obj, is_ref, idx_val, pos, op_pos, false)
|
||||
.map(|v| (v.clone_into_dynamic(), false)),
|
||||
}
|
||||
} else {
|
||||
@ -1050,57 +1044,51 @@ impl Engine {
|
||||
.map(|(v, _)| (v, false))
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
// {xxx:map}.idx_lhs[idx_expr] | {xxx:map}.dot_lhs.rhs
|
||||
// {xxx:map}.prop[expr] | {xxx:map}.prop.expr
|
||||
Expr::Index(x) | Expr::Dot(x) if obj.is::<Map>() => {
|
||||
let (prop, expr, pos) = x.as_ref();
|
||||
let is_idx = matches!(rhs, Expr::Index(_));
|
||||
|
||||
let mut val = if let Expr::Property(p) = &x.0 {
|
||||
let mut val = if let Expr::Property(p) = prop {
|
||||
let ((prop, _, _), _) = p.as_ref();
|
||||
let index = prop.clone().into();
|
||||
self.get_indexed_mut(state, lib, obj, is_ref, index, x.2, op_pos, false)?
|
||||
self.get_indexed_mut(state, lib, obj, is_ref, index, *pos, op_pos, false)?
|
||||
} else {
|
||||
// Syntax error
|
||||
return Err(Box::new(EvalAltResult::ErrorDotExpr(
|
||||
"".into(),
|
||||
rhs.position(),
|
||||
)));
|
||||
unreachable!();
|
||||
};
|
||||
|
||||
self.eval_dot_index_chain_helper(
|
||||
state, lib, &mut val, &x.1, idx_values, is_idx, x.2, level, new_val,
|
||||
state, lib, &mut val, expr, idx_values, is_idx, *pos, level, new_val,
|
||||
)
|
||||
}
|
||||
// xxx.idx_lhs[idx_expr] | xxx.dot_lhs.rhs
|
||||
// xxx.prop[expr] | xxx.prop.expr
|
||||
Expr::Index(x) | Expr::Dot(x) => {
|
||||
let is_idx = matches!(rhs, Expr::Index(_));
|
||||
let (prop, expr, pos) = x.as_ref();
|
||||
let is_idx = matches!(expr, Expr::Index(_));
|
||||
let args = &mut [obj, &mut Default::default()];
|
||||
|
||||
let (mut val, updated) = if let Expr::Property(p) = &x.0 {
|
||||
let (mut val, updated) = if let Expr::Property(p) = prop {
|
||||
let ((_, getter, _), _) = p.as_ref();
|
||||
let args = &mut args[..1];
|
||||
self.exec_fn_call(state, lib, getter, true, 0, args, is_ref, None, x.2, 0)?
|
||||
self.exec_fn_call(state, lib, getter, true, 0, args, is_ref, None, *pos, 0)?
|
||||
} else {
|
||||
// Syntax error
|
||||
return Err(Box::new(EvalAltResult::ErrorDotExpr(
|
||||
"".into(),
|
||||
rhs.position(),
|
||||
)));
|
||||
unreachable!();
|
||||
};
|
||||
let val = &mut val;
|
||||
let target = &mut val.into();
|
||||
|
||||
let (result, may_be_changed) = self.eval_dot_index_chain_helper(
|
||||
state, lib, target, &x.1, idx_values, is_idx, x.2, level, new_val,
|
||||
state, lib, target, expr, idx_values, is_idx, *pos, level, new_val,
|
||||
)?;
|
||||
|
||||
// Feed the value back via a setter just in case it has been updated
|
||||
if updated || may_be_changed {
|
||||
if let Expr::Property(p) = &x.0 {
|
||||
if let Expr::Property(p) = prop {
|
||||
let ((_, _, setter), _) = p.as_ref();
|
||||
// Re-use args because the first &mut parameter will not be consumed
|
||||
args[1] = val;
|
||||
self.exec_fn_call(
|
||||
state, lib, setter, true, 0, args, is_ref, None, x.2, 0,
|
||||
state, lib, setter, true, 0, args, is_ref, None, *pos, 0,
|
||||
)
|
||||
.or_else(|err| match *err {
|
||||
// If there is no setter, no need to feed it back because the property is read-only
|
||||
|
@ -12,7 +12,7 @@ use crate::parser::{
|
||||
use crate::result::EvalAltResult;
|
||||
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
|
||||
use crate::token::{Position, Token};
|
||||
use crate::utils::StaticVec;
|
||||
use crate::utils::{StaticVec, StraightHasherBuilder};
|
||||
|
||||
use crate::stdlib::{
|
||||
any::TypeId,
|
||||
@ -44,10 +44,14 @@ pub struct Module {
|
||||
variables: HashMap<String, Dynamic>,
|
||||
|
||||
/// Flattened collection of all module variables, including those in sub-modules.
|
||||
all_variables: HashMap<u64, Dynamic>,
|
||||
all_variables: HashMap<u64, Dynamic, StraightHasherBuilder>,
|
||||
|
||||
/// External Rust functions.
|
||||
functions: HashMap<u64, (String, FnAccess, StaticVec<TypeId>, CallableFunction)>,
|
||||
functions: HashMap<
|
||||
u64,
|
||||
(String, FnAccess, StaticVec<TypeId>, CallableFunction),
|
||||
StraightHasherBuilder,
|
||||
>,
|
||||
|
||||
/// Script-defined functions.
|
||||
lib: FunctionsLib,
|
||||
@ -57,7 +61,7 @@ pub struct Module {
|
||||
|
||||
/// Flattened collection of all external Rust functions, native or scripted,
|
||||
/// including those in sub-modules.
|
||||
all_functions: HashMap<u64, CallableFunction>,
|
||||
all_functions: HashMap<u64, CallableFunction, StraightHasherBuilder>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Module {
|
||||
@ -101,7 +105,7 @@ impl Module {
|
||||
/// ```
|
||||
pub fn new_with_capacity(capacity: usize) -> Self {
|
||||
Self {
|
||||
functions: HashMap::with_capacity(capacity),
|
||||
functions: HashMap::with_capacity_and_hasher(capacity, StraightHasherBuilder),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ use crate::error::{LexError, ParseError, ParseErrorType};
|
||||
use crate::optimize::{optimize_into_ast, OptimizationLevel};
|
||||
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
||||
use crate::token::{Position, Token, TokenIterator};
|
||||
use crate::utils::StaticVec;
|
||||
use crate::utils::{StaticVec, StraightHasherBuilder};
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
use crate::module::ModuleRef;
|
||||
@ -1496,22 +1496,21 @@ fn parse_op_assignment_stmt<'a>(
|
||||
}
|
||||
|
||||
/// Make a dot expression.
|
||||
fn make_dot_expr(
|
||||
lhs: Expr,
|
||||
rhs: Expr,
|
||||
op_pos: Position,
|
||||
is_index: bool,
|
||||
) -> Result<Expr, ParseError> {
|
||||
fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseError> {
|
||||
Ok(match (lhs, rhs) {
|
||||
// idx_lhs[idx_rhs].rhs
|
||||
// idx_lhs[idx_expr].rhs
|
||||
// Attach dot chain to the bottom level of indexing chain
|
||||
(Expr::Index(x), rhs) => {
|
||||
Expr::Index(Box::new((x.0, make_dot_expr(x.1, rhs, op_pos, true)?, x.2)))
|
||||
let (idx_lhs, idx_expr, pos) = *x;
|
||||
Expr::Index(Box::new((
|
||||
idx_lhs,
|
||||
make_dot_expr(idx_expr, rhs, op_pos)?,
|
||||
pos,
|
||||
)))
|
||||
}
|
||||
// lhs.id
|
||||
(lhs, Expr::Variable(x)) if x.1.is_none() => {
|
||||
let (name, pos) = x.0;
|
||||
let lhs = if is_index { lhs.into_property() } else { lhs };
|
||||
|
||||
let getter = make_getter(&name);
|
||||
let setter = make_setter(&name);
|
||||
@ -1519,11 +1518,6 @@ fn make_dot_expr(
|
||||
|
||||
Expr::Dot(Box::new((lhs, rhs, op_pos)))
|
||||
}
|
||||
(lhs, Expr::Property(x)) => {
|
||||
let lhs = if is_index { lhs.into_property() } else { lhs };
|
||||
let rhs = Expr::Property(x);
|
||||
Expr::Dot(Box::new((lhs, rhs, op_pos)))
|
||||
}
|
||||
// lhs.module::id - syntax error
|
||||
(_, Expr::Variable(x)) if x.1.is_some() => {
|
||||
#[cfg(feature = "no_module")]
|
||||
@ -1531,34 +1525,30 @@ fn make_dot_expr(
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
return Err(PERR::PropertyExpected.into_err(x.1.unwrap().get(0).1));
|
||||
}
|
||||
// lhs.prop
|
||||
(lhs, prop @ Expr::Property(_)) => Expr::Dot(Box::new((lhs, prop, op_pos))),
|
||||
// lhs.dot_lhs.dot_rhs
|
||||
(lhs, Expr::Dot(x)) => {
|
||||
let (dot_lhs, dot_rhs, pos) = *x;
|
||||
Expr::Dot(Box::new((
|
||||
lhs,
|
||||
Expr::Dot(Box::new((
|
||||
dot_lhs.into_property(),
|
||||
dot_rhs.into_property(),
|
||||
pos,
|
||||
))),
|
||||
Expr::Dot(Box::new((dot_lhs.into_property(), dot_rhs, pos))),
|
||||
op_pos,
|
||||
)))
|
||||
}
|
||||
// lhs.idx_lhs[idx_rhs]
|
||||
(lhs, Expr::Index(x)) => {
|
||||
let (idx_lhs, idx_rhs, pos) = *x;
|
||||
let (dot_lhs, dot_rhs, pos) = *x;
|
||||
Expr::Dot(Box::new((
|
||||
lhs,
|
||||
Expr::Index(Box::new((
|
||||
idx_lhs.into_property(),
|
||||
idx_rhs.into_property(),
|
||||
pos,
|
||||
))),
|
||||
Expr::Index(Box::new((dot_lhs.into_property(), dot_rhs, pos))),
|
||||
op_pos,
|
||||
)))
|
||||
}
|
||||
// lhs.func()
|
||||
(lhs, func @ Expr::FnCall(_)) => Expr::Dot(Box::new((lhs, func, op_pos))),
|
||||
// lhs.rhs
|
||||
(lhs, rhs) => Expr::Dot(Box::new((lhs, rhs.into_property(), op_pos))),
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -1822,7 +1812,7 @@ fn parse_binary_op<'a>(
|
||||
_ => (),
|
||||
}
|
||||
|
||||
make_dot_expr(current_lhs, rhs, pos, false)?
|
||||
make_dot_expr(current_lhs, rhs, pos)?
|
||||
}
|
||||
|
||||
token => return Err(PERR::UnknownOperator(token.into()).into_err(pos)),
|
||||
@ -2493,22 +2483,20 @@ pub fn parse_global_expr<'a>(
|
||||
fn parse_global_level<'a>(
|
||||
input: &mut Peekable<TokenIterator<'a>>,
|
||||
max_expr_depth: (usize, usize),
|
||||
) -> Result<(Vec<Stmt>, HashMap<u64, FnDef>), ParseError> {
|
||||
) -> Result<(Vec<Stmt>, Vec<FnDef>), ParseError> {
|
||||
let mut statements = Vec::<Stmt>::new();
|
||||
let mut functions = HashMap::<u64, FnDef>::new();
|
||||
let mut functions = HashMap::with_hasher(StraightHasherBuilder);
|
||||
let mut state = ParseState::new(max_expr_depth.0);
|
||||
|
||||
while !input.peek().unwrap().0.is_eof() {
|
||||
// Collect all the function definitions
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
{
|
||||
let mut access = FnAccess::Public;
|
||||
let mut must_be_fn = false;
|
||||
|
||||
if match_token(input, Token::Private)? {
|
||||
access = FnAccess::Private;
|
||||
must_be_fn = true;
|
||||
}
|
||||
let (access, must_be_fn) = if match_token(input, Token::Private)? {
|
||||
(FnAccess::Private, true)
|
||||
} else {
|
||||
(FnAccess::Public, false)
|
||||
};
|
||||
|
||||
match input.peek().unwrap() {
|
||||
(Token::Fn, _) => {
|
||||
@ -2565,7 +2553,7 @@ fn parse_global_level<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
Ok((statements, functions))
|
||||
Ok((statements, functions.into_iter().map(|(_, v)| v).collect()))
|
||||
}
|
||||
|
||||
/// Run the parser on an input stream, returning an AST.
|
||||
@ -2576,9 +2564,8 @@ pub fn parse<'a>(
|
||||
optimization_level: OptimizationLevel,
|
||||
max_expr_depth: (usize, usize),
|
||||
) -> Result<AST, ParseError> {
|
||||
let (statements, functions) = parse_global_level(input, max_expr_depth)?;
|
||||
let (statements, lib) = parse_global_level(input, max_expr_depth)?;
|
||||
|
||||
let lib = functions.into_iter().map(|(_, v)| v).collect();
|
||||
Ok(
|
||||
// Optimize AST
|
||||
optimize_into_ast(engine, scope, statements, lib, optimization_level),
|
||||
|
66
src/utils.rs
66
src/utils.rs
@ -11,7 +11,7 @@ use crate::stdlib::{
|
||||
borrow::Borrow,
|
||||
boxed::Box,
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
hash::{BuildHasher, Hash, Hasher},
|
||||
iter::FromIterator,
|
||||
mem,
|
||||
mem::MaybeUninit,
|
||||
@ -27,6 +27,48 @@ use crate::stdlib::collections::hash_map::DefaultHasher;
|
||||
#[cfg(feature = "no_std")]
|
||||
use ahash::AHasher;
|
||||
|
||||
/// A hasher that only takes one single `u64` and returns it as a hash key.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics when hashing any data type other than a `u64`.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
|
||||
pub struct StraightHasher(u64);
|
||||
|
||||
impl Hasher for StraightHasher {
|
||||
#[inline(always)]
|
||||
fn finish(&self) -> u64 {
|
||||
self.0
|
||||
}
|
||||
#[inline]
|
||||
fn write(&mut self, bytes: &[u8]) {
|
||||
let mut key = [0_u8; 8];
|
||||
key.copy_from_slice(&bytes[..8]); // Panics if fewer than 8 bytes
|
||||
self.0 = u64::from_le_bytes(key);
|
||||
}
|
||||
}
|
||||
|
||||
impl StraightHasher {
|
||||
/// Create a `StraightHasher`.
|
||||
#[inline(always)]
|
||||
pub fn new() -> Self {
|
||||
Self(0)
|
||||
}
|
||||
}
|
||||
|
||||
/// A hash builder for `StraightHasher`.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
|
||||
pub struct StraightHasherBuilder;
|
||||
|
||||
impl BuildHasher for StraightHasherBuilder {
|
||||
type Hasher = StraightHasher;
|
||||
|
||||
#[inline(always)]
|
||||
fn build_hasher(&self) -> Self::Hasher {
|
||||
StraightHasher::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate a `u64` hash key from a module-qualified function name and parameter types.
|
||||
///
|
||||
/// Module names are passed in via `&str` references from an iterator.
|
||||
@ -108,6 +150,7 @@ pub struct StaticVec<T> {
|
||||
const MAX_STATIC_VEC: usize = 4;
|
||||
|
||||
impl<T> Drop for StaticVec<T> {
|
||||
#[inline(always)]
|
||||
fn drop(&mut self) {
|
||||
self.clear();
|
||||
}
|
||||
@ -174,6 +217,7 @@ impl<T> FromIterator<T> for StaticVec<T> {
|
||||
|
||||
impl<T> StaticVec<T> {
|
||||
/// Create a new `StaticVec`.
|
||||
#[inline(always)]
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
@ -189,6 +233,7 @@ impl<T> StaticVec<T> {
|
||||
self.len = 0;
|
||||
}
|
||||
/// Extract a `MaybeUninit` into a concrete initialized type.
|
||||
#[inline(always)]
|
||||
fn extract(value: MaybeUninit<T>) -> T {
|
||||
unsafe { value.assume_init() }
|
||||
}
|
||||
@ -250,6 +295,7 @@ impl<T> StaticVec<T> {
|
||||
);
|
||||
}
|
||||
/// Is data stored in fixed-size storage?
|
||||
#[inline(always)]
|
||||
fn is_fixed_storage(&self) -> bool {
|
||||
self.len <= MAX_STATIC_VEC
|
||||
}
|
||||
@ -359,10 +405,12 @@ impl<T> StaticVec<T> {
|
||||
result
|
||||
}
|
||||
/// Get the number of items in this `StaticVec`.
|
||||
#[inline(always)]
|
||||
pub fn len(&self) -> usize {
|
||||
self.len
|
||||
}
|
||||
/// Is this `StaticVec` empty?
|
||||
#[inline(always)]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len == 0
|
||||
}
|
||||
@ -605,41 +653,48 @@ pub struct ImmutableString(Shared<String>);
|
||||
impl Deref for ImmutableString {
|
||||
type Target = String;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<String> for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &String {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<str> for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn borrow(&self) -> &str {
|
||||
self.0.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn from(value: &str) -> Self {
|
||||
Self(value.to_string().into())
|
||||
}
|
||||
}
|
||||
impl From<String> for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn from(value: String) -> Self {
|
||||
Self(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<String>> for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn from(value: Box<String>) -> Self {
|
||||
Self(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ImmutableString> for String {
|
||||
#[inline(always)]
|
||||
fn from(value: ImmutableString) -> Self {
|
||||
value.into_owned()
|
||||
}
|
||||
@ -648,42 +703,49 @@ impl From<ImmutableString> for String {
|
||||
impl FromStr for ImmutableString {
|
||||
type Err = ();
|
||||
|
||||
#[inline(always)]
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Self(s.to_string().into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<char> for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
|
||||
Self(iter.into_iter().collect::<String>().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromIterator<&'a char> for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn from_iter<T: IntoIterator<Item = &'a char>>(iter: T) -> Self {
|
||||
Self(iter.into_iter().cloned().collect::<String>().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromIterator<&'a str> for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
|
||||
Self(iter.into_iter().collect::<String>().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromIterator<String> for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn from_iter<T: IntoIterator<Item = String>>(iter: T) -> Self {
|
||||
Self(iter.into_iter().collect::<String>().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self.0.as_str(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(self.0.as_str(), f)
|
||||
}
|
||||
@ -818,6 +880,7 @@ impl Add<char> for &ImmutableString {
|
||||
}
|
||||
|
||||
impl AddAssign<char> for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn add_assign(&mut self, rhs: char) {
|
||||
self.make_mut().push(rhs);
|
||||
}
|
||||
@ -832,6 +895,7 @@ impl ImmutableString {
|
||||
}
|
||||
/// Make sure that the `ImmutableString` is unique (i.e. no other outstanding references).
|
||||
/// Then return a mutable reference to the `String`.
|
||||
#[inline(always)]
|
||||
pub fn make_mut(&mut self) -> &mut String {
|
||||
shared_make_mut(&mut self.0)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user