#[cfg(test)]
use quickcheck::quickcheck;
use lazy_static::lazy_static;
mod boxedset;
use boxedset::HashSet;
use std::cell::RefCell;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Mutex;
use std::any::Any;
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};
use tinyset::Fits64;
lazy_static! {
static ref CONTAINER: state::Container = state::Container::new();
}
#[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]
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> {
pointer: *const T,
}
impl<T> Clone for Intern<T> {
fn clone(&self) -> Self {
Intern {
pointer: self.pointer,
}
}
}
impl<T> Copy for Intern<T> {}
unsafe impl<T> Send for Intern<T> {}
unsafe impl<T> Sync for Intern<T> {}
impl<T: Eq + Hash + Send + 'static> Intern<T> {
fn get_mutex() -> &'static Mutex<HashSet<Box<T>>> {
match CONTAINER.try_get::<Mutex<HashSet<Box<T>>>>() {
Some(m) => m,
None => {
CONTAINER.set::<Mutex<HashSet<Box<T>>>>(Mutex::new(HashSet::new()));
CONTAINER.get::<Mutex<HashSet<Box<T>>>>()
}
}
}
pub fn new(val: T) -> Intern<T> {
let mut m = Self::get_mutex().lock().unwrap();
if let Some(b) = m.get(&val) {
return Intern {
pointer: b.borrow(),
};
}
let b = Box::new(val);
let p: *const T = b.borrow();
m.insert(b);
return Intern { pointer: p };
}
pub fn from<'a, Q: ?Sized + Eq + Hash + 'a>(val: &'a Q) -> Intern<T>
where
T: Borrow<Q> + From<&'a Q>,
{
let mut m = Self::get_mutex().lock().unwrap();
if let Some(b) = m.get(val) {
return Intern {
pointer: b.borrow(),
};
}
let b = Box::new(T::from(val));
let p: *const T = b.borrow();
m.insert(b);
return Intern { pointer: p };
}
pub fn num_objects_interned() -> usize {
if let Some(m) = CONTAINER.try_get::<Mutex<HashSet<Box<T>>>>() {
return m.lock().unwrap().len();
}
0
}
}
fn heap_location() -> u64 {
lazy_static! {
static ref HEAP_LOCATION: Box<usize> = Box::new(0);
}
let p: *const usize = (*HEAP_LOCATION).borrow();
p as u64
}
fn sz<T>() -> u64 {
std::mem::align_of::<T>() as u64
}
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.pointer as u64 / sz::<T>() ^ heap_location() / sz::<T>()
}
}
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]
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]
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);
}
pub struct ArcIntern<T: Eq + Hash + Send + 'static> {
pointer: *const RefCount<T>,
}
unsafe impl<T: Eq + Hash + Send> Send for ArcIntern<T> {}
unsafe impl<T: Eq + Hash + Send> 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>>);
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
}
}
type Container<T> = Mutex<HashSet<BoxRefCount<T>>>;
impl<T: Eq + Hash + Send + 'static> ArcIntern<T> {
fn get_mutex() -> &'static Container<T> {
match CONTAINER.try_get::<Container<T>>() {
Some(m) => m,
None => {
CONTAINER.set::<Container<T>>(Mutex::new(HashSet::new()));
CONTAINER.get::<Container<T>>()
}
}
}
pub fn new(val: T) -> ArcIntern<T> {
loop {
let mymutex = Self::get_mutex();
let mut m = mymutex.lock().unwrap();
if let Some(b) = m.get(&val) {
let oldval = b.0.count.fetch_add(1, Ordering::Relaxed);
if oldval != 0 {
return ArcIntern {
pointer: b.0.borrow(),
};
} else {
b.0.count.fetch_sub(1, Ordering::Relaxed);
}
} else {
let b = Box::new(RefCount {
count: AtomicUsize::new(1),
data: val,
});
let p = ArcIntern {
pointer: b.borrow(),
};
m.insert(BoxRefCount(b));
return p;
}
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>,
{
loop {
let mymutex = Self::get_mutex();
let mut m = mymutex.lock().unwrap();
if let Some(b) = m.get(val) {
yield_on_tests();
let oldval = b.0.count.fetch_add(1, Ordering::Relaxed);
if oldval != 0 {
return ArcIntern {
pointer: b.0.borrow(),
};
} else {
b.0.count.fetch_sub(1, Ordering::Relaxed);
}
} else {
let b = Box::new(RefCount {
count: AtomicUsize::new(1),
data: val.into(),
});
let p = ArcIntern {
pointer: b.borrow(),
};
m.insert(BoxRefCount(b));
return p;
}
std::thread::yield_now();
}
}
pub fn num_objects_interned() -> usize {
if let Some(m) = CONTAINER.try_get::<Container<T>>() {
return m.lock().unwrap().len();
}
0
}
pub fn refcount(&self) -> usize {
unsafe { (*self.pointer).count.load(Ordering::Acquire) }
}
}
impl<T: Eq + Hash + Send + '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))]
fn yield_on_tests() {
}
#[cfg(test)]
fn yield_on_tests() {
std::thread::yield_now();
}
impl<T: Eq + Hash + Send> Drop for ArcIntern<T> {
fn drop(&mut self) {
let count_was = unsafe { (*self.pointer).count.fetch_sub(1, Ordering::Release) };
if count_was == 1 {
yield_on_tests();
std::sync::atomic::fence(Ordering::Acquire);
let _removed;
let mut m = Self::get_mutex().lock().unwrap();
_removed = m.take(unsafe { &*self.pointer });
}
}
}
impl<T: Send + 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 {
unsafe { &*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.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.pointer.hash(state);
}
}
impl<T: $( $traits +)*> PartialEq for $Intern<T> {
fn eq(&self, other: &$Intern<T>) -> bool {
self.pointer == other.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());
}
}
}
}
create_impls!(
ArcIntern,
arcintern_impl_tests,
[Eq, Hash, Send],
[Eq, Hash, Send]
);
create_impls!(Intern, intern_impl_tests, [], [Eq, Hash, Send]);
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::Display::fmt(&self.to_u64(), 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::Display::fmt(&self.to_u64(), f)?;
f.write_str(" : ")?;
self.deref().fmt(f)
}
}
impl<T: Eq + Hash + Send + 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]
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);
}
#[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!(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 TestStruct(String, u64);
#[test]
fn multithreading1() {
use std::thread;
let mut thandles = vec![];
for _i in 0..10 {
thandles.push(thread::spawn(|| {
for _i in 0..100_000 {
let _interned1 = ArcIntern::new(TestStruct("foo".to_string(), 5));
let _interned2 = ArcIntern::new(TestStruct("bar".to_string(), 10));
}
}));
}
for h in thandles.into_iter() {
h.join().unwrap()
}
assert_eq!(ArcIntern::<TestStruct>::num_objects_interned(), 0);
}
#[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<Vec<Box<dyn Any>>> = RefCell::new(Vec::new());
}
pub fn with_local<F, T, R>(f: F) -> R
where
F: FnOnce(&mut T) -> R,
T: Any + Default,
{
LOCAL_STUFF.with(|v| -> R {
for x in v.borrow_mut().iter_mut() {
if let Some(xx) = x.downcast_mut() {
return f(xx);
}
}
let mut b = Box::new(T::default());
let r = f(&mut b);
v.borrow_mut().push(b);
r
})
}
impl<T> Copy for LocalIntern<T> {}
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)]
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
}
}