#![feature(negative_impls)]
use std::{borrow::Borrow, convert::TryInto, fmt::{Debug, Display}, hash::Hash, marker::PhantomData, ops::Deref, pin::Pin};
pub struct Interner<'a, T: 'a + Eq> {
holders: Vec<InternedItemHolder<T>>,
_ph: PhantomData<&'a T>
}
impl<'a, T> !Sync for Interner<'a, T> {}
const BEGIN_INTERNER_CAPACITY: usize = 32;
const INTERNER_CAPACITY_DELTA: f32 = 1.5;
impl<'a, T: 'a + Eq> Interner<'a, T> {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self {
holders: vec![
InternedItemHolder::new(BEGIN_INTERNER_CAPACITY)],
_ph: PhantomData
}
}
pub fn intern(&mut self, item: T) -> Intern<'a, T> {
let mut result = None;
for holder in &self.holders {
for h_item in &holder.items {
if &item == h_item {
result = Some(h_item);
break
}
}
}
if result.is_none() {
self.hold_new_item(item);
result = Some(
self.holders.last().unwrap().items.last().unwrap()
)
}
let reference = result.unwrap();
unsafe { self.transmute_held_item(reference) }
}
pub fn contains(&self, item: &T) -> bool {
for holder in &self.holders {
for h_item in &holder.items {
if item == h_item {
return true
}
}
}
false
}
fn hold_new_item(&mut self, item: T) {
match self.holders.last_mut().unwrap().try_push(item) {
Ok(()) => (),
Err(item) => {
let last_holder_capacity = self.holders.last().unwrap().items.capacity();
let mut new_holder = InternedItemHolder::new(
((last_holder_capacity as f32) * INTERNER_CAPACITY_DELTA) as usize
);
new_holder.items.push(item);
self.holders.push(new_holder);
}
}
}
#[inline]
unsafe fn transmute_held_item(&self, item: &T) -> Intern<'a, T> {
let reference: &'a T = std::mem::transmute(item);
let pinned_reference: Pin<&'a T> = Pin::new_unchecked(reference);
Intern(pinned_reference)
}
pub fn iter<'this>(&'this self) -> Iter<'this, 'a, T> {
Iter { interner: self, holder_id: 0, inside_holder_id: 0 }
}
}
struct InternedItemHolder<T> {
items: Vec<T>
}
impl<T> InternedItemHolder<T> {
fn new(capacity: usize) -> Self {
Self { items: Vec::with_capacity(capacity) }
}
fn try_push(&mut self, item: T) -> Result<(), T> {
if self.items.len() == self.items.capacity() {
Err(item)
} else {
self.items.push(item);
Ok(())
}
}
}
pub struct Intern<'a, T: 'a>(Pin<&'a T>);
impl<'a, T> Clone for Intern<'a, T> {
fn clone(&self) -> Self {
Intern(self.0)
}
}
impl<'a, T> Copy for Intern<'a, T> {}
impl<'a, T> AsRef<T> for Intern<'a, T> {
fn as_ref(&self) -> &T {
self.0.get_ref()
}
}
impl<'a, T> Borrow<T> for Intern<'a, T> {
fn borrow(&self) -> &T {
self.0.get_ref()
}
}
impl<'a, T> Deref for Intern<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl<'a, T: Debug> Debug for Intern<'a, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.as_ref().fmt(f)
}
}
impl<'a, T: Display> Display for Intern<'a, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.as_ref().fmt(f)
}
}
impl<'a, T> PartialEq for Intern<'a, T> {
fn eq(&self, other: &Self) -> bool {
std::ptr::eq(self.as_ref() as *const _, other.as_ref() as *const _)
}
}
impl<'a, T> Eq for Intern<'a, T> {}
impl<'a, T: Hash> Hash for Intern<'a, T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.as_ref().hash(state)
}
}
impl<'a, T: PartialOrd> PartialOrd for Intern<'a, T> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.0.partial_cmp(&other.0)
}
}
impl<'a, T: Ord> Ord for Intern<'a, T> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.cmp(&other.0)
}
}
impl<'a, T> Intern<'a, T> {
pub unsafe fn from_raw(ptr: *const T) -> Self {
Intern(Pin::new_unchecked(ptr.as_ref().unwrap()))
}
}
pub struct Iter<'a, 'intern, T: std::cmp::Eq> {
interner: &'a Interner<'intern, T>,
holder_id: usize,
inside_holder_id: usize
}
impl<'a, 'intern, T: std::cmp::Eq> Iterator for Iter<'a, 'intern, T> {
type Item = Intern<'intern, T>;
fn next(&mut self) -> Option<Self::Item> {
let holder = self.interner.holders.get(self.holder_id)?;
let item = &holder.items[self.inside_holder_id];
if self.inside_holder_id == holder.items.len() - 1 {
self.holder_id += 1;
self.inside_holder_id = 0;
} else {
self.inside_holder_id += 1;
}
Some(unsafe { self.interner.transmute_held_item(item) })
}
}
#[cfg(test)]
mod tests {
use super::{InternedItemHolder, Interner};
#[test]
fn interned_item_holder_test() {
let mut holder = InternedItemHolder::new(4);
assert!(holder.try_push('a').is_ok());
assert!(holder.items.len() == 1);
assert!(holder.items.capacity() == 4);
let first_item_address = holder.items.get(0).unwrap() as *const _ as usize;
assert!(holder.try_push('b').is_ok());
assert!(holder.items.len() == 2);
assert!(holder.items.capacity() == 4);
assert_eq!(
holder.items.get(0).unwrap() as *const _ as usize,
first_item_address
);
let second_item_address = holder.items.get(1).unwrap() as *const _ as usize;
assert!(holder.try_push('c').is_ok());
assert!(holder.try_push('d').is_ok());
assert!(holder.items.len() == 4);
assert!(holder.items.capacity() == 4);
assert_eq!(
holder.items.get(0).unwrap() as *const _ as usize,
first_item_address
);
assert_eq!(
holder.items.get(1).unwrap() as *const _ as usize,
second_item_address
);
assert_eq!(holder.try_push('e'), Err('e'));
assert_eq!(holder.try_push('f'), Err('f'));
assert!(holder.items.len() == 4);
assert!(holder.items.capacity() == 4);
assert_eq!(
holder.items.get(0).unwrap() as *const _ as usize,
first_item_address
);
assert_eq!(
holder.items.get(1).unwrap() as *const _ as usize,
second_item_address
);
assert_eq!(
unsafe { *(first_item_address as *const char) },
'a');
assert_eq!(
unsafe { *(second_item_address as *const char) },
'b');
}
#[test]
fn interner_test() {
let mut int = Interner::new();
let ref_a1 = int.intern('a');
let ref_b = int.intern('b');
let ref_a2 = int.intern('a');
assert_eq!(int.holders.len(), 1);
assert_eq!(int.holders[0].items.len(), 2);
assert!(std::ptr::eq(ref_a1.as_ref(), ref_a2.as_ref()));
assert!(!std::ptr::eq(ref_a1.as_ref(), ref_b.as_ref()));
let ref_b2 = int.intern('b');
let _ref_c = int.intern('c');
assert_eq!(ref_b, ref_b2);
}
#[test]
fn intern_impl_test() {
let mut int = Interner::new();
let a1 = int.intern('a');
let a2 = int.intern('a');
let x = int.intern('x');
assert_eq!(a1.as_ref(), &'a');
assert_eq!(<_ as std::borrow::Borrow<char>>::borrow(&a1), &'a');
assert_eq!(*a1, 'a');
assert_eq!(a1, a2);
assert_ne!(a1, x);
}
#[test]
fn interner_iter_test() {
let mut int = Interner::new();
for i in 0..100 {
int.intern(i);
}
let collected: Vec<i32> = int.iter().map(|i| *i).collect();
assert_eq!(
collected,
(0..100).collect::<Vec<i32>>()
);
}
}