Skip to content

Commit

Permalink
Refine strings interner.
Browse files Browse the repository at this point in the history
  • Loading branch information
schungx committed Feb 11, 2024
1 parent f6e23f7 commit b2fc03d
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 87 deletions.
29 changes: 10 additions & 19 deletions src/api/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
use crate::func::native::locked_write;
use crate::parser::{ParseResult, ParseState};
use crate::types::StringsInterner;
use crate::{Engine, OptimizationLevel, Scope, AST};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
Expand Down Expand Up @@ -220,15 +219,11 @@ impl Engine {
) -> ParseResult<AST> {
let (stream, tc) = self.lex(scripts.as_ref());

let mut interner;
let mut guard;
let interned_strings = if let Some(ref interner) = self.interned_strings {
guard = locked_write(interner).unwrap();
&mut *guard
} else {
interner = StringsInterner::new();
&mut interner
};
let guard = &mut self
.interned_strings
.as_ref()
.and_then(|interner| locked_write(interner));
let interned_strings = guard.as_deref_mut();

let input = &mut stream.peekable();
let lib = &mut <_>::default();
Expand Down Expand Up @@ -304,15 +299,11 @@ impl Engine {
let scripts = [script];
let (stream, t) = self.lex(&scripts);

let mut interner;
let mut guard;
let interned_strings = if let Some(ref interner) = self.interned_strings {
guard = locked_write(interner).unwrap();
&mut *guard
} else {
interner = StringsInterner::new();
&mut interner
};
let guard = &mut self
.interned_strings
.as_ref()
.and_then(|interner| locked_write(interner));
let interned_strings = guard.as_deref_mut();

let input = &mut stream.peekable();
let lib = &mut <_>::default();
Expand Down
15 changes: 5 additions & 10 deletions src/api/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use crate::eval::{Caches, GlobalRuntimeState};
use crate::func::native::locked_write;
use crate::parser::ParseState;
use crate::types::dynamic::Variant;
use crate::types::StringsInterner;
use crate::{Dynamic, Engine, Position, RhaiResult, RhaiResultOf, Scope, AST, ERR};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
Expand Down Expand Up @@ -115,15 +114,11 @@ impl Engine {
) -> RhaiResultOf<T> {
let scripts = [script];
let ast = {
let mut interner;
let mut guard;
let interned_strings = if let Some(ref interner) = self.interned_strings {
guard = locked_write(interner).unwrap();
&mut *guard
} else {
interner = StringsInterner::new();
&mut interner
};
let guard = &mut self
.interned_strings
.as_ref()
.and_then(|interner| locked_write(interner));
let interned_strings = guard.as_deref_mut();

let (stream, tc) = self.lex(&scripts);

Expand Down
11 changes: 8 additions & 3 deletions src/api/formatting.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Module that provide formatting services to the [`Engine`].
use crate::func::locked_write;
use crate::packages::iter_basic::{BitRange, CharsStream, StepRange};
use crate::parser::{ParseResult, ParseState};
use crate::types::StringsInterner;
use crate::{
Engine, ExclusiveRange, FnPtr, ImmutableString, InclusiveRange, Position, RhaiError,
SmartString, ERR,
Expand Down Expand Up @@ -264,11 +264,16 @@ impl Engine {

tc.borrow_mut().compressed = Some(String::new());
stream.state.last_token = Some(SmartString::new_const());
let mut interner = StringsInterner::new();

let guard = &mut self
.interned_strings
.as_ref()
.and_then(|interner| locked_write(interner));
let interned_strings = guard.as_deref_mut();

let input = &mut stream.peekable();
let lib = &mut <_>::default();
let mut state = ParseState::new(None, &mut interner, input, tc, lib);
let mut state = ParseState::new(None, interned_strings, input, tc, lib);

let mut _ast = self.parse(
&mut state,
Expand Down
15 changes: 5 additions & 10 deletions src/api/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use crate::func::native::locked_write;
use crate::parser::{ParseSettingFlags, ParseState};
use crate::tokenizer::Token;
use crate::types::dynamic::Union;
use crate::types::StringsInterner;
use crate::{Dynamic, Engine, LexError, Map, RhaiResultOf};
use std::fmt::Write;
#[cfg(feature = "no_std")]
Expand Down Expand Up @@ -118,15 +117,11 @@ impl Engine {
);

let ast = {
let mut interner;
let mut guard;
let interned_strings = if let Some(ref interner) = self.interned_strings {
guard = locked_write(interner).unwrap();
&mut *guard
} else {
interner = StringsInterner::new();
&mut interner
};
let guard = &mut self
.interned_strings
.as_ref()
.and_then(|interner| locked_write(interner));
let interned_strings = guard.as_deref_mut();

let input = &mut stream.peekable();
let lib = &mut <_>::default();
Expand Down
28 changes: 28 additions & 0 deletions src/api/limits.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! Settings for [`Engine`]'s limitations.
#![cfg(not(feature = "unchecked"))]

use crate::func::{locked_read, locked_write};
use crate::types::StringsInterner;
use crate::Engine;
use std::num::{NonZeroU64, NonZeroUsize};
#[cfg(feature = "no_std")]
Expand All @@ -20,6 +22,8 @@ pub mod default_limits {
/// Not available under `no_function`.
#[cfg(not(feature = "no_function"))]
pub const MAX_FUNCTION_EXPR_DEPTH: usize = 16;
/// Maximum number of strings interned.
pub const MAX_STRINGS_INTERNED: usize = 256;
}
#[cfg(not(debug_assertions))]
pub mod default_limits {
Expand All @@ -35,6 +39,8 @@ pub mod default_limits {
/// Not available under `no_function`.
#[cfg(not(feature = "no_function"))]
pub const MAX_FUNCTION_EXPR_DEPTH: usize = 32;
/// Maximum number of strings interned.
pub const MAX_STRINGS_INTERNED: usize = 1024;
}

/// A type containing all the limits imposed by the [`Engine`].
Expand Down Expand Up @@ -344,4 +350,26 @@ impl Engine {
#[cfg(feature = "no_object")]
return 0;
}
/// Set the maximum number of strings to be interned.
#[inline(always)]
pub fn set_max_strings_interned(&mut self, max: usize) -> &mut Self {
if max == 0 {
self.interned_strings = None;
} else if let Some(ref interner) = self.interned_strings {
if let Some(mut guard) = locked_write(interner) {
guard.set_max(max);
}
} else {
self.interned_strings = Some(StringsInterner::new(self.max_strings_interned()).into());
}
self
}
/// The maximum number of strings to be interned.
#[inline(always)]
#[must_use]
pub fn max_strings_interned(&self) -> usize {
self.interned_strings.as_ref().map_or(0, |interner| {
locked_read(interner).map_or(0, |guard| guard.max())
})
}
}
15 changes: 5 additions & 10 deletions src/api/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
use crate::eval::Caches;
use crate::func::native::locked_write;
use crate::parser::ParseState;
use crate::types::StringsInterner;
use crate::{Engine, RhaiResultOf, Scope, AST};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
Expand Down Expand Up @@ -61,15 +60,11 @@ impl Engine {
let ast = {
let (stream, tc) = self.lex(&scripts);

let mut interner;
let mut guard;
let interned_strings = if let Some(ref interner) = self.interned_strings {
guard = locked_write(interner).unwrap();
&mut *guard
} else {
interner = StringsInterner::new();
&mut interner
};
let guard = &mut self
.interned_strings
.as_ref()
.and_then(|interner| locked_write(interner));
let interned_strings = guard.as_deref_mut();

let input = &mut stream.peekable();
let lib = &mut <_>::default();
Expand Down
5 changes: 3 additions & 2 deletions src/engine.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Main module defining the script evaluation [`Engine`].
use crate::api::limits::default_limits::MAX_STRINGS_INTERNED;

Check failure on line 3 in src/engine.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,no_time,no_function,no_float,no_position,no_inde...

failed to resolve: could not find `limits` in `api`

Check failure on line 3 in src/engine.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,sync,no_time,no_function,no_float,no_position,no...

failed to resolve: could not find `limits` in `api`

Check failure on line 3 in src/engine.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,unchecked,serde,metadata,internals,debugging, st...

failed to resolve: could not find `limits` in `api`
use crate::api::options::LangOptions;
use crate::func::native::{
locked_write, OnDebugCallback, OnDefVarCallback, OnParseTokenCallback, OnPrintCallback,
Expand Down Expand Up @@ -96,7 +97,7 @@ pub struct Engine {
pub(crate) module_resolver: Option<Box<dyn crate::ModuleResolver>>,

/// Strings interner.
pub(crate) interned_strings: Option<Box<Locked<StringsInterner>>>,
pub(crate) interned_strings: Option<Locked<StringsInterner>>,

/// A set of symbols to disable.
pub(crate) disabled_symbols: BTreeSet<Identifier>,
Expand Down Expand Up @@ -279,7 +280,7 @@ impl Engine {
Some(Box::new(crate::module::resolvers::FileModuleResolver::new()));
}

engine.interned_strings = Some(Locked::new(StringsInterner::new()).into());
engine.set_max_strings_interned(MAX_STRINGS_INTERNED);

Check failure on line 283 in src/engine.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,no_time,no_function,no_float,no_position,no_inde...

no method named `set_max_strings_interned` found for struct `Engine` in the current scope

Check failure on line 283 in src/engine.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,sync,no_time,no_function,no_float,no_position,no...

no method named `set_max_strings_interned` found for struct `Engine` in the current scope

Check failure on line 283 in src/engine.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,unchecked,serde,metadata,internals,debugging, st...

no method named `set_max_strings_interned` found for struct `engine::Engine` in the current scope

// default print/debug implementations
#[cfg(not(feature = "no_std"))]
Expand Down
45 changes: 27 additions & 18 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ pub struct ParseState<'a, 't, 's, 'f> {
/// Controls whether parsing of an expression should stop given the next token.
pub expr_filter: fn(&Token) -> bool,
/// Strings interner.
pub interned_strings: &'s mut StringsInterner,
pub interned_strings: Option<&'s mut StringsInterner>,
/// External [scope][Scope] with constants.
pub external_constants: Option<&'a Scope<'a>>,
/// Global runtime state.
Expand Down Expand Up @@ -124,7 +124,7 @@ impl<'a, 't, 's, 'f> ParseState<'a, 't, 's, 'f> {
#[must_use]
pub fn new(
external_constants: Option<&'a Scope>,
interned_strings: &'s mut StringsInterner,
interned_strings: Option<&'s mut StringsInterner>,
input: &'t mut TokenStream<'a>,
tokenizer_control: TokenizerControl,
#[cfg(not(feature = "no_function"))] lib: &'f mut FnLib,
Expand Down Expand Up @@ -208,7 +208,7 @@ impl<'a, 't, 's, 'f> ParseState<'a, 't, 's, 'f> {
#[cfg(not(feature = "no_closure"))]
if self.allow_capture {
if !is_func_name && index == 0 && !self.external_vars.iter().any(|v| v.name == name) {
let name = self.interned_strings.get(name);
let name = self.get_interned_string(name);
self.external_vars.push(Ident { name, pos: _pos });
}
} else {
Expand Down Expand Up @@ -247,7 +247,10 @@ impl<'a, 't, 's, 'f> ParseState<'a, 't, 's, 'f> {
&mut self,
text: impl AsRef<str> + Into<ImmutableString>,
) -> ImmutableString {
self.interned_strings.get(text)
match self.interned_strings {
Some(ref mut interner) => interner.get(text),
None => text.into(),
}
}

/// Get an interned property getter, creating one if it is not yet interned.
Expand All @@ -258,11 +261,14 @@ impl<'a, 't, 's, 'f> ParseState<'a, 't, 's, 'f> {
&mut self,
text: impl AsRef<str> + Into<ImmutableString>,
) -> ImmutableString {
self.interned_strings.get_with_mapper(
b'g',
|s| crate::engine::make_getter(s.as_ref()).into(),
text,
)
match self.interned_strings {
Some(ref mut interner) => interner.get_with_mapper(
b'g',
|s| crate::engine::make_getter(s.as_ref()).into(),
text,
),
None => crate::engine::make_getter(text.as_ref()).into(),
}
}

/// Get an interned property setter, creating one if it is not yet interned.
Expand All @@ -273,11 +279,14 @@ impl<'a, 't, 's, 'f> ParseState<'a, 't, 's, 'f> {
&mut self,
text: impl AsRef<str> + Into<ImmutableString>,
) -> ImmutableString {
self.interned_strings.get_with_mapper(
b's',
|s| crate::engine::make_setter(s.as_ref()).into(),
text,
)
match self.interned_strings {
Some(ref mut interner) => interner.get_with_mapper(
b's',
|s| crate::engine::make_setter(s.as_ref()).into(),
text,
),
None => crate::engine::make_setter(text.as_ref()).into(),
}
}
}

Expand Down Expand Up @@ -1465,7 +1474,7 @@ impl Engine {
#[cfg(not(feature = "no_function"))]
Token::Pipe | Token::Or if settings.has_option(LangOptions::ANON_FN) => {
// Build new parse state
let new_interner = &mut StringsInterner::new();
let new_interner = None;
let new_state = &mut ParseState::new(
state.external_constants,
new_interner,
Expand All @@ -1475,7 +1484,7 @@ impl Engine {
);

// We move the strings interner to the new parse state object by swapping it...
std::mem::swap(state.interned_strings, new_state.interned_strings);
std::mem::swap(&mut state.interned_strings, &mut new_state.interned_strings);

#[cfg(not(feature = "no_module"))]
{
Expand Down Expand Up @@ -1510,7 +1519,7 @@ impl Engine {
let result = self.parse_anon_fn(new_state, new_settings.level_up()?);

// Restore the strings interner by swapping it back
std::mem::swap(state.interned_strings, new_state.interned_strings);
std::mem::swap(&mut state.interned_strings, &mut new_state.interned_strings);

let (expr, fn_def, _externals) = result?;

Expand Down Expand Up @@ -3270,7 +3279,7 @@ impl Engine {
// Build new parse state
let new_state = &mut ParseState::new(
state.external_constants,
state.interned_strings,
state.interned_strings.as_deref_mut(),
state.input,
state.tokenizer_control.clone(),
state.lib,
Expand Down
Loading

0 comments on commit b2fc03d

Please sign in to comment.