1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
use jrsonnet_gc::{unsafe_empty_trace, Finalize, Trace};
use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize};
use std::{
	cell::RefCell,
	fmt::{self, Display},
	hash::{BuildHasherDefault, Hash, Hasher},
	ops::Deref,
	rc::Rc,
};

#[derive(Clone, PartialOrd, Ord, Eq)]
pub struct IStr(Rc<str>);
impl Finalize for IStr {}
unsafe impl Trace for IStr {
	unsafe_empty_trace!();
}

impl Deref for IStr {
	type Target = str;

	fn deref(&self) -> &Self::Target {
		&self.0
	}
}

impl PartialEq for IStr {
	fn eq(&self, other: &Self) -> bool {
		// It is ok, since all IStr should be inlined into same pool
		Rc::ptr_eq(&self.0, &other.0)
	}
}

impl PartialEq<str> for IStr {
	fn eq(&self, other: &str) -> bool {
		&self.0 as &str == other
	}
}

impl Hash for IStr {
	fn hash<H: Hasher>(&self, state: &mut H) {
		state.write_usize(Rc::as_ptr(&self.0) as *const () as usize)
	}
}

impl Drop for IStr {
	fn drop(&mut self) {
		// First reference - current object, second - POOL
		if Rc::strong_count(&self.0) <= 2 {
			let _result = STR_POOL.try_with(|pool| pool.borrow_mut().remove(&self.0));
		}
	}
}

impl fmt::Debug for IStr {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		write!(f, "{:?}", &self.0)
	}
}

impl Display for IStr {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		f.write_str(&self.0)
	}
}

thread_local! {
	static STR_POOL: RefCell<FxHashMap<Rc<str>, ()>> = RefCell::new(FxHashMap::with_capacity_and_hasher(200, BuildHasherDefault::default()));
}

impl From<&str> for IStr {
	fn from(str: &str) -> Self {
		IStr(STR_POOL.with(|pool| {
			let mut pool = pool.borrow_mut();
			if let Some((k, _)) = pool.get_key_value(str) {
				k.clone()
			} else {
				let rc: Rc<str> = str.into();
				pool.insert(rc.clone(), ());
				rc
			}
		}))
	}
}

impl From<String> for IStr {
	fn from(str: String) -> Self {
		(&str as &str).into()
	}
}

impl Serialize for IStr {
	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
	where
		S: serde::Serializer,
	{
		(&self.0 as &str).serialize(serializer)
	}
}

impl<'de> Deserialize<'de> for IStr {
	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
	where
		D: serde::Deserializer<'de>,
	{
		let s = <&str>::deserialize(deserializer)?;
		Ok(s.into())
	}
}