use std::cmp::Ordering;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::ops::Deref;
use crate::base::fnv::FnvMap;
use crate::{
forget_lifetime,
gc::{CloneUnrooted, CopyUnrooted, Gc, Trace},
value::GcStr,
Result,
};
#[derive(Eq)]
pub struct InternedStr(GcStr);
unsafe impl Send for InternedStr {}
unsafe impl Sync for InternedStr {}
unsafe impl CopyUnrooted for InternedStr {}
impl CloneUnrooted for InternedStr {
type Value = Self;
unsafe fn clone_unrooted(&self) -> Self::Value {
self.copy_unrooted()
}
}
unsafe impl Trace for InternedStr {
impl_trace! { self, _gc, {} }
}
impl PartialEq<InternedStr> for InternedStr {
fn eq(&self, other: &InternedStr) -> bool {
self.as_ptr() == other.as_ptr()
}
}
impl<'a> PartialEq<&'a str> for InternedStr {
fn eq(&self, other: &&'a str) -> bool {
**self == **other
}
}
impl PartialOrd for InternedStr {
fn partial_cmp(&self, other: &InternedStr) -> Option<Ordering> {
self.as_ptr().partial_cmp(&other.as_ptr())
}
}
impl Ord for InternedStr {
fn cmp(&self, other: &InternedStr) -> Ordering {
self.as_ptr().cmp(&other.as_ptr())
}
}
impl Hash for InternedStr {
fn hash<H>(&self, hasher: &mut H)
where
H: Hasher,
{
self.as_ptr().hash(hasher)
}
}
impl Deref for InternedStr {
type Target = str;
fn deref(&self) -> &str {
&self.0
}
}
impl AsRef<str> for InternedStr {
fn as_ref(&self) -> &str {
&self.0
}
}
impl InternedStr {
pub fn inner(&self) -> &GcStr {
&self.0
}
}
#[cfg_attr(feature = "serde_derive", derive(DeserializeState, SerializeState))]
#[cfg_attr(
feature = "serde_derive",
serde(
deserialize_state = "crate::serialization::DeSeed<'gc>",
de_parameters = "'gc"
)
)]
#[cfg_attr(
feature = "serde_derive",
serde(serialize_state = "crate::serialization::SeSeed")
)]
pub struct Interner {
#[cfg_attr(feature = "serde_derive", serde(skip))]
indexes: FnvMap<&'static str, InternedStr>,
}
unsafe impl Trace for Interner {
impl_trace! { self, gc,
match self {
Self { indexes, .. } =>
for (_, v) in indexes {
match v {
InternedStr(x) => mark::<GcStr>(x, gc),
}
},
}
}
}
impl Interner {
pub fn new() -> Interner {
Interner {
indexes: FnvMap::default(),
}
}
pub fn intern(&mut self, gc: &mut Gc, s: &str) -> Result<InternedStr> {
unsafe {
match self.indexes.get(s) {
Some(interned_str) => return Ok(interned_str.clone_unrooted()),
None => (),
}
let gc_str = InternedStr(gc.alloc(s)?.unrooted());
let key: &'static str = forget_lifetime(&gc_str);
self.indexes.insert(key, gc_str.clone_unrooted());
Ok(gc_str)
}
}
}
impl fmt::Debug for InternedStr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "InternedStr({:p}, {:?})", &*self.0, self.0)
}
}
impl fmt::Display for InternedStr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", &self[..])
}
}