#![deny(warnings)]
use std::{
borrow::Borrow,
cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd},
collections::{hash_map::RandomState, HashMap},
fmt,
hash::{BuildHasher, Hash, Hasher},
ops::Deref,
sync::{Arc, RwLock, Weak},
};
pub extern crate lazy_static;
#[cfg(test)]
mod test;
#[macro_export]
macro_rules! consign {
(
$(#[$meta:meta])*
let $name:ident = consign($capa:expr) for $typ:ty ;
) => (
$crate::lazy_static::lazy_static! {
$(#[$meta])*
static ref $name: std::sync::RwLock<
$crate::HConsign<$typ>
> = std::sync::RwLock::new(
$crate::HConsign::with_capacity( $capa )
);
}
);
(
$(#[$meta:meta])*
let $name:ident = consign($capa:expr, $hash_builder:expr) for $typ:ty ;
) => (
$crate::lazy_static::lazy_static! {
$(#[$meta])*
static ref $name: std::sync::RwLock<
$crate::HConsign<$typ>
> = std::sync::RwLock::new(
$crate::HConsign::with_capacity_and_hasher( $capa, $hash_builder )
);
}
);
}
#[deprecated(
since = "1.4.0",
note = "Deprecated for poor performance. We recommend the hash_coll module instead."
)]
pub mod coll;
pub mod hash_coll;
pub trait HashConsed {
type Inner;
}
pub struct HConsed<T> {
elm: Arc<T>,
uid: u64,
}
impl<T> HashConsed for HConsed<T> {
type Inner = T;
}
impl<T> HConsed<T> {
#[inline]
pub fn get(&self) -> &T {
self.elm.deref()
}
#[inline]
pub fn uid(&self) -> u64 {
self.uid
}
#[inline]
pub fn to_weak(&self) -> WHConsed<T> {
WHConsed {
elm: Arc::downgrade(&self.elm),
uid: self.uid,
}
}
pub fn to_weak_ref(&self) -> Weak<T> {
Arc::downgrade(&self.elm)
}
pub fn arc_count(&self) -> usize {
Arc::strong_count(&self.elm)
}
}
impl<T: fmt::Debug> fmt::Debug for HConsed<T> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "{:?}", self.elm)
}
}
impl<T> Clone for HConsed<T> {
fn clone(&self) -> Self {
HConsed {
elm: self.elm.clone(),
uid: self.uid,
}
}
}
impl<T> PartialEq for HConsed<T> {
#[inline]
fn eq(&self, rhs: &Self) -> bool {
self.uid == rhs.uid
}
}
impl<T> Eq for HConsed<T> {}
impl<T> PartialOrd for HConsed<T> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<T> Ord for HConsed<T> {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.uid.cmp(&other.uid)
}
}
impl<T: Hash> Hash for HConsed<T> {
#[inline]
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
self.uid.hash(state)
}
}
impl<T> Deref for HConsed<T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
self.elm.deref()
}
}
impl<T> Borrow<T> for HConsed<T> {
fn borrow(&self) -> &T {
self.elm.borrow()
}
}
impl<T: fmt::Display> fmt::Display for HConsed<T> {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
self.elm.fmt(fmt)
}
}
pub struct WHConsed<T> {
elm: Weak<T>,
uid: u64,
}
impl<T> WHConsed<T> {
pub fn to_hconsed(&self) -> Option<HConsed<T>> {
self.elm.upgrade().map(|arc| HConsed {
elm: arc,
uid: self.uid,
})
}
pub fn as_weak_ref(&self) -> &Weak<T> {
&self.elm
}
}
impl<T: fmt::Display> fmt::Display for WHConsed<T> {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
if let Some(arc) = self.elm.upgrade() {
arc.fmt(fmt)
} else {
write!(fmt, "<freed>")
}
}
}
impl<T> Hash for WHConsed<T> {
#[inline]
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
self.uid.hash(state)
}
}
impl<T> PartialEq for WHConsed<T> {
#[inline]
fn eq(&self, rhs: &Self) -> bool {
self.uid == rhs.uid
}
}
impl<T> Eq for WHConsed<T> {}
impl<T> PartialOrd for WHConsed<T> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<T> Ord for WHConsed<T> {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.uid.cmp(&other.uid)
}
}
#[cfg(feature = "weak-table")]
use weak_table::traits::{WeakElement, WeakKey};
#[cfg(feature = "weak-table")]
impl<T> WeakElement for WHConsed<T> {
type Strong = HConsed<T>;
fn new(x: &Self::Strong) -> Self {
x.to_weak()
}
fn view(&self) -> Option<Self::Strong> {
self.to_hconsed()
}
fn is_expired(&self) -> bool {
self.elm.is_expired()
}
fn clone(x: &Self::Strong) -> Self::Strong
where
Self: Sized,
{
x.clone()
}
}
#[cfg(feature = "weak-table")]
impl<T: std::hash::Hash + Eq> WeakKey for WHConsed<T> {
type Key = T;
fn with_key<F, R>(view: &Self::Strong, f: F) -> R
where
F: FnOnce(&Self::Key) -> R,
{
f(view)
}
}
pub struct HConsign<T: Hash + Eq + Clone, S = RandomState> {
table: HashMap<T, WHConsed<T>, S>,
count: u64,
}
impl<T: Hash + Eq + Clone> HConsign<T, RandomState> {
#[inline]
pub fn empty() -> Self {
HConsign {
table: HashMap::new(),
count: 0,
}
}
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
HConsign {
table: HashMap::with_capacity(capacity),
count: 0,
}
}
}
impl<T: Hash + Eq + Clone, S> HConsign<T, S> {
#[inline]
pub fn fold<Acc, F>(&self, mut init: Acc, mut f: F) -> Acc
where
F: FnMut(Acc, HConsed<T>) -> Acc,
{
for weak in self.table.values() {
if let Some(consed) = weak.to_hconsed() {
init = f(init, consed)
}
}
init
}
#[inline]
pub fn fold_res<Acc, F, E>(&self, mut init: Acc, mut f: F) -> Result<Acc, E>
where
F: FnMut(Acc, HConsed<T>) -> Result<Acc, E>,
{
for weak in self.table.values() {
if let Some(consed) = weak.to_hconsed() {
init = f(init, consed)?
}
}
Ok(init)
}
#[inline]
pub fn iter(&self) -> impl ExactSizeIterator<Item = &T> {
self.table.keys()
}
#[inline]
pub fn contains(&self, elem: &T) -> bool
where
S: BuildHasher,
{
self.table.contains_key(elem)
}
#[inline]
pub fn len(&self) -> usize {
self.table.len()
}
#[inline]
pub fn capacity(&self) -> usize {
self.table.capacity()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.table.is_empty()
}
}
impl<T: Hash + Eq + Clone, S: BuildHasher> HConsign<T, S> {
#[inline]
pub fn with_hasher(build_hasher: S) -> Self {
HConsign {
table: HashMap::with_hasher(build_hasher),
count: 0,
}
}
#[inline]
pub fn with_capacity_and_hasher(capacity: usize, build_hasher: S) -> Self {
HConsign {
table: HashMap::with_capacity_and_hasher(capacity, build_hasher),
count: 0,
}
}
#[inline]
fn insert(&mut self, key: T, wconsed: WHConsed<T>) {
let prev = self.table.insert(key, wconsed);
debug_assert!(match prev {
None => true,
Some(prev) => prev.to_hconsed().is_none(),
})
}
#[inline]
fn get(&self, key: &T) -> Option<HConsed<T>> {
if let Some(old) = self.table.get(key) {
old.to_hconsed()
} else {
None
}
}
}
impl<T: Hash + Eq + Clone, S> fmt::Display for HConsign<T, S>
where
T: Hash + fmt::Display,
{
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "consign:")?;
for e in self.table.values() {
write!(fmt, "\n | {e}")?;
}
Ok(())
}
}
pub trait HashConsign<T: Hash>: Sized {
fn mk_is_new(self, elm: T) -> (HConsed<T>, bool);
fn mk(self, elm: T) -> HConsed<T> {
self.mk_is_new(elm).0
}
fn collect(self);
fn shrink_to_fit(self);
fn collect_to_fit(self);
fn reserve(self, additional: usize);
}
impl<T: Hash + Eq + Clone, S: BuildHasher> HashConsign<T> for &mut HConsign<T, S> {
fn mk_is_new(self, elm: T) -> (HConsed<T>, bool) {
if let Some(hconsed) = self.get(&elm) {
debug_assert!(*hconsed.elm == elm);
return (hconsed, false);
}
let hconsed = HConsed {
elm: Arc::new(elm.clone()),
uid: self.count,
};
self.count += 1;
self.insert(elm, hconsed.to_weak());
(hconsed, true)
}
fn collect(self) {
let mut old_len = self.table.len() + 1;
let mut max_uid = None;
while old_len != self.table.len() {
old_len = self.table.len();
max_uid = None;
self.table.retain(|_key, val| {
if val.elm.strong_count() > 0 {
let max = max_uid.get_or_insert(val.uid);
if *max < val.uid {
*max = val.uid
}
true
} else {
false
}
});
}
if self.table.is_empty() {
self.count = 0
} else if let Some(max) = max_uid {
self.count = max + 1
}
}
fn shrink_to_fit(self) {
self.table.shrink_to_fit()
}
fn collect_to_fit(self) {
self.collect();
self.shrink_to_fit();
}
fn reserve(self, additional: usize) {
self.table.reserve(additional)
}
}
macro_rules! get {
{ read on $consign:expr } => {
get! { @expect $consign.read() }
};
{ write on $consign:expr } => {
get! { @expect $consign.write() }
};
{ @expect $e:expr } => {
$e.expect("[hashconsing] global consign is poisoned")
};
}
impl<T: Hash + Eq + Clone> HashConsign<T> for &RwLock<HConsign<T>> {
fn mk_is_new(self, elm: T) -> (HConsed<T>, bool) {
{
let slf = get!(read on self);
if let Some(hconsed) = slf.get(&elm) {
debug_assert!(*hconsed.elm == elm);
return (hconsed, false);
}
};
let mut slf = get!(write on self);
if let Some(hconsed) = slf.get(&elm) {
debug_assert!(*hconsed.elm == elm);
return (hconsed, false);
}
let hconsed = HConsed {
elm: Arc::new(elm.clone()),
uid: slf.count,
};
slf.count += 1;
slf.insert(elm, hconsed.to_weak());
(hconsed, true)
}
fn collect(self) {
get!(write on self).collect()
}
fn shrink_to_fit(self) {
get!(write on self).shrink_to_fit()
}
fn collect_to_fit(self) {
get!(write on self).collect_to_fit()
}
fn reserve(self, additional: usize) {
get!(write on self).reserve(additional)
}
}