use crate::prelude::*;
use crate::value::{CheapClone, JsString};
pub struct StringDict {
strings: FxHashMap<Box<str>, JsString>,
}
impl StringDict {
pub fn new() -> Self {
Self {
strings: FxHashMap::default(),
}
}
pub fn with_common_strings() -> Self {
let mut dict = Self::new();
for s in COMMON_STRINGS {
dict.get_or_insert(s);
}
dict
}
pub fn get_or_insert(&mut self, s: &str) -> JsString {
if let Some(existing) = self.strings.get(s) {
return existing.cheap_clone();
}
let js_str = JsString::from(s);
self.strings.insert(s.into(), js_str.cheap_clone());
js_str
}
}
impl Default for StringDict {
fn default() -> Self {
Self::new()
}
}
const COMMON_STRINGS: &[&str] = &[
"length",
"prototype",
"constructor",
"__proto__",
"name",
"message",
"stack",
"value",
"writable",
"enumerable",
"configurable",
"get",
"set",
"toString",
"valueOf",
"hasOwnProperty",
"toJSON",
"next",
"done",
"return",
"throw",
"undefined",
"null",
"boolean",
"number",
"string",
"object",
"function",
"symbol",
"true",
"false",
"Object",
"Array",
"String",
"Number",
"Boolean",
"Function",
"Error",
"TypeError",
"ReferenceError",
"SyntaxError",
"RangeError",
"Map",
"Set",
"Date",
"RegExp",
"Promise",
"Symbol",
"this",
"arguments",
"callee",
"caller",
"log",
"error",
"warn",
"info",
"debug",
"PI",
"E",
"abs",
"floor",
"ceil",
"round",
"max",
"min",
"i",
"j",
"k",
"x",
"y",
"n",
"s",
"v",
"key",
"val",
"arr",
"obj",
"fn",
"cb",
"err",
"res",
"req",
"push",
"pop",
"shift",
"unshift",
"slice",
"splice",
"concat",
"join",
"reverse",
"sort",
"indexOf",
"lastIndexOf",
"includes",
"find",
"findIndex",
"filter",
"map",
"forEach",
"reduce",
"reduceRight",
"every",
"some",
"flat",
"flatMap",
"fill",
"copyWithin",
"at",
"charAt",
"charCodeAt",
"substring",
"substr",
"toLowerCase",
"toUpperCase",
"trim",
"trimStart",
"trimEnd",
"split",
"repeat",
"replace",
"replaceAll",
"padStart",
"padEnd",
"startsWith",
"endsWith",
"match",
"search",
"keys",
"values",
"entries",
"assign",
"freeze",
"seal",
"create",
"defineProperty",
"getOwnPropertyDescriptor",
"toFixed",
"toPrecision",
"toExponential",
"isNaN",
"isFinite",
"isInteger",
"isSafeInteger",
"parseInt",
"parseFloat",
"then",
"catch",
"finally",
"resolve",
"reject",
"all",
"race",
"allSettled",
"any",
"call",
"apply",
"bind",
"source",
"flags",
"global",
"ignoreCase",
"multiline",
"test",
"exec",
"has",
"delete",
"clear",
"size",
"add",
"now",
"UTC",
"parse",
"getTime",
"getFullYear",
"getMonth",
"getDate",
"getDay",
"getHours",
"getMinutes",
"getSeconds",
"getMilliseconds",
"toISOString",
"stringify",
"yield",
"super",
"static",
"extends",
"implements",
];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_string_dict_deduplication() {
let mut dict = StringDict::new();
let s1 = dict.get_or_insert("hello");
let s2 = dict.get_or_insert("hello");
assert_eq!(s1, s2);
assert!(core::ptr::eq(s1.as_str(), s2.as_str()));
}
#[test]
fn test_string_dict_different_strings() {
let mut dict = StringDict::new();
let s1 = dict.get_or_insert("hello");
let s2 = dict.get_or_insert("world");
assert_ne!(s1, s2);
assert!(!core::ptr::eq(s1.as_str(), s2.as_str()));
}
#[test]
fn test_common_strings_preloaded() {
let mut dict = StringDict::with_common_strings();
let s1 = dict.get_or_insert("length");
let s2 = dict.get_or_insert("length");
assert!(core::ptr::eq(s1.as_str(), s2.as_str()));
}
}