Hash functions only once via custom hasher.

This commit is contained in:
Stephen Chung 2020-05-31 00:02:23 +08:00
parent 439053b153
commit c9de37e8d1
4 changed files with 130 additions and 87 deletions

View File

@ -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::result::EvalAltResult;
use crate::scope::{EntryType as ScopeEntryType, Scope}; use crate::scope::{EntryType as ScopeEntryType, Scope};
use crate::token::Position; use crate::token::Position;
use crate::utils::StaticVec; use crate::utils::{StaticVec, StraightHasherBuilder};
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
use crate::parser::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`. /// The key of the `HashMap` is a `u64` hash calculated by the function `calc_fn_hash`.
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct FunctionsLib(HashMap<u64, Shared<FnDef>>); pub struct FunctionsLib(HashMap<u64, Shared<FnDef>, StraightHasherBuilder>);
impl FunctionsLib { impl FunctionsLib {
/// Create a new `FunctionsLib` from a collection of `FnDef`. /// 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 { impl Deref for FunctionsLib {
type Target = HashMap<u64, Shared<FnDef>>; type Target = HashMap<u64, Shared<FnDef>, StraightHasherBuilder>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.0 &self.0
@ -269,7 +269,7 @@ impl Deref for FunctionsLib {
} }
impl DerefMut 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 &mut self.0
} }
} }
@ -952,21 +952,24 @@ impl Engine {
let mut idx_val = idx_values.pop(); let mut idx_val = idx_values.pop();
if is_index { if is_index {
let pos = rhs.position();
match rhs { match rhs {
// xxx[idx].dot_rhs... | xxx[idx][dot_rhs]... // xxx[idx].expr... | xxx[idx][expr]...
Expr::Dot(x) | Expr::Index(x) => { Expr::Dot(x) | Expr::Index(x) => {
let (idx, expr, pos) = x.as_ref();
let is_idx = matches!(rhs, Expr::Index(_)); let is_idx = matches!(rhs, Expr::Index(_));
let pos = x.0.position(); let idx_pos = idx.position();
let this_ptr = &mut self let this_ptr = &mut self.get_indexed_mut(
.get_indexed_mut(state, lib, obj, is_ref, idx_val, pos, op_pos, false)?; state, lib, obj, is_ref, idx_val, idx_pos, op_pos, false,
)?;
self.eval_dot_index_chain_helper( 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 // xxx[rhs] = new_val
_ if new_val.is_some() => { _ if new_val.is_some() => {
let pos = rhs.position();
let this_ptr = &mut self let this_ptr = &mut self
.get_indexed_mut(state, lib, obj, is_ref, idx_val, pos, op_pos, true)?; .get_indexed_mut(state, lib, obj, is_ref, idx_val, pos, op_pos, true)?;
@ -975,16 +978,7 @@ impl Engine {
} }
// xxx[rhs] // xxx[rhs]
_ => self _ => self
.get_indexed_mut( .get_indexed_mut(state, lib, obj, is_ref, idx_val, pos, op_pos, false)
state,
lib,
obj,
is_ref,
idx_val,
rhs.position(),
op_pos,
false,
)
.map(|v| (v.clone_into_dynamic(), false)), .map(|v| (v.clone_into_dynamic(), false)),
} }
} else { } else {
@ -1050,57 +1044,51 @@ impl Engine {
.map(|(v, _)| (v, false)) .map(|(v, _)| (v, false))
} }
#[cfg(not(feature = "no_object"))] #[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>() => { 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 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 ((prop, _, _), _) = p.as_ref();
let index = prop.clone().into(); 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 { } else {
// Syntax error unreachable!();
return Err(Box::new(EvalAltResult::ErrorDotExpr(
"".into(),
rhs.position(),
)));
}; };
self.eval_dot_index_chain_helper( 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) => { 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 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 ((_, getter, _), _) = p.as_ref();
let args = &mut args[..1]; 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 { } else {
// Syntax error unreachable!();
return Err(Box::new(EvalAltResult::ErrorDotExpr(
"".into(),
rhs.position(),
)));
}; };
let val = &mut val; let val = &mut val;
let target = &mut val.into(); let target = &mut val.into();
let (result, may_be_changed) = self.eval_dot_index_chain_helper( 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 // Feed the value back via a setter just in case it has been updated
if updated || may_be_changed { if updated || may_be_changed {
if let Expr::Property(p) = &x.0 { if let Expr::Property(p) = prop {
let ((_, _, setter), _) = p.as_ref(); let ((_, _, setter), _) = p.as_ref();
// Re-use args because the first &mut parameter will not be consumed // Re-use args because the first &mut parameter will not be consumed
args[1] = val; args[1] = val;
self.exec_fn_call( 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 { .or_else(|err| match *err {
// If there is no setter, no need to feed it back because the property is read-only // If there is no setter, no need to feed it back because the property is read-only

View File

@ -12,7 +12,7 @@ use crate::parser::{
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, Token}; use crate::token::{Position, Token};
use crate::utils::StaticVec; use crate::utils::{StaticVec, StraightHasherBuilder};
use crate::stdlib::{ use crate::stdlib::{
any::TypeId, any::TypeId,
@ -44,10 +44,14 @@ pub struct Module {
variables: HashMap<String, Dynamic>, variables: HashMap<String, Dynamic>,
/// Flattened collection of all module variables, including those in sub-modules. /// 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. /// External Rust functions.
functions: HashMap<u64, (String, FnAccess, StaticVec<TypeId>, CallableFunction)>, functions: HashMap<
u64,
(String, FnAccess, StaticVec<TypeId>, CallableFunction),
StraightHasherBuilder,
>,
/// Script-defined functions. /// Script-defined functions.
lib: FunctionsLib, lib: FunctionsLib,
@ -57,7 +61,7 @@ pub struct Module {
/// Flattened collection of all external Rust functions, native or scripted, /// Flattened collection of all external Rust functions, native or scripted,
/// including those in sub-modules. /// including those in sub-modules.
all_functions: HashMap<u64, CallableFunction>, all_functions: HashMap<u64, CallableFunction, StraightHasherBuilder>,
} }
impl fmt::Debug for Module { impl fmt::Debug for Module {
@ -101,7 +105,7 @@ impl Module {
/// ``` /// ```
pub fn new_with_capacity(capacity: usize) -> Self { pub fn new_with_capacity(capacity: usize) -> Self {
Self { Self {
functions: HashMap::with_capacity(capacity), functions: HashMap::with_capacity_and_hasher(capacity, StraightHasherBuilder),
..Default::default() ..Default::default()
} }
} }

View File

@ -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::StaticVec; use crate::utils::{StaticVec, StraightHasherBuilder};
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
use crate::module::ModuleRef; use crate::module::ModuleRef;
@ -1496,22 +1496,21 @@ fn parse_op_assignment_stmt<'a>(
} }
/// Make a dot expression. /// Make a dot expression.
fn make_dot_expr( fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseError> {
lhs: Expr,
rhs: Expr,
op_pos: Position,
is_index: bool,
) -> Result<Expr, ParseError> {
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
// idx_lhs[idx_rhs].rhs // idx_lhs[idx_expr].rhs
// Attach dot chain to the bottom level of indexing chain // Attach dot chain to the bottom level of indexing chain
(Expr::Index(x), rhs) => { (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.id
(lhs, Expr::Variable(x)) if x.1.is_none() => { (lhs, Expr::Variable(x)) if x.1.is_none() => {
let (name, pos) = x.0; let (name, pos) = x.0;
let lhs = if is_index { lhs.into_property() } else { lhs };
let getter = make_getter(&name); let getter = make_getter(&name);
let setter = make_setter(&name); let setter = make_setter(&name);
@ -1519,11 +1518,6 @@ fn make_dot_expr(
Expr::Dot(Box::new((lhs, rhs, op_pos))) 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 // lhs.module::id - syntax error
(_, Expr::Variable(x)) if x.1.is_some() => { (_, Expr::Variable(x)) if x.1.is_some() => {
#[cfg(feature = "no_module")] #[cfg(feature = "no_module")]
@ -1531,34 +1525,30 @@ fn make_dot_expr(
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
return Err(PERR::PropertyExpected.into_err(x.1.unwrap().get(0).1)); 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.dot_lhs.dot_rhs
(lhs, Expr::Dot(x)) => { (lhs, Expr::Dot(x)) => {
let (dot_lhs, dot_rhs, pos) = *x; let (dot_lhs, dot_rhs, pos) = *x;
Expr::Dot(Box::new(( Expr::Dot(Box::new((
lhs, lhs,
Expr::Dot(Box::new(( Expr::Dot(Box::new((dot_lhs.into_property(), dot_rhs, pos))),
dot_lhs.into_property(),
dot_rhs.into_property(),
pos,
))),
op_pos, op_pos,
))) )))
} }
// lhs.idx_lhs[idx_rhs] // lhs.idx_lhs[idx_rhs]
(lhs, Expr::Index(x)) => { (lhs, Expr::Index(x)) => {
let (idx_lhs, idx_rhs, pos) = *x; let (dot_lhs, dot_rhs, pos) = *x;
Expr::Dot(Box::new(( Expr::Dot(Box::new((
lhs, lhs,
Expr::Index(Box::new(( Expr::Index(Box::new((dot_lhs.into_property(), dot_rhs, pos))),
idx_lhs.into_property(),
idx_rhs.into_property(),
pos,
))),
op_pos, op_pos,
))) )))
} }
// lhs.func()
(lhs, func @ Expr::FnCall(_)) => Expr::Dot(Box::new((lhs, func, op_pos))),
// lhs.rhs // 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)), 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>( fn parse_global_level<'a>(
input: &mut Peekable<TokenIterator<'a>>, input: &mut Peekable<TokenIterator<'a>>,
max_expr_depth: (usize, usize), 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 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); let mut state = ParseState::new(max_expr_depth.0);
while !input.peek().unwrap().0.is_eof() { while !input.peek().unwrap().0.is_eof() {
// Collect all the function definitions // Collect all the function definitions
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
{ {
let mut access = FnAccess::Public; let (access, must_be_fn) = if match_token(input, Token::Private)? {
let mut must_be_fn = false; (FnAccess::Private, true)
} else {
if match_token(input, Token::Private)? { (FnAccess::Public, false)
access = FnAccess::Private; };
must_be_fn = true;
}
match input.peek().unwrap() { match input.peek().unwrap() {
(Token::Fn, _) => { (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. /// Run the parser on an input stream, returning an AST.
@ -2576,9 +2564,8 @@ pub fn parse<'a>(
optimization_level: OptimizationLevel, optimization_level: OptimizationLevel,
max_expr_depth: (usize, usize), max_expr_depth: (usize, usize),
) -> Result<AST, ParseError> { ) -> 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( Ok(
// Optimize AST // Optimize AST
optimize_into_ast(engine, scope, statements, lib, optimization_level), optimize_into_ast(engine, scope, statements, lib, optimization_level),

View File

@ -11,7 +11,7 @@ use crate::stdlib::{
borrow::Borrow, borrow::Borrow,
boxed::Box, boxed::Box,
fmt, fmt,
hash::{Hash, Hasher}, hash::{BuildHasher, Hash, Hasher},
iter::FromIterator, iter::FromIterator,
mem, mem,
mem::MaybeUninit, mem::MaybeUninit,
@ -27,6 +27,48 @@ use crate::stdlib::collections::hash_map::DefaultHasher;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use ahash::AHasher; 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. /// 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. /// 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; const MAX_STATIC_VEC: usize = 4;
impl<T> Drop for StaticVec<T> { impl<T> Drop for StaticVec<T> {
#[inline(always)]
fn drop(&mut self) { fn drop(&mut self) {
self.clear(); self.clear();
} }
@ -174,6 +217,7 @@ impl<T> FromIterator<T> for StaticVec<T> {
impl<T> StaticVec<T> { impl<T> StaticVec<T> {
/// Create a new `StaticVec`. /// Create a new `StaticVec`.
#[inline(always)]
pub fn new() -> Self { pub fn new() -> Self {
Default::default() Default::default()
} }
@ -189,6 +233,7 @@ impl<T> StaticVec<T> {
self.len = 0; self.len = 0;
} }
/// Extract a `MaybeUninit` into a concrete initialized type. /// Extract a `MaybeUninit` into a concrete initialized type.
#[inline(always)]
fn extract(value: MaybeUninit<T>) -> T { fn extract(value: MaybeUninit<T>) -> T {
unsafe { value.assume_init() } unsafe { value.assume_init() }
} }
@ -250,6 +295,7 @@ impl<T> StaticVec<T> {
); );
} }
/// Is data stored in fixed-size storage? /// Is data stored in fixed-size storage?
#[inline(always)]
fn is_fixed_storage(&self) -> bool { fn is_fixed_storage(&self) -> bool {
self.len <= MAX_STATIC_VEC self.len <= MAX_STATIC_VEC
} }
@ -359,10 +405,12 @@ impl<T> StaticVec<T> {
result result
} }
/// Get the number of items in this `StaticVec`. /// Get the number of items in this `StaticVec`.
#[inline(always)]
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.len self.len
} }
/// Is this `StaticVec` empty? /// Is this `StaticVec` empty?
#[inline(always)]
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.len == 0 self.len == 0
} }
@ -605,41 +653,48 @@ pub struct ImmutableString(Shared<String>);
impl Deref for ImmutableString { impl Deref for ImmutableString {
type Target = String; type Target = String;
#[inline(always)]
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.0 &self.0
} }
} }
impl AsRef<String> for ImmutableString { impl AsRef<String> for ImmutableString {
#[inline(always)]
fn as_ref(&self) -> &String { fn as_ref(&self) -> &String {
&self.0 &self.0
} }
} }
impl Borrow<str> for ImmutableString { impl Borrow<str> for ImmutableString {
#[inline(always)]
fn borrow(&self) -> &str { fn borrow(&self) -> &str {
self.0.as_str() self.0.as_str()
} }
} }
impl From<&str> for ImmutableString { impl From<&str> for ImmutableString {
#[inline(always)]
fn from(value: &str) -> Self { fn from(value: &str) -> Self {
Self(value.to_string().into()) Self(value.to_string().into())
} }
} }
impl From<String> for ImmutableString { impl From<String> for ImmutableString {
#[inline(always)]
fn from(value: String) -> Self { fn from(value: String) -> Self {
Self(value.into()) Self(value.into())
} }
} }
impl From<Box<String>> for ImmutableString { impl From<Box<String>> for ImmutableString {
#[inline(always)]
fn from(value: Box<String>) -> Self { fn from(value: Box<String>) -> Self {
Self(value.into()) Self(value.into())
} }
} }
impl From<ImmutableString> for String { impl From<ImmutableString> for String {
#[inline(always)]
fn from(value: ImmutableString) -> Self { fn from(value: ImmutableString) -> Self {
value.into_owned() value.into_owned()
} }
@ -648,42 +703,49 @@ impl From<ImmutableString> for String {
impl FromStr for ImmutableString { impl FromStr for ImmutableString {
type Err = (); type Err = ();
#[inline(always)]
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(s.to_string().into())) Ok(Self(s.to_string().into()))
} }
} }
impl FromIterator<char> for ImmutableString { impl FromIterator<char> for ImmutableString {
#[inline(always)]
fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self { fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
Self(iter.into_iter().collect::<String>().into()) Self(iter.into_iter().collect::<String>().into())
} }
} }
impl<'a> FromIterator<&'a char> for ImmutableString { impl<'a> FromIterator<&'a char> for ImmutableString {
#[inline(always)]
fn from_iter<T: IntoIterator<Item = &'a char>>(iter: T) -> Self { fn from_iter<T: IntoIterator<Item = &'a char>>(iter: T) -> Self {
Self(iter.into_iter().cloned().collect::<String>().into()) Self(iter.into_iter().cloned().collect::<String>().into())
} }
} }
impl<'a> FromIterator<&'a str> for ImmutableString { impl<'a> FromIterator<&'a str> for ImmutableString {
#[inline(always)]
fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self { fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
Self(iter.into_iter().collect::<String>().into()) Self(iter.into_iter().collect::<String>().into())
} }
} }
impl<'a> FromIterator<String> for ImmutableString { impl<'a> FromIterator<String> for ImmutableString {
#[inline(always)]
fn from_iter<T: IntoIterator<Item = String>>(iter: T) -> Self { fn from_iter<T: IntoIterator<Item = String>>(iter: T) -> Self {
Self(iter.into_iter().collect::<String>().into()) Self(iter.into_iter().collect::<String>().into())
} }
} }
impl fmt::Display for ImmutableString { impl fmt::Display for ImmutableString {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self.0.as_str(), f) fmt::Display::fmt(self.0.as_str(), f)
} }
} }
impl fmt::Debug for ImmutableString { impl fmt::Debug for ImmutableString {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self.0.as_str(), f) fmt::Debug::fmt(self.0.as_str(), f)
} }
@ -818,6 +880,7 @@ impl Add<char> for &ImmutableString {
} }
impl AddAssign<char> for ImmutableString { impl AddAssign<char> for ImmutableString {
#[inline(always)]
fn add_assign(&mut self, rhs: char) { fn add_assign(&mut self, rhs: char) {
self.make_mut().push(rhs); self.make_mut().push(rhs);
} }
@ -832,6 +895,7 @@ impl ImmutableString {
} }
/// Make sure that the `ImmutableString` is unique (i.e. no other outstanding references). /// Make sure that the `ImmutableString` is unique (i.e. no other outstanding references).
/// Then return a mutable reference to the `String`. /// Then return a mutable reference to the `String`.
#[inline(always)]
pub fn make_mut(&mut self) -> &mut String { pub fn make_mut(&mut self) -> &mut String {
shared_make_mut(&mut self.0) shared_make_mut(&mut self.0)
} }