Remove Interners struct.
This commit is contained in:
parent
f7d910fa55
commit
b75ea94908
@ -43,22 +43,17 @@ const NEVER_ENDS: &str = "`Token`";
|
|||||||
/// Unroll `switch` ranges no larger than this.
|
/// Unroll `switch` ranges no larger than this.
|
||||||
const SMALL_SWITCH_RANGE: usize = 16;
|
const SMALL_SWITCH_RANGE: usize = 16;
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
const NUM_INTERNERS: usize = if cfg!(feature = "no_object") { 1 } else { 3 };
|
||||||
pub struct InternedStrings<'e> {
|
|
||||||
pub main: StringsInterner<'e>,
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
pub getters: StringsInterner<'e>,
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
pub setters: StringsInterner<'e>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// _(internals)_ A type that encapsulates the current state of the parser.
|
/// _(internals)_ A type that encapsulates the current state of the parser.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
pub struct ParseState<'e> {
|
pub struct ParseState<'e> {
|
||||||
/// Input stream buffer containing the next character to read.
|
/// Input stream buffer containing the next character to read.
|
||||||
pub tokenizer_control: TokenizerControl,
|
pub tokenizer_control: TokenizerControl,
|
||||||
|
/// Controls whether parsing of an expression should stop given the next token.
|
||||||
|
pub expr_filter: fn(&Token) -> bool,
|
||||||
/// String interners.
|
/// String interners.
|
||||||
interned_strings: InternedStrings<'e>,
|
interned_strings: [StringsInterner<'e>; NUM_INTERNERS],
|
||||||
/// External [scope][Scope] with constants.
|
/// External [scope][Scope] with constants.
|
||||||
pub scope: &'e Scope<'e>,
|
pub scope: &'e Scope<'e>,
|
||||||
/// Global runtime state.
|
/// Global runtime state.
|
||||||
@ -67,8 +62,6 @@ pub struct ParseState<'e> {
|
|||||||
pub stack: Scope<'e>,
|
pub stack: Scope<'e>,
|
||||||
/// Size of the local variables stack upon entry of the current block scope.
|
/// Size of the local variables stack upon entry of the current block scope.
|
||||||
pub block_stack_len: usize,
|
pub block_stack_len: usize,
|
||||||
/// Controls whether parsing of an expression should stop given the next token.
|
|
||||||
pub expr_filter: fn(&Token) -> bool,
|
|
||||||
/// Tracks a list of external variables (variables that are not explicitly declared in the scope).
|
/// Tracks a list of external variables (variables that are not explicitly declared in the scope).
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
pub external_vars: Vec<crate::ast::Ident>,
|
pub external_vars: Vec<crate::ast::Ident>,
|
||||||
@ -118,7 +111,7 @@ impl<'e> ParseState<'e> {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
engine: &Engine,
|
engine: &Engine,
|
||||||
scope: &'e Scope,
|
scope: &'e Scope,
|
||||||
interners: InternedStrings<'e>,
|
interned_strings: [StringsInterner<'e>; NUM_INTERNERS],
|
||||||
tokenizer_control: TokenizerControl,
|
tokenizer_control: TokenizerControl,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -128,7 +121,7 @@ impl<'e> ParseState<'e> {
|
|||||||
external_vars: Vec::new(),
|
external_vars: Vec::new(),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
allow_capture: true,
|
allow_capture: true,
|
||||||
interned_strings: interners,
|
interned_strings,
|
||||||
scope,
|
scope,
|
||||||
global: GlobalRuntimeState::new(engine),
|
global: GlobalRuntimeState::new(engine),
|
||||||
stack: Scope::new(),
|
stack: Scope::new(),
|
||||||
@ -250,40 +243,35 @@ impl<'e> ParseState<'e> {
|
|||||||
|
|
||||||
/// Get an interned string, creating one if it is not yet interned.
|
/// Get an interned string, creating one if it is not yet interned.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[allow(dead_code)]
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_interned_string(
|
pub fn get_interned_string(
|
||||||
&mut self,
|
&mut self,
|
||||||
text: impl AsRef<str> + Into<ImmutableString>,
|
text: impl AsRef<str> + Into<ImmutableString>,
|
||||||
) -> ImmutableString {
|
) -> ImmutableString {
|
||||||
self.interned_strings.main.get(text)
|
self.interned_strings[0].get(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an interned property getter, creating one if it is not yet interned.
|
/// Get an interned property getter, creating one if it is not yet interned.
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[allow(dead_code)]
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_interned_getter(
|
pub fn get_interned_getter(
|
||||||
&mut self,
|
&mut self,
|
||||||
text: impl AsRef<str> + Into<ImmutableString>,
|
text: impl AsRef<str> + Into<ImmutableString>,
|
||||||
) -> ImmutableString {
|
) -> ImmutableString {
|
||||||
self.interned_strings
|
self.interned_strings[1]
|
||||||
.getters
|
|
||||||
.get_with_mapper(|s| crate::engine::make_getter(s.as_ref()).into(), text)
|
.get_with_mapper(|s| crate::engine::make_getter(s.as_ref()).into(), text)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an interned property setter, creating one if it is not yet interned.
|
/// Get an interned property setter, creating one if it is not yet interned.
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[allow(dead_code)]
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_interned_setter(
|
pub fn get_interned_setter(
|
||||||
&mut self,
|
&mut self,
|
||||||
text: impl AsRef<str> + Into<ImmutableString>,
|
text: impl AsRef<str> + Into<ImmutableString>,
|
||||||
) -> ImmutableString {
|
) -> ImmutableString {
|
||||||
self.interned_strings
|
self.interned_strings[2]
|
||||||
.setters
|
|
||||||
.get_with_mapper(|s| crate::engine::make_setter(s.as_ref()).into(), text)
|
.get_with_mapper(|s| crate::engine::make_setter(s.as_ref()).into(), text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1384,12 +1372,13 @@ impl Engine {
|
|||||||
// | ...
|
// | ...
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
Token::Pipe | Token::Or if settings.options.contains(LangOptions::ANON_FN) => {
|
Token::Pipe | Token::Or if settings.options.contains(LangOptions::ANON_FN) => {
|
||||||
let interners = std::mem::take(&mut state.interned_strings);
|
// Build new parse state
|
||||||
|
let interned_strings = std::mem::take(&mut state.interned_strings);
|
||||||
|
|
||||||
let mut new_state = ParseState::new(
|
let mut new_state = ParseState::new(
|
||||||
self,
|
self,
|
||||||
state.scope,
|
state.scope,
|
||||||
interners,
|
interned_strings,
|
||||||
state.tokenizer_control.clone(),
|
state.tokenizer_control.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1435,6 +1424,7 @@ impl Engine {
|
|||||||
|
|
||||||
let result = self.parse_anon_fn(input, &mut new_state, lib, new_settings);
|
let result = self.parse_anon_fn(input, &mut new_state, lib, new_settings);
|
||||||
|
|
||||||
|
// Restore parse state
|
||||||
state.interned_strings = new_state.interned_strings;
|
state.interned_strings = new_state.interned_strings;
|
||||||
|
|
||||||
let (expr, func) = result?;
|
let (expr, func) = result?;
|
||||||
@ -3259,12 +3249,13 @@ impl Engine {
|
|||||||
|
|
||||||
match input.next().expect(NEVER_ENDS) {
|
match input.next().expect(NEVER_ENDS) {
|
||||||
(Token::Fn, pos) => {
|
(Token::Fn, pos) => {
|
||||||
let interners = std::mem::take(&mut state.interned_strings);
|
// Build new parse state
|
||||||
|
let interned_strings = std::mem::take(&mut state.interned_strings);
|
||||||
|
|
||||||
let mut new_state = ParseState::new(
|
let mut new_state = ParseState::new(
|
||||||
self,
|
self,
|
||||||
state.scope,
|
state.scope,
|
||||||
interners,
|
interned_strings,
|
||||||
state.tokenizer_control.clone(),
|
state.tokenizer_control.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -3314,6 +3305,7 @@ impl Engine {
|
|||||||
comments,
|
comments,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Restore parse state
|
||||||
state.interned_strings = new_state.interned_strings;
|
state.interned_strings = new_state.interned_strings;
|
||||||
|
|
||||||
let func = func?;
|
let func = func?;
|
||||||
|
@ -5,6 +5,7 @@ use crate::ImmutableString;
|
|||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{
|
use std::{
|
||||||
collections::BTreeMap,
|
collections::BTreeMap,
|
||||||
|
fmt,
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
ops::AddAssign,
|
ops::AddAssign,
|
||||||
@ -20,10 +21,12 @@ pub const MAX_STRING_LEN: usize = 24;
|
|||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// Normal identifiers, property getters and setters are interned separately.
|
/// Normal identifiers, property getters and setters are interned separately.
|
||||||
#[derive(Debug, Clone, Hash)]
|
#[derive(Clone, Hash)]
|
||||||
pub struct StringsInterner<'a> {
|
pub struct StringsInterner<'a> {
|
||||||
/// Maximum capacity.
|
/// Maximum number of strings interned.
|
||||||
max: usize,
|
pub capacity: usize,
|
||||||
|
/// Maximum string length.
|
||||||
|
pub max_string_len: usize,
|
||||||
/// Normal strings.
|
/// Normal strings.
|
||||||
strings: BTreeMap<u64, ImmutableString>,
|
strings: BTreeMap<u64, ImmutableString>,
|
||||||
/// Take care of the lifetime parameter.
|
/// Take care of the lifetime parameter.
|
||||||
@ -37,24 +40,26 @@ impl Default for StringsInterner<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for StringsInterner<'_> {
|
||||||
|
#[inline]
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_list().entries(self.strings.values()).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl StringsInterner<'_> {
|
impl StringsInterner<'_> {
|
||||||
/// Create a new [`StringsInterner`].
|
/// Create a new [`StringsInterner`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::new_with_capacity(MAX_INTERNED_STRINGS)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new [`StringsInterner`] with maximum capacity.
|
|
||||||
#[inline]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_with_capacity(capacity: usize) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
max: capacity,
|
capacity: MAX_INTERNED_STRINGS,
|
||||||
|
max_string_len: MAX_STRING_LEN,
|
||||||
strings: BTreeMap::new(),
|
strings: BTreeMap::new(),
|
||||||
dummy: PhantomData,
|
dummy: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an identifier from a text string, adding it to the interner if necessary.
|
/// Get an identifier from a text string, adding it to the interner if necessary.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -72,12 +77,9 @@ impl StringsInterner<'_> {
|
|||||||
) -> ImmutableString {
|
) -> ImmutableString {
|
||||||
let key = text.as_ref();
|
let key = text.as_ref();
|
||||||
|
|
||||||
// Do not intern numbers
|
// Do not intern long strings or numbers
|
||||||
if key.bytes().all(|c| c == b'.' || (c >= b'0' && c <= b'9')) {
|
if key.len() > MAX_STRING_LEN || key.bytes().all(|c| c == b'.' || (c >= b'0' && c <= b'9'))
|
||||||
return text.into();
|
{
|
||||||
}
|
|
||||||
|
|
||||||
if key.len() > MAX_STRING_LEN {
|
|
||||||
return mapper(text);
|
return mapper(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,11 +100,15 @@ impl StringsInterner<'_> {
|
|||||||
self.strings.insert(key, value.clone());
|
self.strings.insert(key, value.clone());
|
||||||
|
|
||||||
// If the interner is over capacity, remove the longest entry
|
// If the interner is over capacity, remove the longest entry
|
||||||
if self.strings.len() > self.max {
|
if self.strings.len() > self.capacity {
|
||||||
// Leave some buffer to grow when shrinking the cache.
|
// Leave some buffer to grow when shrinking the cache.
|
||||||
// We leave at least two entries, one for the empty string, and one for the string
|
// We leave at least two entries, one for the empty string, and one for the string
|
||||||
// that has just been inserted.
|
// that has just been inserted.
|
||||||
let max = if self.max < 5 { 2 } else { self.max - 3 };
|
let max = if self.capacity < 5 {
|
||||||
|
2
|
||||||
|
} else {
|
||||||
|
self.capacity - 3
|
||||||
|
};
|
||||||
|
|
||||||
while self.strings.len() > max {
|
while self.strings.len() > max {
|
||||||
let (_, n) = self.strings.iter().fold((0, 0), |(x, n), (&k, v)| {
|
let (_, n) = self.strings.iter().fold((0, 0), |(x, n), (&k, v)| {
|
||||||
@ -127,7 +133,7 @@ impl StringsInterner<'_> {
|
|||||||
self.strings.len()
|
self.strings.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Number of strings interned.
|
/// Are there no interned strings?
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
@ -135,7 +141,7 @@ impl StringsInterner<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Clear all interned strings.
|
/// Clear all interned strings.
|
||||||
#[inline]
|
#[inline(always)]
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.strings.clear();
|
self.strings.clear();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user