forked from Orchid/orchid
Converted Interner to work with Rc-s
- Interner no longer contains unsafe code - Tokens now hold a reference to the value they represent directly This will enable many future improvements
This commit is contained in:
@@ -5,9 +5,9 @@
|
||||
mod monotype;
|
||||
mod multitype;
|
||||
mod token;
|
||||
mod traits;
|
||||
// mod traits;
|
||||
|
||||
pub use monotype::TypedInterner;
|
||||
pub use multitype::Interner;
|
||||
pub use token::Tok;
|
||||
pub use traits::{DisplayBundle, InternedDisplay, InternedInto};
|
||||
// pub use traits::{DisplayBundle, InternedDisplay, InternedInto};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::borrow::Borrow;
|
||||
use std::cell::RefCell;
|
||||
use std::hash::{BuildHasher, Hash};
|
||||
use std::num::NonZeroU32;
|
||||
use std::rc::Rc;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
|
||||
@@ -11,20 +11,19 @@ use super::token::Tok;
|
||||
/// Lasso but much simpler, in part because not much can be known about the
|
||||
/// type.
|
||||
pub struct TypedInterner<T: 'static + Eq + Hash + Clone> {
|
||||
tokens: RefCell<HashMap<&'static T, Tok<T>>>,
|
||||
values: RefCell<Vec<(&'static T, bool)>>,
|
||||
tokens: RefCell<HashMap<Rc<T>, Tok<T>>>,
|
||||
}
|
||||
impl<T: Eq + Hash + Clone> TypedInterner<T> {
|
||||
/// Create a fresh interner instance
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
tokens: RefCell::new(HashMap::new()),
|
||||
values: RefCell::new(Vec::new()),
|
||||
}
|
||||
pub fn new() -> Rc<Self> {
|
||||
Rc::new(Self { tokens: RefCell::new(HashMap::new()) })
|
||||
}
|
||||
|
||||
/// Intern an object, returning a token
|
||||
pub fn i<Q: ?Sized + Eq + Hash + ToOwned<Owned = T>>(&self, q: &Q) -> Tok<T>
|
||||
pub fn i<Q: ?Sized + Eq + Hash + ToOwned<Owned = T>>(
|
||||
self: &Rc<Self>,
|
||||
q: &Q,
|
||||
) -> Tok<T>
|
||||
where
|
||||
T: Borrow<Q>,
|
||||
{
|
||||
@@ -34,65 +33,11 @@ impl<T: Eq + Hash + Clone> TypedInterner<T> {
|
||||
.raw_entry_mut()
|
||||
.from_hash(hash, |k| <T as Borrow<Q>>::borrow(k) == q);
|
||||
let kv = raw_entry.or_insert_with(|| {
|
||||
let mut values = self.values.borrow_mut();
|
||||
let uniq_key: NonZeroU32 =
|
||||
(values.len() as u32 + 1u32).try_into().expect("can never be zero");
|
||||
let keybox = Box::new(q.to_owned());
|
||||
let keyref = Box::leak(keybox);
|
||||
values.push((keyref, true));
|
||||
let token = Tok::<T>::from_id(uniq_key);
|
||||
(keyref, token)
|
||||
let keyrc = Rc::new(q.to_owned());
|
||||
let token = Tok::<T>::new(keyrc.clone(), Rc::downgrade(self));
|
||||
(keyrc, token)
|
||||
});
|
||||
*kv.1
|
||||
}
|
||||
|
||||
/// Resolve a token, obtaining a reference to the held object.
|
||||
/// It is illegal to use a token obtained from one interner with
|
||||
/// another.
|
||||
pub fn r(&self, t: Tok<T>) -> &T {
|
||||
let values = self.values.borrow();
|
||||
let key = t.into_usize() - 1;
|
||||
values[key].0.borrow()
|
||||
}
|
||||
|
||||
/// Intern a static reference without allocating the data on the heap
|
||||
#[allow(unused)]
|
||||
pub fn intern_static(&self, tref: &'static T) -> Tok<T> {
|
||||
let mut tokens = self.tokens.borrow_mut();
|
||||
let token = *tokens
|
||||
.raw_entry_mut()
|
||||
.from_key(tref)
|
||||
.or_insert_with(|| {
|
||||
let mut values = self.values.borrow_mut();
|
||||
let uniq_key: NonZeroU32 =
|
||||
(values.len() as u32 + 1u32).try_into().expect("can never be zero");
|
||||
values.push((tref, false));
|
||||
let token = Tok::<T>::from_id(uniq_key);
|
||||
(tref, token)
|
||||
})
|
||||
.1;
|
||||
token
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Eq + Hash + Clone> Default for TypedInterner<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Eq + Hash + Clone> Drop for TypedInterner<T> {
|
||||
fn drop(&mut self) {
|
||||
// make sure all values leaked by us are dropped
|
||||
// FIXME: with the new hashmap logic we can actually store Rc-s
|
||||
// which negates the need for unsafe here
|
||||
let mut values = self.values.borrow_mut();
|
||||
for (item, owned) in values.drain(..) {
|
||||
if !owned {
|
||||
continue;
|
||||
}
|
||||
let _ = unsafe { Box::from_raw((item as *const T).cast_mut()) };
|
||||
}
|
||||
kv.1.clone()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ use hashbrown::HashMap;
|
||||
|
||||
use super::monotype::TypedInterner;
|
||||
use super::token::Tok;
|
||||
use super::InternedDisplay;
|
||||
// use super::InternedDisplay;
|
||||
|
||||
/// A collection of interners based on their type. Allows to intern any object
|
||||
/// that implements [ToOwned]. Objects of the same type are stored together in a
|
||||
@@ -32,57 +32,33 @@ impl Interner {
|
||||
interner.i(q)
|
||||
}
|
||||
|
||||
/// Resolve a token to a reference
|
||||
pub fn r<T: 'static + Eq + Hash + Clone>(&self, t: Tok<T>) -> &T {
|
||||
let mut interners = self.interners.borrow_mut();
|
||||
let interner = get_interner(&mut interners);
|
||||
// TODO: figure this out
|
||||
unsafe { (interner.r(t) as *const T).as_ref().unwrap() }
|
||||
}
|
||||
|
||||
/// Fully resolve an interned list of interned things
|
||||
/// TODO: make this generic over containers
|
||||
pub fn extern_vec<T: 'static + Eq + Hash + Clone>(
|
||||
&self,
|
||||
t: Tok<Vec<Tok<T>>>,
|
||||
) -> Vec<T> {
|
||||
let mut interners = self.interners.borrow_mut();
|
||||
let v_int = get_interner(&mut interners);
|
||||
let t_int = get_interner(&mut interners);
|
||||
let v = v_int.r(t);
|
||||
v.iter().map(|t| t_int.r(*t)).cloned().collect()
|
||||
}
|
||||
|
||||
/// Fully resolve a list of interned things.
|
||||
pub fn extern_all<T: 'static + Eq + Hash + Clone>(
|
||||
&self,
|
||||
s: &[Tok<T>],
|
||||
) -> Vec<T> {
|
||||
s.iter().map(|t| self.r(*t)).cloned().collect()
|
||||
pub fn extern_all<T: 'static + Eq + Hash + Clone>(s: &[Tok<T>]) -> Vec<T> {
|
||||
s.iter().map(|t| (**t).clone()).collect()
|
||||
}
|
||||
|
||||
/// A variant of `unwrap` using [InternedDisplay] to circumvent `unwrap`'s
|
||||
/// dependencyon [Debug]. For clarity, [expect] should be preferred.
|
||||
pub fn unwrap<T, E: InternedDisplay>(&self, result: Result<T, E>) -> T {
|
||||
result.unwrap_or_else(|e| {
|
||||
println!("Unwrapped Error: {}", e.bundle(self));
|
||||
panic!("Unwrapped an error");
|
||||
})
|
||||
}
|
||||
// /// A variant of `unwrap` using [InternedDisplay] to circumvent `unwrap`'s
|
||||
// /// dependencyon [Debug]. For clarity, [expect] should be preferred.
|
||||
// pub fn unwrap<T, E: InternedDisplay>(&self, result: Result<T, E>) -> T {
|
||||
// result.unwrap_or_else(|e| {
|
||||
// println!("Unwrapped Error: {}", e.bundle(self));
|
||||
// panic!("Unwrapped an error");
|
||||
// })
|
||||
// }
|
||||
|
||||
/// A variant of `expect` using [InternedDisplay] to circumvent `expect`'s
|
||||
/// depeendency on [Debug].
|
||||
pub fn expect<T, E: InternedDisplay>(
|
||||
&self,
|
||||
result: Result<T, E>,
|
||||
msg: &str,
|
||||
) -> T {
|
||||
result.unwrap_or_else(|e| {
|
||||
println!("Expectation failed: {msg}");
|
||||
println!("Error: {}", e.bundle(self));
|
||||
panic!("Expected an error");
|
||||
})
|
||||
}
|
||||
// /// A variant of `expect` using [InternedDisplay] to circumvent `expect`'s
|
||||
// /// depeendency on [Debug].
|
||||
// pub fn expect<T, E: InternedDisplay>(
|
||||
// &self,
|
||||
// result: Result<T, E>,
|
||||
// msg: &str,
|
||||
// ) -> T {
|
||||
// result.unwrap_or_else(|e| {
|
||||
// println!("Expectation failed: {msg}");
|
||||
// println!("Error: {}", e.bundle(self));
|
||||
// panic!("Expected an error");
|
||||
// })
|
||||
// }
|
||||
}
|
||||
|
||||
impl Default for Interner {
|
||||
@@ -98,7 +74,7 @@ fn get_interner<T: 'static + Eq + Hash + Clone>(
|
||||
let boxed = interners
|
||||
.raw_entry_mut()
|
||||
.from_key(&TypeId::of::<T>())
|
||||
.or_insert_with(|| (TypeId::of::<T>(), Rc::new(TypedInterner::<T>::new())))
|
||||
.or_insert_with(|| (TypeId::of::<T>(), TypedInterner::<T>::new()))
|
||||
.1
|
||||
.clone();
|
||||
boxed.downcast().expect("the typeid is supposed to protect from this")
|
||||
|
||||
@@ -1,66 +1,94 @@
|
||||
use std::cmp::PartialEq;
|
||||
use std::fmt::Debug;
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::hash::Hash;
|
||||
use std::marker::PhantomData;
|
||||
use std::num::NonZeroU32;
|
||||
use std::num::NonZeroUsize;
|
||||
use std::ops::Deref;
|
||||
use std::rc::{Rc, Weak};
|
||||
|
||||
use super::TypedInterner;
|
||||
|
||||
/// A number representing an object of type `T` stored in some interner. It is a
|
||||
/// logic error to compare tokens obtained from different interners, or to use a
|
||||
/// token with an interner other than the one that created it, but this is
|
||||
/// currently not enforced.
|
||||
pub struct Tok<T> {
|
||||
id: NonZeroU32,
|
||||
phantom_data: PhantomData<T>,
|
||||
#[derive(Clone)]
|
||||
pub struct Tok<T: Eq + Hash + Clone + 'static> {
|
||||
data: Rc<T>,
|
||||
interner: Weak<TypedInterner<T>>,
|
||||
}
|
||||
impl<T> Tok<T> {
|
||||
/// Wrap an ID number into a token
|
||||
pub fn from_id(id: NonZeroU32) -> Self {
|
||||
Self { id, phantom_data: PhantomData }
|
||||
impl<T: Eq + Hash + Clone + 'static> Tok<T> {
|
||||
/// Create a new token. Used exclusively by the interner
|
||||
pub(crate) fn new(data: Rc<T>, interner: Weak<TypedInterner<T>>) -> Self {
|
||||
Self { data, interner }
|
||||
}
|
||||
/// Take the ID number out of a token
|
||||
pub fn into_id(self) -> NonZeroU32 {
|
||||
self.id
|
||||
pub fn id(&self) -> NonZeroUsize {
|
||||
((self.data.as_ref() as *const T as usize).try_into())
|
||||
.expect("Pointer can always be cast to nonzero")
|
||||
}
|
||||
/// Cast into usize
|
||||
pub fn into_usize(self) -> usize {
|
||||
let zero: u32 = self.id.into();
|
||||
zero as usize
|
||||
pub fn usize(&self) -> usize {
|
||||
self.id().into()
|
||||
}
|
||||
///
|
||||
pub fn assert_comparable(&self, other: &Self) {
|
||||
let iref = self.interner.as_ptr() as usize;
|
||||
assert!(
|
||||
iref == other.interner.as_ptr() as usize,
|
||||
"Tokens must come from the same interner"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Debug for Tok<T> {
|
||||
impl<T: Eq + Hash + Clone + 'static> Tok<Vec<Tok<T>>> {
|
||||
/// Extern all elements of the vector in a new vector
|
||||
pub fn extern_vec(&self) -> Vec<T> {
|
||||
self.iter().map(|t| (**t).clone()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Eq + Hash + Clone + 'static> Deref for Tok<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.data.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Eq + Hash + Clone + 'static> Debug for Tok<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Token({})", self.id)
|
||||
write!(f, "Token({})", self.id())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Copy for Tok<T> {}
|
||||
impl<T> Clone for Tok<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self { id: self.id, phantom_data: PhantomData }
|
||||
impl<T: Eq + Hash + Clone + Display + 'static> Display for Tok<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", *self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for Tok<T> {}
|
||||
impl<T> PartialEq for Tok<T> {
|
||||
impl<T: Eq + Hash + Clone + 'static> Eq for Tok<T> {}
|
||||
impl<T: Eq + Hash + Clone + 'static> PartialEq for Tok<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.id == other.id
|
||||
self.assert_comparable(other);
|
||||
self.id() == other.id()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Ord for Tok<T> {
|
||||
impl<T: Eq + Hash + Clone + 'static> Ord for Tok<T> {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.id.cmp(&other.id)
|
||||
self.assert_comparable(other);
|
||||
self.id().cmp(&other.id())
|
||||
}
|
||||
}
|
||||
impl<T> PartialOrd for Tok<T> {
|
||||
impl<T: Eq + Hash + Clone + 'static> PartialOrd for Tok<T> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Hash for Tok<T> {
|
||||
impl<T: Eq + Hash + Clone + 'static> Hash for Tok<T> {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
state.write_u32(self.id.into())
|
||||
state.write_usize(self.usize())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user