Remove Interners struct.

This commit is contained in:
Stephen Chung 2022-08-14 14:32:16 +08:00
parent f7d910fa55
commit b75ea94908
2 changed files with 44 additions and 46 deletions

View File

@ -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?;

View File

@ -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();
} }