use std::collections::HashMap;
use somni_expr::{
value::{LoadStore, ValueType},
OperatorError, Type, TypeSet, TypedValue,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct StringIndex(pub usize);
impl StringIndex {
pub const fn dummy() -> Self {
Self(0)
}
}
impl ValueType for StringIndex {
const TYPE: Type = Type::String;
type NegateOutput = Self;
fn equals(a: Self, b: Self) -> Result<bool, OperatorError> {
Ok(a == b)
}
}
impl<T> LoadStore<T> for StringIndex
where
T: TypeSet<String = StringIndex>,
{
type Output<'s>
= Self
where
T: 's;
fn load<'s>(_ctx: &'s T, typed: &'s TypedValue<T>) -> Option<Self::Output<'s>> {
if let TypedValue::String(value) = typed {
return Some(*value);
}
None
}
fn store(&self, _ctx: &mut T) -> TypedValue<T> {
TypedValue::String(*self)
}
}
#[derive(Default, Clone, Debug)]
pub struct Strings {
strings: String,
positions: Vec<(usize, usize)>, }
impl Strings {
pub fn intern(&mut self, value: &str) -> StringIndex {
let start = self.strings.len();
let length = value.len();
self.strings.push_str(value);
let index = self.positions.len();
self.positions.push((start, length));
StringIndex(index)
}
pub fn lookup(&self, idx: StringIndex) -> &str {
let (start, length) = self.positions[idx.0];
&self.strings[start..start + length]
}
pub fn find(&self, name: &str) -> Option<StringIndex> {
for (index, (start, length)) in self.positions.iter().enumerate() {
if &self.strings[*start..*start + *length] == name {
return Some(StringIndex(index));
}
}
None
}
}
#[derive(Default, Clone, Debug)]
pub struct StringInterner {
strings: Strings,
reverse_lookup: HashMap<String, StringIndex>, }
impl StringInterner {
pub fn new() -> Self {
StringInterner::default()
}
pub fn lookup(&self, idx: StringIndex) -> &str {
self.strings.lookup(idx)
}
pub fn lookup_index_by_value(&self, value: &str) -> Option<StringIndex> {
self.reverse_lookup.get(value).cloned()
}
pub fn find(&self, value: &str) -> Option<StringIndex> {
self.reverse_lookup.get(value).copied()
}
pub fn intern(&mut self, value: &str) -> StringIndex {
if let Some(index) = self.find(value) {
return index;
}
let index = self.strings.intern(value);
self.reverse_lookup.insert(value.to_string(), index);
index
}
pub fn finalize(self) -> Strings {
self.strings
}
}