#![doc(
html_logo_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg"
)]
#![cfg_attr(not(test), forbid(clippy::unwrap_used))]
#![warn(
clippy::perf,
clippy::single_match_else,
clippy::dbg_macro,
clippy::doc_markdown,
clippy::wildcard_imports,
clippy::struct_excessive_bools,
clippy::doc_markdown,
clippy::semicolon_if_nothing_returned,
clippy::pedantic
)]
#![deny(
clippy::all,
clippy::cast_lossless,
clippy::redundant_closure_for_method_calls,
clippy::use_self,
clippy::unnested_or_patterns,
clippy::trivially_copy_pass_by_ref,
clippy::needless_pass_by_value,
clippy::match_wildcard_for_single_variants,
clippy::map_unwrap_or,
unused_qualifications,
unused_import_braces,
unused_lifetimes,
unreachable_pub,
trivial_numeric_casts,
// rustdoc,
missing_debug_implementations,
missing_copy_implementations,
deprecated_in_future,
meta_variable_misuse,
non_ascii_idents,
rust_2018_compatibility,
rust_2018_idioms,
future_incompatible,
nonstandard_style,
unsafe_op_in_unsafe_fn
)]
#![allow(
clippy::module_name_repetitions,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::cast_precision_loss,
clippy::cast_possible_wrap,
clippy::cast_ptr_alignment,
clippy::missing_panics_doc,
clippy::too_many_lines,
clippy::unreadable_literal,
clippy::missing_inline_in_public_items,
clippy::cognitive_complexity,
clippy::must_use_candidate,
clippy::missing_errors_doc,
clippy::as_conversions,
clippy::let_unit_value,
rustdoc::missing_doc_code_examples
)]
extern crate static_assertions as sa;
mod fixed_string;
mod interned_str;
mod sym;
#[cfg(test)]
mod tests;
use fixed_string::FixedString;
pub use sym::*;
use std::fmt::{Debug, Display};
use interned_str::InternedStr;
use rustc_hash::FxHashMap;
#[derive(Debug, Default)]
pub struct Interner {
symbols: FxHashMap<InternedStr, Sym>,
spans: Vec<InternedStr>,
head: FixedString,
full: Vec<FixedString>,
}
impl Interner {
#[inline]
pub fn new() -> Self {
Self::default()
}
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
Self {
symbols: FxHashMap::default(),
spans: Vec::with_capacity(capacity),
head: FixedString::new(capacity),
full: Vec::new(),
}
}
#[inline]
pub fn len(&self) -> usize {
COMMON_STRINGS.len() + self.spans.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
COMMON_STRINGS.is_empty() && self.spans.is_empty()
}
pub fn get<T>(&self, string: T) -> Option<Sym>
where
T: AsRef<str>,
{
let string = string.as_ref();
Self::get_common(string).or_else(|| self.symbols.get(string).copied())
}
pub fn get_or_intern<T>(&mut self, string: T) -> Sym
where
T: AsRef<str>,
{
let string = string.as_ref();
if let Some(sym) = self.get(string) {
return sym;
}
let interned_str = unsafe {
self.head.push(string).unwrap_or_else(|| {
let new_cap =
(usize::max(self.head.capacity(), string.len()) + 1).next_power_of_two();
let new_head = FixedString::new(new_cap);
let old_head = std::mem::replace(&mut self.head, new_head);
if !old_head.is_empty() {
self.full.push(old_head);
}
self.head.push_unchecked(string)
})
};
unsafe { self.generate_symbol(interned_str) }
}
pub fn get_or_intern_static(&mut self, string: &'static str) -> Sym {
self.get(string).unwrap_or_else(|| {
unsafe { self.generate_symbol(InternedStr::new(string.into())) }
})
}
#[inline]
pub fn resolve(&self, symbol: Sym) -> Option<&str> {
let index = symbol.get() - 1;
COMMON_STRINGS.index(index).copied().or_else(|| {
self.spans.get(index - COMMON_STRINGS.len()).map(|ptr|
unsafe {ptr.as_str()})
})
}
#[inline]
pub fn resolve_expect(&self, symbol: Sym) -> &str {
self.resolve(symbol).expect("string disappeared")
}
fn get_common(string: &str) -> Option<Sym> {
COMMON_STRINGS.get_index(string).map(|idx|
unsafe {
Sym::new_unchecked(idx + 1)
})
}
unsafe fn generate_symbol(&mut self, string: InternedStr) -> Sym {
let next = Sym::new(self.len() + 1).expect("cannot get interner symbol: integer overflow");
self.spans.push(string.clone());
self.symbols.insert(string, next);
next
}
}
pub trait ToInternedString {
fn to_interned_string(&self, interner: &Interner) -> String;
}
impl<T> ToInternedString for T
where
T: Display,
{
fn to_interned_string(&self, _interner: &Interner) -> String {
self.to_string()
}
}