#[cfg(test)]
#[cfg(feature = "tinyset")]
use quickcheck::quickcheck;
mod boxedset;
use boxedset::HashSet;
#[cfg(feature = "arc")]
use dashmap::{mapref::entry::Entry, DashMap};
use once_cell::sync::OnceCell;
use std::cell::RefCell;
use std::sync::atomic::AtomicUsize;
#[cfg(feature = "arc")]
use std::sync::atomic::Ordering;
use std::sync::Mutex;
mod container;
use container::{TypeHolder, TypeHolderSend};
use std::any::Any;
#[cfg(feature = "arc")]
use std::any::TypeId;
use std::borrow::Borrow;
use std::convert::AsRef;
use std::fmt::{Debug, Display, Pointer};
use std::hash::{Hash, Hasher};
use std::ops::Deref;
#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[cfg(feature = "tinyset")]
use tinyset::Fits64;
#[test]
fn like_doctest_intern() {
let x = Intern::new("hello".to_string());
let y = Intern::<String>::from("world");
assert_ne!(x, y);
assert_eq!(x, Intern::from("hello"));
assert_eq!(y, Intern::from("world"));
assert_eq!(&*x, "hello");
}
#[test]
#[cfg(feature = "arc")]
fn like_doctest_arcintern() {
let x = ArcIntern::new("hello".to_string());
let y = ArcIntern::<String>::from("world");
assert_ne!(x, y);
assert_eq!(x, ArcIntern::from("hello"));
assert_eq!(y, ArcIntern::from("world"));
assert_eq!(&*x, "hello");
}
#[test]
fn like_doctest_localintern() {
let x = LocalIntern::new("hello".to_string());
let y = LocalIntern::<String>::from("world");
assert_ne!(x, y);
assert_eq!(x, LocalIntern::from("hello"));
assert_eq!(y, LocalIntern::from("world"));
assert_eq!(&*x, "hello");
}
pub struct Intern<T: 'static> {
pointer: &'static T,
}
impl<T> Clone for Intern<T> {
fn clone(&self) -> Self {
Intern {
pointer: self.pointer,
}
}
}
impl<T> Copy for Intern<T> {}
impl<T> Intern<T> {
fn get_pointer(&self) -> *const T {
self.pointer as *const T
}
}
fn with_global<F, T, R>(f: F) -> R
where
F: FnOnce(&mut T) -> R,
T: Any + Send + Default,
{
static INTERN_CONTAINERS: OnceCell<Mutex<TypeHolderSend>> = OnceCell::new();
let type_map = INTERN_CONTAINERS.get_or_init(|| Mutex::new(TypeHolderSend::new()));
f(type_map.lock().unwrap().get_type_mut())
}
impl<T: Eq + Hash + Send + Sync + 'static> Intern<T> {
pub fn new(val: T) -> Intern<T> {
with_global(|m: &mut HashSet<&'static T>| -> Intern<T> {
if let Some(&b) = m.get(&val) {
return Intern { pointer: b };
}
let p: &'static T = Box::leak(Box::new(val));
m.insert(p);
Intern { pointer: p }
})
}
pub fn from<'a, Q: ?Sized + Eq + Hash + 'a>(val: &'a Q) -> Intern<T>
where
T: Borrow<Q> + From<&'a Q>,
{
with_global(|m: &mut HashSet<&'static T>| -> Intern<T> {
if let Some(&b) = m.get(val) {
return Intern { pointer: b };
}
let p = Box::leak(Box::new(T::from(val)));
m.insert(p);
Intern { pointer: p }
})
}
pub fn as_ref(self) -> &'static T {
self.pointer
}
pub fn num_objects_interned() -> usize {
with_global(|m: &mut HashSet<&'static T>| -> usize {
m.len()
})
}
}
#[cfg(feature = "tinyset")]
fn heap_location() -> u64 {
static HEAP_LOCATION: OnceCell<Box<usize>> = OnceCell::new();
let p: *const usize = HEAP_LOCATION.get_or_init(|| Box::new(0)).borrow();
p as u64
}
#[cfg(feature = "tinyset")]
fn sz<T>() -> u64 {
std::mem::align_of::<T>() as u64
}
#[cfg(feature = "tinyset")]
impl<T: Debug> Fits64 for Intern<T> {
unsafe fn from_u64(x: u64) -> Self {
Intern {
pointer: &*(((x ^ heap_location() / sz::<T>()) * sz::<T>()) as *const T),
}
}
fn to_u64(self) -> u64 {
self.get_pointer() as u64 / sz::<T>() ^ heap_location() / sz::<T>()
}
}
#[cfg(feature = "tinyset")]
impl<T: Debug> Fits64 for LocalIntern<T> {
unsafe fn from_u64(x: u64) -> Self {
LocalIntern {
pointer: ((x ^ heap_location() / sz::<T>()) * sz::<T>()) as *const T,
}
}
fn to_u64(self) -> u64 {
self.pointer as u64 / sz::<T>() ^ heap_location() / sz::<T>()
}
}
#[test]
#[cfg(feature = "tinyset")]
fn test_localintern_set64() {
use tinyset::Set64;
let mut s = Set64::<LocalIntern<u32>>::new();
s.insert(LocalIntern::new(5));
s.insert(LocalIntern::new(6));
s.insert(LocalIntern::new(6));
s.insert(LocalIntern::new(7));
assert!(s.contains(LocalIntern::new(5)));
assert!(s.contains(LocalIntern::new(6)));
assert!(s.contains(LocalIntern::new(7)));
assert!(!s.contains(LocalIntern::new(8)));
assert_eq!(s.len(), 3);
}
#[test]
#[cfg(feature = "tinyset")]
fn test_intern_set64() {
use tinyset::Set64;
let mut s = Set64::<Intern<u32>>::new();
s.insert(Intern::new(5));
s.insert(Intern::new(6));
s.insert(Intern::new(6));
s.insert(Intern::new(7));
assert!(s.contains(Intern::new(5)));
assert!(s.contains(Intern::new(6)));
assert!(s.contains(Intern::new(7)));
assert!(!s.contains(Intern::new(8)));
assert_eq!(s.len(), 3);
}
#[cfg(feature = "arc")]
pub struct ArcIntern<T: Eq + Hash + Send + Sync + 'static> {
pointer: *const RefCount<T>,
}
#[cfg(feature = "arc")]
unsafe impl<T: Eq + Hash + Send + Sync> Send for ArcIntern<T> {}
#[cfg(feature = "arc")]
unsafe impl<T: Eq + Hash + Send + Sync> Sync for ArcIntern<T> {}
#[derive(Debug)]
struct RefCount<T> {
count: AtomicUsize,
data: T,
}
impl<T: Eq> Eq for RefCount<T> {}
impl<T: PartialEq> PartialEq for RefCount<T> {
fn eq(&self, other: &Self) -> bool {
self.data == other.data
}
}
impl<T: Hash> Hash for RefCount<T> {
fn hash<H: Hasher>(&self, hasher: &mut H) {
self.data.hash(hasher)
}
}
#[derive(Eq, PartialEq, Hash)]
struct BoxRefCount<T>(Box<RefCount<T>>);
#[cfg(feature = "arc")]
impl<T> BoxRefCount<T> {
fn into_inner(self) -> T {
self.0.data
}
}
impl<T> Borrow<T> for BoxRefCount<T> {
fn borrow(&self) -> &T {
&self.0.data
}
}
impl<T> Borrow<RefCount<T>> for BoxRefCount<T> {
fn borrow(&self) -> &RefCount<T> {
&self.0
}
}
impl<T> Deref for BoxRefCount<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0.data
}
}
#[cfg(feature = "arc")]
type Container<T> = DashMap<BoxRefCount<T>, ()>;
#[cfg(feature = "arc")]
type Untyped = Box<(dyn Any + Send + Sync + 'static)>;
#[cfg(feature = "arc")]
static ARC_CONTAINERS: OnceCell<DashMap<TypeId, Untyped>> = OnceCell::new();
#[cfg(feature = "arc")]
impl<T: Eq + Hash + Send + Sync + 'static> ArcIntern<T> {
fn get_pointer(&self) -> *const RefCount<T> {
self.pointer
}
fn get_container() -> dashmap::mapref::one::Ref<'static, TypeId, Untyped> {
let type_map = ARC_CONTAINERS.get_or_init(DashMap::new);
let boxed = if let Some(boxed) = type_map.get(&TypeId::of::<T>()) {
boxed
} else {
type_map
.entry(TypeId::of::<T>())
.or_insert_with(|| Box::new(Container::<T>::new()))
.downgrade()
};
boxed
}
pub fn new(mut val: T) -> ArcIntern<T> {
loop {
let c = Self::get_container();
let m = c.downcast_ref::<Container<T>>().unwrap();
if let Some(b) = m.get_mut(&val) {
let b = b.key();
let oldval = b.0.count.fetch_add(1, Ordering::SeqCst);
if oldval != 0 {
return ArcIntern {
pointer: b.0.borrow(),
};
} else {
b.0.count.fetch_sub(1, Ordering::SeqCst);
}
} else {
let b = Box::new(RefCount {
count: AtomicUsize::new(1),
data: val,
});
match m.entry(BoxRefCount(b)) {
Entry::Vacant(e) => {
let p = ArcIntern {
pointer: e.key().0.borrow(),
};
e.insert(());
return p;
}
Entry::Occupied(e) => {
let box_ref_count = e.into_key();
val = box_ref_count.into_inner();
}
}
}
std::thread::yield_now();
}
}
pub fn from<'a, Q: ?Sized + Eq + Hash + 'a>(val: &'a Q) -> ArcIntern<T>
where
T: Borrow<Q> + From<&'a Q>,
{
Self::new(val.into())
}
pub fn num_objects_interned() -> usize {
let c = Self::get_container();
c.downcast_ref::<Container<T>>()
.map(|m| m.len())
.unwrap_or(0)
}
pub fn refcount(&self) -> usize {
unsafe { (*self.pointer).count.load(Ordering::Acquire) }
}
}
#[cfg(feature = "arc")]
impl<T: Eq + Hash + Send + Sync + 'static> Clone for ArcIntern<T> {
fn clone(&self) -> Self {
unsafe { (*self.pointer).count.fetch_add(1, Ordering::Relaxed) };
ArcIntern {
pointer: self.pointer,
}
}
}
#[cfg(not(test))]
#[cfg(feature = "arc")]
fn yield_on_tests() {}
#[cfg(test)]
#[cfg(feature = "arc")]
fn yield_on_tests() {
std::thread::yield_now();
}
#[cfg(feature = "arc")]
impl<T: Eq + Hash + Send + Sync> Drop for ArcIntern<T> {
fn drop(&mut self) {
let count_was = unsafe { (*self.pointer).count.fetch_sub(1, Ordering::SeqCst) };
if count_was == 1 {
yield_on_tests();
std::sync::atomic::fence(Ordering::SeqCst);
let _remove;
let c = Self::get_container();
let m = c.downcast_ref::<Container<T>>().unwrap();
_remove = m.remove(unsafe { &*self.pointer });
}
}
}
#[cfg(feature = "arc")]
impl<T: Send + Sync + Hash + Eq> AsRef<T> for ArcIntern<T> {
fn as_ref(&self) -> &T {
unsafe { &(*self.pointer).data }
}
}
impl<T> AsRef<T> for Intern<T> {
fn as_ref(&self) -> &T {
self.pointer
}
}
impl<T> AsRef<T> for LocalIntern<T> {
fn as_ref(&self) -> &T {
unsafe { &*self.pointer }
}
}
macro_rules! create_impls {
( $Intern:ident, $testname:ident,
[$( $traits:ident ),*], [$( $newtraits:ident ),*] ) => {
impl<T: $( $traits +)*> Borrow<T> for $Intern<T> {
fn borrow(&self) -> &T {
self.as_ref()
}
}
impl<T: $( $traits +)*> Deref for $Intern<T> {
type Target = T;
fn deref(&self) -> &T {
self.as_ref()
}
}
impl<T: $( $traits +)* Display> Display for $Intern<T> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
self.deref().fmt(f)
}
}
impl<T: $( $traits +)*> Pointer for $Intern<T> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
Pointer::fmt(&self.get_pointer(), f)
}
}
impl<T: $( $newtraits +)* 'static> From<T> for $Intern<T> {
fn from(t: T) -> Self {
$Intern::new(t)
}
}
impl<T: $( $newtraits +)* Default + 'static> Default for $Intern<T> {
fn default() -> $Intern<T> {
$Intern::new(Default::default())
}
}
impl<T: $( $traits +)*> Hash for $Intern<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.get_pointer().hash(state);
}
}
impl<T: $( $traits +)*> PartialEq for $Intern<T> {
fn eq(&self, other: &$Intern<T>) -> bool {
self.get_pointer() == other.get_pointer()
}
}
impl<T: $( $traits +)*> Eq for $Intern<T> {}
impl<T: $( $traits +)* PartialOrd> PartialOrd for $Intern<T> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.as_ref().partial_cmp(other)
}
fn lt(&self, other: &Self) -> bool { self.as_ref().lt(other) }
fn le(&self, other: &Self) -> bool { self.as_ref().le(other) }
fn gt(&self, other: &Self) -> bool { self.as_ref().gt(other) }
fn ge(&self, other: &Self) -> bool { self.as_ref().ge(other) }
}
impl<T: $( $traits +)* Ord> Ord for $Intern<T> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.as_ref().cmp(other)
}
}
#[cfg(feature = "serde")]
impl<T: $( $traits +)* Serialize> Serialize for $Intern<T> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.as_ref().serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'de, T: $( $newtraits +)* 'static + Deserialize<'de>> Deserialize<'de> for $Intern<T> {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
T::deserialize(deserializer).map(|x: T| Self::new(x))
}
}
#[cfg(test)]
mod $testname {
use super::$Intern;
use super::{Borrow,Deref};
#[test]
fn eq_string() {
assert_eq!($Intern::new("hello"), $Intern::new("hello"));
assert_ne!($Intern::new("goodbye"), $Intern::new("farewell"));
}
#[test]
fn display() {
let world = $Intern::new("world");
println!("Hello {}", world);
}
#[test]
fn debug() {
let world = $Intern::new("world");
println!("Hello {:?}", world);
}
#[test]
fn has_default() {
assert_eq!( $Intern::<Option<String>>::default(),
$Intern::<Option<String>>::new(None));
}
#[test]
fn can_clone() {
assert_eq!( $Intern::<Option<String>>::default().clone(),
$Intern::<Option<String>>::new(None));
}
#[test]
fn has_borrow() {
let x = $Intern::<Option<String>>::default();
let b: &Option<String> = x.borrow();
assert_eq!( b, $Intern::<Option<String>>::new(None).as_ref());
}
#[test]
fn has_deref() {
let x = $Intern::<Option<String>>::default();
let b: &Option<String> = x.as_ref();
assert_eq!( b, $Intern::<Option<String>>::new(None).deref());
}
}
}
}
#[cfg(feature = "arc")]
create_impls!(
ArcIntern,
arcintern_impl_tests,
[Eq, Hash, Send, Sync],
[Eq, Hash, Send, Sync]
);
create_impls!(Intern, intern_impl_tests, [], [Eq, Hash, Send, Sync]);
create_impls!(LocalIntern, localintern_impl_tests, [], [Eq, Hash]);
impl<T: Debug> Debug for Intern<T> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
std::fmt::Debug::fmt(&self.get_pointer(), f)?;
f.write_str(" : ")?;
self.deref().fmt(f)
}
}
impl<T: Debug> Debug for LocalIntern<T> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
std::fmt::Debug::fmt(&self.pointer, f)?;
f.write_str(" : ")?;
self.deref().fmt(f)
}
}
#[cfg(feature = "arc")]
impl<T: Eq + Hash + Send + Sync + Debug> Debug for ArcIntern<T> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
Pointer::fmt(&self.pointer, f)?;
f.write_str(" : ")?;
self.deref().fmt(f)
}
}
#[test]
#[cfg(feature = "arc")]
fn test_arcintern_freeing() {
assert_eq!(ArcIntern::<i32>::num_objects_interned(), 0);
assert_eq!(ArcIntern::new(5), ArcIntern::new(5));
{
let _interned = ArcIntern::new(6);
assert_eq!(ArcIntern::<i32>::num_objects_interned(), 1);
}
{
let _interned = ArcIntern::new(6);
assert_eq!(ArcIntern::<i32>::num_objects_interned(), 1);
}
{
let _interned = ArcIntern::new(7);
assert_eq!(ArcIntern::<i32>::num_objects_interned(), 1);
}
let six = ArcIntern::new(6);
{
let _interned = ArcIntern::new(7);
assert_eq!(ArcIntern::<i32>::num_objects_interned(), 2);
}
assert_eq!(ArcIntern::new(6), six);
}
#[cfg(feature = "arc")]
#[test]
fn test_arcintern_nested_drop() {
#[derive(PartialEq, Eq, Hash)]
enum Nat {
Zero,
Successor(ArcIntern<Nat>),
}
let zero = ArcIntern::new(Nat::Zero);
let _one = ArcIntern::new(Nat::Successor(zero));
}
#[test]
fn test_intern_num_objects() {
assert_eq!(Intern::<i32>::num_objects_interned(), 0);
assert_eq!(Intern::new(5), Intern::new(5));
{
let interned = Intern::new(6);
assert_eq!(*interned, 6);
assert_eq!(Intern::<i32>::num_objects_interned(), 2);
}
{
let _interned = Intern::new(6);
assert_eq!(Intern::<i32>::num_objects_interned(), 2);
}
{
let _interned = Intern::new(7);
assert_eq!(Intern::<i32>::num_objects_interned(), 3);
}
}
#[cfg(test)]
#[derive(Eq, PartialEq, Hash)]
pub struct TestStructCount(String, u64, std::sync::Arc<bool>);
#[cfg(feature = "arc")]
#[test]
fn multithreading1() {
use std::sync::Arc;
use std::thread;
let mut thandles = vec![];
let drop_check = Arc::new(true);
for _i in 0..10 {
thandles.push(thread::spawn({
let drop_check = drop_check.clone();
move || {
for _i in 0..100_000 {
let _interned1 =
ArcIntern::new(TestStructCount("foo".to_string(), 5, drop_check.clone()));
let _interned2 =
ArcIntern::new(TestStructCount("bar".to_string(), 10, drop_check.clone()));
}
}
}));
}
for h in thandles.into_iter() {
h.join().unwrap()
}
assert_eq!(Arc::strong_count(&drop_check), 1);
assert_eq!(ArcIntern::<TestStructCount>::num_objects_interned(), 0);
}
#[cfg(test)]
#[derive(Eq, PartialEq, Hash)]
pub struct TestStruct(String, u64);
#[test]
fn multithreading_intern() {
use std::thread;
let mut thandles = vec![];
for _i in 0..10 {
thandles.push(thread::spawn(|| {
for _i in 0..100_000 {
let _interned1 = Intern::new(TestStruct("foo".to_string(), 5));
let _interned2 = Intern::new(TestStruct("bar".to_string(), 10));
}
}));
}
for h in thandles.into_iter() {
h.join().unwrap()
}
}
pub struct LocalIntern<T> {
pointer: *const T,
}
impl<T> Clone for LocalIntern<T> {
fn clone(&self) -> Self {
LocalIntern {
pointer: self.pointer,
}
}
}
thread_local! {
#[allow(unused)]
pub static LOCAL_STUFF: RefCell<TypeHolder> = RefCell::new(TypeHolder::new());
}
fn with_local<F, T, R>(f: F) -> R
where
F: FnOnce(&mut T) -> R,
T: Any + Default,
{
LOCAL_STUFF.with(|v| -> R { f(v.borrow_mut().get_type_mut()) })
}
impl<T> Copy for LocalIntern<T> {}
impl<T> LocalIntern<T> {
fn get_pointer(&self) -> *const T {
self.pointer
}
}
impl<T: Eq + Hash + 'static> LocalIntern<T> {
pub fn new(val: T) -> LocalIntern<T> {
with_local(|m: &mut HashSet<Box<T>>| -> LocalIntern<T> {
if let Some(ref b) = m.get(&val) {
return LocalIntern {
pointer: (*b).borrow(),
};
}
let b = Box::new(val);
let p: *const T = b.borrow();
m.insert(b);
LocalIntern { pointer: p }
})
}
pub fn from<'a, Q: ?Sized + Eq + Hash + 'a>(val: &'a Q) -> LocalIntern<T>
where
T: Borrow<Q> + From<&'a Q>,
{
with_local(|m: &mut HashSet<Box<T>>| -> LocalIntern<T> {
if let Some(ref b) = m.get(val) {
return LocalIntern {
pointer: (*b).borrow(),
};
}
let b = Box::new(T::from(val));
let p: *const T = b.borrow();
m.insert(b);
LocalIntern { pointer: p }
})
}
pub fn num_objects_interned() -> usize {
with_local(|m: &mut HashSet<Box<T>>| -> usize { m.len() })
}
}
#[test]
fn test_localintern_num_objects() {
assert_eq!(LocalIntern::new(5), LocalIntern::new(5));
{
let _interned = LocalIntern::new(6);
assert_eq!(LocalIntern::<i32>::num_objects_interned(), 2);
}
{
let _interned = LocalIntern::new(6);
assert_eq!(LocalIntern::<i32>::num_objects_interned(), 2);
}
{
let _interned = LocalIntern::new(7);
assert_eq!(LocalIntern::<i32>::num_objects_interned(), 3);
}
}
#[cfg(test)]
#[cfg(feature = "tinyset")]
quickcheck! {
fn fits64_localintern(s: String) -> bool {
tinyset::set64::test_fits64(LocalIntern::new(s));
true
}
fn fits64_intern(s: String) -> bool {
tinyset::set64::test_fits64(Intern::new(s));
true
}
}