#[cfg(feature = "single-threaded")]
use std::cell::RefCell;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use std::rc::{Rc, Weak};
pub struct Hc<T>
where
T: Hash + Eq,
{
inner: Rc<Inner<T>>,
}
impl<T> Hc<T>
where
T: Hash + Eq,
{
pub fn get(&self) -> &T {
&self.inner.elem
}
}
impl<T: PartialEq> PartialEq for Hc<T>
where
T: Hash + Eq,
{
fn eq(&self, other: &Self) -> bool {
self.inner.elem == other.inner.elem
}
}
impl<T> Eq for Hc<T> where T: Hash + Eq {}
impl<T> Hash for Hc<T>
where
T: Hash + Eq,
{
fn hash<H: Hasher>(&self, state: &mut H) {
self.inner.elem.hash(state);
}
}
impl<T> Clone for Hc<T>
where
T: Hash + Eq,
{
fn clone(&self) -> Self {
Hc {
inner: self.inner.clone(),
}
}
}
impl<T: std::fmt::Debug> std::fmt::Debug for Hc<T>
where
T: Hash + Eq,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.inner.elem.fmt(f)
}
}
impl<T: std::fmt::Display> std::fmt::Display for Hc<T>
where
T: Hash + Eq,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.inner.elem.fmt(f)
}
}
impl<T> std::ops::Deref for Hc<T>
where
T: Hash + Eq,
{
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner.elem
}
}
impl<T> AsRef<T> for Hc<T>
where
T: Hash + Eq,
{
fn as_ref(&self) -> &T {
&self.inner.elem
}
}
impl<T: PartialOrd> PartialOrd for Hc<T>
where
T: Hash + Eq,
{
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.inner.elem.partial_cmp(&other.inner.elem)
}
}
impl<T> Ord for Hc<T>
where
T: Ord + Hash + Eq,
{
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.inner.elem.cmp(&other.inner.elem)
}
}
pub struct HcTable<T>
where
T: Hash + Eq,
{
inner: Rc<InnerTable<T>>,
}
impl<T> HcTable<T>
where
T: Hash + Eq,
{
pub fn new() -> Self {
HcTable {
inner: Rc::new(InnerTable::new()),
}
}
pub fn hashcons(&self, value: T) -> Hc<T> {
Hc {
inner: self.intern(value),
}
}
fn intern(&self, value: T) -> Rc<Inner<T>> {
let rc_table = self.inner.clone();
let rc_table_dup = Rc::clone(&rc_table);
let mut mut_table = rc_table_dup.table.borrow_mut();
let rc_value = Rc::new(value);
let rc_val_dup = rc_value.clone();
match mut_table.entry(rc_val_dup) {
Entry::Occupied(mut o) => {
let weak_hc = o.get();
if let Some(rc_hc) = weak_hc.upgrade() {
return rc_hc;
}
let elem = rc_value;
let _table = rc_table;
let new_elem = Rc::new(Inner { elem, _table });
o.insert(Rc::downgrade(&new_elem));
new_elem
}
Entry::Vacant(v) => {
let _table = rc_table;
let elem = rc_value;
let new_elem = Rc::new(Inner { elem, _table });
v.insert(Rc::downgrade(&new_elem));
new_elem
}
}
}
#[cfg(not(feature = "auto-cleanup"))]
pub fn cleanup(&self) {
self.inner.cleanup();
}
pub fn len(&self) -> usize {
self.inner.len()
}
}
impl<T> Clone for HcTable<T>
where
T: Hash + Eq,
{
fn clone(&self) -> Self {
HcTable {
inner: self.inner.clone(),
}
}
}
struct Inner<T>
where
T: Hash + Eq,
{
elem: Rc<T>,
_table: Rc<InnerTable<T>>,
}
#[cfg(feature = "auto-cleanup")]
impl<T> Drop for Inner<T>
where
T: Hash + Eq,
{
fn drop(&mut self) {
let rc_table = self._table.clone();
let key = self.elem.clone();
let mut mut_table = rc_table.table.borrow_mut();
mut_table.remove_entry(&key);
}
}
pub struct InnerTable<T>
where
T: Hash + Eq,
{
table: RefCell<HashMap<Rc<T>, Weak<Inner<T>>>>,
}
impl<T> InnerTable<T>
where
T: Hash + Eq,
{
fn new() -> Self {
InnerTable {
table: RefCell::new(HashMap::new()),
}
}
fn len(&self) -> usize {
self.table.borrow().len()
}
#[cfg(not(feature = "auto-cleanup"))]
fn cleanup(&self) {
loop {
let mut mut_table = self.table.borrow_mut();
let prev_len = mut_table.len();
mut_table.retain(|_, weak_hc: &mut Weak<Inner<T>>| weak_hc.strong_count() > 0);
if mut_table.len() == prev_len {
break;
}
}
}
}