fayalite/crates/fayalite/src/intern.rs
Jacob Lifshay 1bc835803b
All checks were successful
/ test (pull_request) Successful in 4m28s
/ test (push) Successful in 5m32s
speed up LazyInterned by redoing caching using RwLock and add a thread-local cache
2026-02-03 18:00:36 -08:00

1168 lines
34 KiB
Rust

// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
#![allow(clippy::type_complexity)]
use crate::{intern::type_map::TypeIdMap, util::DefaultBuildHasher};
use bitvec::{ptr::BitPtr, slice::BitSlice, vec::BitVec};
use hashbrown::HashTable;
use once_cell::race::OnceRef;
use serde::{Deserialize, Serialize};
use std::{
any::{Any, TypeId},
borrow::{Borrow, Cow},
cell::RefCell,
cmp::Ordering,
ffi::{OsStr, OsString},
fmt,
hash::{BuildHasher, Hash, Hasher},
iter::FusedIterator,
ops::Deref,
path::{Path, PathBuf},
sync::RwLock,
};
mod interner;
mod type_map;
/// invariant: T must be zero-sized, `type_id` is unique for every possible T value.
struct LazyInternedLazyInner<T: ?Sized + 'static> {
type_id: TypeId,
value: T,
}
impl<T: ?Sized + 'static> Hash for LazyInternedLazyInner<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
let Self { type_id, value: _ } = self;
type_id.hash(state);
}
}
impl<T: ?Sized + 'static> PartialEq for LazyInternedLazyInner<T> {
fn eq(&self, other: &Self) -> bool {
let Self { type_id, value: _ } = self;
*type_id == other.type_id
}
}
impl<T: ?Sized + 'static> Eq for LazyInternedLazyInner<T> {}
impl<T: ?Sized + 'static> fmt::Debug for LazyInternedLazyInner<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LazyInternedLazyInner")
.finish_non_exhaustive()
}
}
impl<T: ?Sized + 'static> LazyInternedLazyInner<T> {
const fn new(value: T) -> Self
where
T: Sized,
{
const { assert!(size_of::<T>() == 0) };
Self {
type_id: TypeId::of::<T>(),
value,
}
}
}
pub struct LazyInternedLazy<T: ?Sized + Send + Sync + 'static>(
&'static LazyInternedLazyInner<dyn Fn() -> Interned<T> + Send + Sync>,
);
impl<T: ?Sized + Send + Sync + 'static> LazyInternedLazy<T> {
pub const fn new_const<V: Default + Into<Interned<T>>>() -> Self {
Self(&const { LazyInternedLazyInner::new(|| V::default().into()) })
}
pub const fn new_const_default() -> Self
where
Interned<T>: Default,
{
Self::new_const::<Interned<T>>()
}
pub fn interned(self) -> Interned<T> {
struct Map(hashbrown::HashTable<(TypeId, &'static (dyn Any + Send + Sync))>);
impl Map {
const EMPTY: Self = Self(hashbrown::HashTable::new());
fn get<T: ?Sized + Send + Sync + 'static>(
&self,
lazy_interned_lazy: LazyInternedLazy<T>,
hash: u64,
) -> Option<&'static Interned<T>> {
let &(_, v) = self.0.find(hash, |v| v.0 == lazy_interned_lazy.0.type_id)?;
let Some(retval) = v.downcast_ref::<Interned<T>>() else {
unreachable!();
};
Some(retval)
}
fn get_or_insert<T: ?Sized + Send + Sync + 'static>(
&mut self,
lazy_interned_lazy: LazyInternedLazy<T>,
hash: u64,
v: &'static Interned<T>,
) -> &'static Interned<T> {
let entry = self
.0
.entry(
hash,
|&(k, _)| k == lazy_interned_lazy.0.type_id,
|&(k, _)| type_map::TypeIdBuildHasher.hash_one(k),
)
.or_insert_with(|| (lazy_interned_lazy.0.type_id, v));
let &(_, v) = entry.get();
let Some(retval) = v.downcast_ref::<Interned<T>>() else {
unreachable!();
};
retval
}
}
static GLOBAL_CACHE: RwLock<Map> = RwLock::new(Map::EMPTY);
#[cold]
fn insert_in_thread_local_cache<T: ?Sized + Send + Sync + 'static>(
cache: &RefCell<Map>,
this: LazyInternedLazy<T>,
hash: u64,
) -> Interned<T> {
let read_lock = GLOBAL_CACHE.read().unwrap();
let v = read_lock.get(this, hash);
drop(read_lock);
let v = v.unwrap_or_else(|| {
let v = Box::leak(Box::new((this.0.value)()));
GLOBAL_CACHE.write().unwrap().get_or_insert(this, hash, v)
});
*cache.borrow_mut().get_or_insert(this, hash, v)
}
thread_local! {
static THREAD_LOCAL_CACHE: RefCell<Map> = const { RefCell::new(Map::EMPTY) };
}
let hash = type_map::TypeIdBuildHasher.hash_one(self.0.type_id);
THREAD_LOCAL_CACHE.with(|cache| {
let borrow = cache.borrow();
if let Some(v) = borrow.get(self, hash) {
*v
} else {
drop(borrow);
insert_in_thread_local_cache(cache, self, hash)
}
})
}
}
impl<T: ?Sized + Send + Sync + 'static> Copy for LazyInternedLazy<T> {}
impl<T: ?Sized + Send + Sync + 'static> Clone for LazyInternedLazy<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized + Send + Sync + 'static> Hash for LazyInternedLazy<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl<T: ?Sized + Send + Sync + 'static> Eq for LazyInternedLazy<T> {}
impl<T: ?Sized + Send + Sync + 'static> PartialEq for LazyInternedLazy<T> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
pub enum LazyInterned<T: ?Sized + Send + Sync + 'static> {
Interned(Interned<T>),
Lazy(LazyInternedLazy<T>),
}
impl<T: ?Sized + Send + Sync + 'static> LazyInterned<T> {
pub const fn new_const<V: Default + Into<Interned<T>>>() -> Self {
Self::Lazy(LazyInternedLazy::new_const::<V>())
}
pub const fn new_const_default() -> Self
where
Interned<T>: Default,
{
Self::new_const::<Interned<T>>()
}
pub fn interned(self) -> Interned<T> {
match self {
Self::Interned(retval) => retval,
Self::Lazy(retval) => retval.interned(),
}
}
}
impl<T: ?Sized + Sync + Send + 'static> Clone for LazyInterned<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized + Sync + Send + 'static> Copy for LazyInterned<T> {}
impl<T: ?Sized + Sync + Send + 'static> Deref for LazyInterned<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
Interned::into_inner(self.interned())
}
}
impl<T: ?Sized + Sync + Send + 'static> Eq for LazyInterned<T> where Interned<T>: Eq {}
impl<T: ?Sized + Sync + Send + 'static> PartialEq for LazyInterned<T>
where
Interned<T>: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.interned() == other.interned()
}
}
impl<T: ?Sized + Sync + Send + 'static> Ord for LazyInterned<T>
where
Interned<T>: Ord,
{
fn cmp(&self, other: &Self) -> Ordering {
self.interned().cmp(&other.interned())
}
}
impl<T: ?Sized + Sync + Send + 'static> PartialOrd for LazyInterned<T>
where
Interned<T>: PartialOrd,
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.interned().partial_cmp(&other.interned())
}
}
impl<T: ?Sized + Sync + Send + 'static> Hash for LazyInterned<T>
where
Interned<T>: Hash,
{
fn hash<H: Hasher>(&self, state: &mut H) {
self.interned().hash(state);
}
}
pub trait InternedCompare {
type InternedCompareKey: Ord + Hash;
fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey;
}
/// Warning: doesn't do what you want with `T = dyn Trait`,
/// since it compares pointers, but pointers can be unequal due do
/// different copies of the same vtable
pub struct PtrEqWithMetadata<T: ?Sized>(pub *const T);
impl<T: ?Sized> Copy for PtrEqWithMetadata<T> {}
impl<T: ?Sized> Clone for PtrEqWithMetadata<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized> PartialEq for PtrEqWithMetadata<T> {
fn eq(&self, other: &Self) -> bool {
std::ptr::eq(self.0, other.0)
}
}
impl<T: ?Sized> Eq for PtrEqWithMetadata<T> {}
impl<T: ?Sized> PartialOrd for PtrEqWithMetadata<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<T: ?Sized> Ord for PtrEqWithMetadata<T> {
fn cmp(&self, other: &Self) -> Ordering {
#[allow(ambiguous_wide_pointer_comparisons)]
self.0.cmp(&other.0)
}
}
impl<T: ?Sized> Hash for PtrEqWithMetadata<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl<T: ?Sized> fmt::Debug for PtrEqWithMetadata<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("PtrEqWithMetadata").field(&self.0).finish()
}
}
/// Warning: doesn't do what you want for `[T]` or `str`, since it ignores length
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct PtrEqWithTypeId(pub *const (), pub TypeId);
mod sealed {
pub trait SupportsPtrEqWithTypeIdSealed {}
}
pub trait SupportsPtrEqWithTypeId: 'static + sealed::SupportsPtrEqWithTypeIdSealed {
fn get_ptr_eq_with_type_id(&self) -> PtrEqWithTypeId;
}
impl<T: 'static> sealed::SupportsPtrEqWithTypeIdSealed for T {}
impl<T: 'static> SupportsPtrEqWithTypeId for T {
fn get_ptr_eq_with_type_id(&self) -> PtrEqWithTypeId {
PtrEqWithTypeId(self as *const Self as *const (), TypeId::of::<Self>())
}
}
impl<T> InternedCompare for T {
type InternedCompareKey = PtrEqWithMetadata<Self>;
fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey {
PtrEqWithMetadata(this)
}
}
impl<T> InternedCompare for [T] {
type InternedCompareKey = PtrEqWithMetadata<Self>;
fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey {
PtrEqWithMetadata(this)
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BitSlicePtrEq(BitPtr, usize);
impl InternedCompare for BitSlice {
type InternedCompareKey = BitSlicePtrEq;
fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey {
BitSlicePtrEq(this.as_bitptr(), this.len())
}
}
/// Safety: `as_bytes` and `from_bytes_unchecked` must return the same pointer as the input.
/// all values returned by `as_bytes` must be valid to pass to `from_bytes_unchecked`.
/// `into_bytes` must return the exact same thing as `as_bytes`.
/// `Interned<Self>` must contain the exact same references as `Interned<[u8]>`,
/// so they can be safely interconverted without needing re-interning.
unsafe trait InternStrLike: ToOwned {
fn as_bytes(this: &Self) -> &[u8];
fn into_bytes(this: Self::Owned) -> Vec<u8>;
/// Safety: `bytes` must be a valid sequence of bytes for this type. All UTF-8 sequences are valid.
unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self;
}
macro_rules! impl_intern_str_like {
($ty:ty, owned = $Owned:ty) => {
impl InternedCompare for $ty {
type InternedCompareKey = PtrEqWithMetadata<[u8]>;
fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey {
PtrEqWithMetadata(InternStrLike::as_bytes(this))
}
}
impl Intern for $ty {
fn intern(&self) -> Interned<Self> {
Self::intern_cow(Cow::Borrowed(self))
}
fn intern_cow(this: Cow<'_, Self>) -> Interned<Self> {
Interned::cast_unchecked(
<[u8]>::intern_cow(match this {
Cow::Borrowed(v) => Cow::Borrowed(<Self as InternStrLike>::as_bytes(v)),
Cow::Owned(v) => {
// verify $Owned is correct
let v: $Owned = v;
Cow::Owned(<Self as InternStrLike>::into_bytes(v))
}
}),
// Safety: guaranteed safe because we got the bytes from `as_bytes`/`into_bytes`
|v| unsafe { <Self as InternStrLike>::from_bytes_unchecked(v) },
)
}
}
impl Default for Interned<$ty> {
fn default() -> Self {
// Safety: safe because the empty sequence is valid UTF-8
unsafe { <$ty as InternStrLike>::from_bytes_unchecked(&[]) }.intern()
}
}
impl<'de> Deserialize<'de> for Interned<$ty> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
Cow::<'de, $ty>::deserialize(deserializer).map(Intern::intern_cow)
}
}
impl From<$Owned> for Interned<$ty> {
fn from(v: $Owned) -> Self {
v.intern_deref()
}
}
impl From<Interned<$ty>> for $Owned {
fn from(v: Interned<$ty>) -> Self {
Interned::into_inner(v).into()
}
}
impl From<Interned<$ty>> for Box<$ty> {
fn from(v: Interned<$ty>) -> Self {
Interned::into_inner(v).into()
}
}
};
}
// Safety: satisfies `InternStrLike`'s requirements where the valid sequences for `from_bytes_unchecked` matches `str`
unsafe impl InternStrLike for str {
fn as_bytes(this: &Self) -> &[u8] {
this.as_bytes()
}
fn into_bytes(this: Self::Owned) -> Vec<u8> {
this.into_bytes()
}
unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
// Safety: `bytes` is guaranteed UTF-8 by the caller
unsafe { str::from_utf8_unchecked(bytes) }
}
}
impl_intern_str_like!(str, owned = String);
// Safety: satisfies `InternStrLike`'s requirements where the valid sequences for `from_bytes_unchecked` matches `OsStr`
unsafe impl InternStrLike for OsStr {
fn as_bytes(this: &Self) -> &[u8] {
this.as_encoded_bytes()
}
fn into_bytes(this: Self::Owned) -> Vec<u8> {
this.into_encoded_bytes()
}
unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
// Safety: `bytes` is guaranteed valid for `OsStr` by the caller
unsafe { OsStr::from_encoded_bytes_unchecked(bytes) }
}
}
impl_intern_str_like!(OsStr, owned = OsString);
// Safety: satisfies `InternStrLike`'s requirements where the valid sequences for `from_bytes_unchecked` matches `OsStr`
unsafe impl InternStrLike for Path {
fn as_bytes(this: &Self) -> &[u8] {
this.as_os_str().as_encoded_bytes()
}
fn into_bytes(this: Self::Owned) -> Vec<u8> {
this.into_os_string().into_encoded_bytes()
}
unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
// Safety: `bytes` is guaranteed valid for `OsStr` by the caller
unsafe { Path::new(OsStr::from_encoded_bytes_unchecked(bytes)) }
}
}
impl_intern_str_like!(Path, owned = PathBuf);
impl Interned<str> {
pub fn from_utf8(v: Interned<[u8]>) -> Result<Self, std::str::Utf8Error> {
Interned::try_cast_unchecked(v, str::from_utf8)
}
pub fn as_interned_bytes(self) -> Interned<[u8]> {
Interned::cast_unchecked(self, str::as_bytes)
}
pub fn as_interned_os_str(self) -> Interned<OsStr> {
Interned::cast_unchecked(self, AsRef::as_ref)
}
pub fn as_interned_path(self) -> Interned<Path> {
Interned::cast_unchecked(self, AsRef::as_ref)
}
}
impl From<Interned<str>> for Interned<OsStr> {
fn from(value: Interned<str>) -> Self {
value.as_interned_os_str()
}
}
impl From<Interned<str>> for Interned<Path> {
fn from(value: Interned<str>) -> Self {
value.as_interned_path()
}
}
impl Interned<OsStr> {
pub fn as_interned_encoded_bytes(self) -> Interned<[u8]> {
Interned::cast_unchecked(self, OsStr::as_encoded_bytes)
}
pub fn to_interned_str(self) -> Option<Interned<str>> {
Interned::try_cast_unchecked(self, |v| v.to_str().ok_or(())).ok()
}
pub fn display(self) -> std::ffi::os_str::Display<'static> {
Self::into_inner(self).display()
}
pub fn as_interned_path(self) -> Interned<Path> {
Interned::cast_unchecked(self, AsRef::as_ref)
}
}
impl From<Interned<OsStr>> for Interned<Path> {
fn from(value: Interned<OsStr>) -> Self {
value.as_interned_path()
}
}
impl Interned<Path> {
pub fn as_interned_os_str(self) -> Interned<OsStr> {
Interned::cast_unchecked(self, AsRef::as_ref)
}
pub fn to_interned_str(self) -> Option<Interned<str>> {
Interned::try_cast_unchecked(self, |v| v.to_str().ok_or(())).ok()
}
pub fn display(self) -> std::path::Display<'static> {
Self::into_inner(self).display()
}
pub fn interned_file_name(self) -> Option<Interned<OsStr>> {
Some(self.file_name()?.intern())
}
}
impl From<Interned<Path>> for Interned<OsStr> {
fn from(value: Interned<Path>) -> Self {
value.as_interned_os_str()
}
}
pub trait InternSlice: Sized {
type Element: 'static + Send + Sync + Clone + Hash + Eq;
fn intern_slice(self) -> Interned<[Self::Element]>;
}
impl<T: 'static + Send + Sync + Clone + Hash + Eq> InternSlice for Box<[T]> {
type Element = T;
fn intern_slice(self) -> Interned<[Self::Element]> {
self.into_vec().intern_slice()
}
}
impl<T: 'static + Send + Sync + Clone + Hash + Eq> InternSlice for Vec<T> {
type Element = T;
fn intern_slice(self) -> Interned<[Self::Element]> {
self.intern_deref()
}
}
impl<T: 'static + Send + Sync + Clone + Hash + Eq> InternSlice for &'_ [T] {
type Element = T;
fn intern_slice(self) -> Interned<[Self::Element]> {
self.intern()
}
}
impl<T: 'static + Send + Sync + Clone + Hash + Eq> InternSlice for &'_ mut [T] {
type Element = T;
fn intern_slice(self) -> Interned<[Self::Element]> {
self.intern()
}
}
impl<T: 'static + Send + Sync + Clone + Hash + Eq, const N: usize> InternSlice for [T; N] {
type Element = T;
fn intern_slice(self) -> Interned<[Self::Element]> {
(&self).intern_slice()
}
}
impl<T: 'static + Send + Sync + Clone + Hash + Eq, const N: usize> InternSlice for Box<[T; N]> {
type Element = T;
fn intern_slice(self) -> Interned<[Self::Element]> {
let this: Box<[T]> = self;
this.intern_slice()
}
}
impl<T: 'static + Send + Sync + Clone + Hash + Eq, const N: usize> InternSlice for &'_ [T; N] {
type Element = T;
fn intern_slice(self) -> Interned<[Self::Element]> {
let this: &[T] = self;
this.intern()
}
}
impl<T: 'static + Send + Sync + Clone + Hash + Eq, const N: usize> InternSlice for &'_ mut [T; N] {
type Element = T;
fn intern_slice(self) -> Interned<[Self::Element]> {
let this: &[T] = self;
this.intern()
}
}
pub trait Intern: Any + Send + Sync {
fn intern(&self) -> Interned<Self>;
fn intern_deref(self) -> Interned<Self::Target>
where
Self: Sized + Deref<Target: Intern + ToOwned<Owned = Self>>,
{
Self::Target::intern_owned(self)
}
fn intern_sized(self) -> Interned<Self>
where
Self: Clone,
{
Self::intern_owned(self)
}
fn intern_owned(this: <Self as ToOwned>::Owned) -> Interned<Self>
where
Self: ToOwned,
{
Self::intern_cow(Cow::Owned(this))
}
fn intern_cow(this: Cow<'_, Self>) -> Interned<Self>
where
Self: ToOwned,
{
this.intern()
}
}
impl<T: ?Sized + Intern + ToOwned> From<Cow<'_, T>> for Interned<T> {
fn from(value: Cow<'_, T>) -> Self {
Intern::intern_cow(value)
}
}
impl<T: ?Sized + Intern> From<&'_ T> for Interned<T> {
fn from(value: &'_ T) -> Self {
Intern::intern(value)
}
}
impl<T: Intern + Clone> From<T> for Interned<T> {
fn from(value: T) -> Self {
Intern::intern_sized(value)
}
}
impl<T: ?Sized + 'static + Send + Sync + ToOwned> From<Interned<T>> for Cow<'_, T> {
fn from(value: Interned<T>) -> Self {
Cow::Borrowed(Interned::into_inner(value))
}
}
pub struct Interned<T: ?Sized + 'static + Send + Sync> {
inner: &'static T,
}
macro_rules! forward_fmt_trait {
($Tr:ident) => {
impl<T: ?Sized + Intern + fmt::$Tr> fmt::$Tr for LazyInterned<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
T::fmt(&**self, f)
}
}
impl<T: ?Sized + 'static + Send + Sync + fmt::$Tr> fmt::$Tr for Interned<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
}
}
};
}
forward_fmt_trait!(Debug);
forward_fmt_trait!(Display);
forward_fmt_trait!(LowerExp);
forward_fmt_trait!(LowerHex);
forward_fmt_trait!(Octal);
forward_fmt_trait!(Pointer);
forward_fmt_trait!(UpperExp);
forward_fmt_trait!(UpperHex);
impl<T: ?Sized + 'static + Send + Sync + AsRef<U>, U: ?Sized> AsRef<U> for Interned<T> {
fn as_ref(&self) -> &U {
T::as_ref(self)
}
}
#[derive(Clone, Debug)]
pub struct InternedSliceIter<T: Clone + 'static + Send + Sync> {
slice: Interned<[T]>,
index: std::ops::Range<usize>,
}
impl<T: Clone + 'static + Send + Sync> Iterator for InternedSliceIter<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
self.index.next().map(|index| self.slice[index].clone())
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.index.size_hint()
}
}
impl<T: Clone + 'static + Send + Sync> DoubleEndedIterator for InternedSliceIter<T> {
fn next_back(&mut self) -> Option<Self::Item> {
self.index
.next_back()
.map(|index| self.slice[index].clone())
}
}
impl<T: Clone + 'static + Send + Sync> FusedIterator for InternedSliceIter<T> {}
impl<T: Clone + 'static + Send + Sync> ExactSizeIterator for InternedSliceIter<T> {}
impl<T: Clone + 'static + Send + Sync> IntoIterator for Interned<[T]> {
type Item = T;
type IntoIter = InternedSliceIter<T>;
fn into_iter(self) -> Self::IntoIter {
InternedSliceIter {
index: 0..self.len(),
slice: self,
}
}
}
impl<'a, T: 'static + Send + Sync> IntoIterator for &'a Interned<[T]> {
type Item = &'a T;
type IntoIter = std::slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.inner.iter()
}
}
impl<'a, T: 'static + Send + Sync> IntoIterator for &'a mut Interned<[T]> {
type Item = &'a T;
type IntoIter = std::slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.inner.iter()
}
}
impl<I: Clone> FromIterator<I> for Interned<[I]>
where
[I]: Intern,
{
fn from_iter<T: IntoIterator<Item = I>>(iter: T) -> Self {
Intern::intern_owned(Vec::from_iter(iter))
}
}
impl<I> FromIterator<I> for Interned<str>
where
String: FromIterator<I>,
{
fn from_iter<T: IntoIterator<Item = I>>(iter: T) -> Self {
String::from_iter(iter).intern_deref()
}
}
impl<I> FromIterator<I> for Interned<Path>
where
PathBuf: FromIterator<I>,
{
fn from_iter<T: IntoIterator<Item = I>>(iter: T) -> Self {
PathBuf::from_iter(iter).intern_deref()
}
}
impl<I> FromIterator<I> for Interned<OsStr>
where
OsString: FromIterator<I>,
{
fn from_iter<T: IntoIterator<Item = I>>(iter: T) -> Self {
OsString::from_iter(iter).intern_deref()
}
}
impl From<Interned<str>> for clap::builder::Str {
fn from(value: Interned<str>) -> Self {
Interned::into_inner(value).into()
}
}
impl From<Interned<str>> for clap::builder::OsStr {
fn from(value: Interned<str>) -> Self {
Interned::into_inner(value).into()
}
}
impl From<Interned<str>> for clap::builder::StyledStr {
fn from(value: Interned<str>) -> Self {
Interned::into_inner(value).into()
}
}
impl From<Interned<str>> for clap::Id {
fn from(value: Interned<str>) -> Self {
Interned::into_inner(value).into()
}
}
impl<T: 'static + Clone + Send + Sync> From<Interned<[T]>> for Vec<T> {
fn from(value: Interned<[T]>) -> Self {
Vec::from(&*value)
}
}
impl<T: 'static + Clone + Send + Sync> From<Interned<[T]>> for Box<[T]> {
fn from(value: Interned<[T]>) -> Self {
Box::from(&*value)
}
}
impl<I> Default for Interned<[I]>
where
[I]: Intern,
{
fn default() -> Self {
Intern::intern(&[])
}
}
impl Default for Interned<BitSlice> {
fn default() -> Self {
<&BitSlice>::default().intern()
}
}
impl<I: Clone + Default> Default for Interned<I>
where
I: Intern,
{
fn default() -> Self {
I::default().intern()
}
}
impl<T: ?Sized + 'static + Send + Sync> Interned<T> {
pub fn cast_unchecked<U: ?Sized + 'static + Send + Sync>(
this: Self,
f: impl FnOnce(&'static T) -> &'static U,
) -> Interned<U> {
Interned {
inner: f(this.inner),
}
}
pub fn try_cast_unchecked<U: ?Sized + 'static + Send + Sync, E>(
this: Self,
f: impl FnOnce(&'static T) -> Result<&'static U, E>,
) -> Result<Interned<U>, E> {
Ok(Interned {
inner: f(this.inner)?,
})
}
pub fn into_inner(this: Self) -> &'static T {
this.inner
}
pub fn get_ref(this: &Self) -> &&'static T {
&this.inner
}
}
impl<T: ?Sized + 'static + Send + Sync> Clone for Interned<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized + 'static + Send + Sync> Copy for Interned<T> where &'static T: Copy {}
impl<T: ?Sized + 'static + Send + Sync> Deref for Interned<T>
where
&'static T: Borrow<T>,
{
type Target = T;
fn deref(&self) -> &Self::Target {
self.inner
}
}
impl<T: ?Sized + 'static + Send + Sync + InternedCompare> PartialEq for Interned<T> {
fn eq(&self, other: &Self) -> bool {
T::interned_compare_key_ref(self.inner) == T::interned_compare_key_ref(other.inner)
}
}
impl<T: ?Sized + 'static + Send + Sync + InternedCompare> Eq for Interned<T> {}
impl<T: ?Sized + 'static + Send + Sync + InternedCompare> PartialOrd for Interned<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<T: ?Sized + 'static + Send + Sync + InternedCompare> Ord for Interned<T> {
fn cmp(&self, other: &Self) -> Ordering {
T::interned_compare_key_ref(self.inner).cmp(&T::interned_compare_key_ref(other.inner))
}
}
impl<T: ?Sized + 'static + Send + Sync + InternedCompare> Hash for Interned<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
T::interned_compare_key_ref(self.inner).hash(state);
}
}
impl<T: ?Sized + 'static + Send + Sync + Serialize> Serialize for Interned<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
T::serialize(self, serializer)
}
}
impl<'de, T: 'static + Send + Sync + Deserialize<'de> + Clone + Intern> Deserialize<'de>
for Interned<T>
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
T::deserialize(deserializer).map(Intern::intern_sized)
}
}
impl<'de, T: 'static + Send + Sync + Clone> Deserialize<'de> for Interned<[T]>
where
[T]: Intern,
Vec<T>: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
Vec::<T>::deserialize(deserializer).map(Intern::intern_owned)
}
}
impl<'de> Deserialize<'de> for Interned<BitSlice> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
BitVec::deserialize(deserializer).map(Intern::intern_owned)
}
}
impl<T: Clone + Send + Sync + 'static + Hash + Eq> Intern for T {
fn intern(&self) -> Interned<Self> {
Self::intern_cow(Cow::Borrowed(self))
}
fn intern_owned(this: <Self as ToOwned>::Owned) -> Interned<Self>
where
Self: ToOwned,
{
Self::intern_cow(Cow::Owned(this))
}
fn intern_cow(this: Cow<'_, Self>) -> Interned<Self>
where
Self: ToOwned,
{
interner::Interner::get().intern_sized(this)
}
}
impl<T: Clone + Send + Sync + 'static + Hash + Eq> Intern for [T] {
fn intern(&self) -> Interned<Self> {
Self::intern_cow(Cow::Borrowed(self))
}
fn intern_owned(this: <Self as ToOwned>::Owned) -> Interned<Self>
where
Self: ToOwned,
{
Self::intern_cow(Cow::Owned(this))
}
fn intern_cow(this: Cow<'_, Self>) -> Interned<Self>
where
Self: ToOwned,
{
interner::Interner::get().intern_slice(this)
}
}
impl Intern for BitSlice {
fn intern(&self) -> Interned<Self> {
Self::intern_cow(Cow::Borrowed(self))
}
fn intern_owned(this: <Self as ToOwned>::Owned) -> Interned<Self>
where
Self: ToOwned,
{
Self::intern_cow(Cow::Owned(this))
}
fn intern_cow(this: Cow<'_, Self>) -> Interned<Self>
where
Self: ToOwned,
{
interner::Interner::get().intern_bit_slice(this)
}
}
pub trait MemoizeGeneric: 'static + Send + Sync + Hash + Eq + Copy {
type InputRef<'a>: 'a + Send + Sync + Hash + Copy;
type InputOwned: 'static + Send + Sync;
type InputCow<'a>: 'a;
type Output: 'static + Send + Sync + Clone;
fn input_eq(a: Self::InputRef<'_>, b: Self::InputRef<'_>) -> bool;
fn input_borrow(input: &Self::InputOwned) -> Self::InputRef<'_>;
fn input_cow_into_owned(input: Self::InputCow<'_>) -> Self::InputOwned;
fn input_cow_borrow<'a>(input: &'a Self::InputCow<'_>) -> Self::InputRef<'a>;
fn input_cow_from_owned<'a>(input: Self::InputOwned) -> Self::InputCow<'a>;
fn input_cow_from_ref(input: Self::InputRef<'_>) -> Self::InputCow<'_>;
fn inner(self, input: Self::InputRef<'_>) -> Self::Output;
fn get_cow(self, input: Self::InputCow<'_>) -> Self::Output {
static TYPE_ID_MAP: TypeIdMap = TypeIdMap::new();
thread_local! {
static TYPE_ID_MAP_CACHE: TypeIdMap = const { TypeIdMap::new() };
}
let map: &RwLock<(
DefaultBuildHasher,
HashTable<(Self, Self::InputOwned, Self::Output)>,
)> = TYPE_ID_MAP_CACHE.with(|cache| {
cache.get_or_insert_with(|| {
TYPE_ID_MAP.get_or_insert_with(|| Box::leak(Default::default()))
})
});
fn hash_eq_key<'a, 'b, T: MemoizeGeneric>(
this: &'a T,
input: T::InputRef<'b>,
) -> (&'a T, T::InputRef<'b>) {
(this, input)
}
fn hash_eq_key_eq<T: MemoizeGeneric>(
key: (&T, T::InputRef<'_>),
this: &T,
input: T::InputRef<'_>,
) -> bool {
let other = hash_eq_key(this, input);
key.0 == other.0 && T::input_eq(key.1, other.1)
}
let key = hash_eq_key(&self, Self::input_cow_borrow(&input));
let hash;
{
let read = map.read().unwrap();
let (hasher, map) = &*read;
hash = hasher.hash_one(key);
if let Some((_, _, output)) = map.find(hash, |(this2, input2, _)| {
hash_eq_key_eq(key, this2, Self::input_borrow(input2))
}) {
return output.clone();
}
drop(read);
}
// make sure to call inner while map isn't locked since inner may call get_cow again
let output = self.inner(Self::input_cow_borrow(&input));
let mut write = map.write().unwrap();
let (hasher, map) = &mut *write;
map.entry(
hash,
|(this2, input2, _)| hash_eq_key_eq(key, this2, Self::input_borrow(input2)),
|(this2, input2, _)| hasher.hash_one(hash_eq_key(this2, Self::input_borrow(input2))),
)
.or_insert_with(|| (self, Self::input_cow_into_owned(input), output))
.get()
.2
.clone()
}
fn get_owned(self, input: Self::InputOwned) -> Self::Output {
self.get_cow(Self::input_cow_from_owned(input))
}
fn get(self, input: Self::InputRef<'_>) -> Self::Output {
self.get_cow(Self::input_cow_from_ref(input))
}
}
pub trait Memoize: 'static + Send + Sync + Hash + Eq + Copy {
type Input: ?Sized + 'static + Send + Sync + ToOwned<Owned = Self::InputOwned> + Hash + Eq;
type InputOwned: 'static + Send + Sync + Borrow<Self::Input>;
type Output: 'static + Send + Sync + Clone;
fn inner(self, input: &Self::Input) -> Self::Output;
fn get_cow(self, input: Cow<'_, Self::Input>) -> Self::Output {
#[derive(Hash, Eq, PartialEq, Copy, Clone)]
struct MemoizeGenericWrapper<T: Memoize>(T);
impl<T: Memoize> MemoizeGeneric for MemoizeGenericWrapper<T> {
type InputRef<'a> = &'a T::Input;
type InputOwned = T::InputOwned;
type InputCow<'a> = Cow<'a, T::Input>;
type Output = T::Output;
fn input_borrow(input: &Self::InputOwned) -> Self::InputRef<'_> {
input.borrow()
}
fn input_eq(a: Self::InputRef<'_>, b: Self::InputRef<'_>) -> bool {
a == b
}
fn input_cow_into_owned(input: Self::InputCow<'_>) -> Self::InputOwned {
input.into_owned()
}
fn input_cow_borrow<'a>(input: &'a Self::InputCow<'_>) -> Self::InputRef<'a> {
&**input
}
fn input_cow_from_owned<'a>(input: Self::InputOwned) -> Self::InputCow<'a> {
Cow::Owned(input)
}
fn input_cow_from_ref(input: Self::InputRef<'_>) -> Self::InputCow<'_> {
Cow::Borrowed(input)
}
fn inner(self, input: Self::InputRef<'_>) -> Self::Output {
self.0.inner(input)
}
}
MemoizeGenericWrapper(self).get_cow(input)
}
fn get_owned(self, input: Self::InputOwned) -> Self::Output {
self.get_cow(Cow::Owned(input))
}
fn get(self, input: &Self::Input) -> Self::Output {
self.get_cow(Cow::Borrowed(input))
}
}
/// like `once_cell::race::OnceBox` but for `Interned<T>` instead of `Box<T>`
pub struct OnceInterned<T: 'static + Send + Sync>(OnceRef<'static, T>);
impl<T: 'static + Send + Sync + fmt::Debug> fmt::Debug for OnceInterned<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("OnceInterned").field(&self.get()).finish()
}
}
impl<T: 'static + Send + Sync> Default for OnceInterned<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: 'static + Send + Sync> OnceInterned<T> {
pub const fn new() -> Self {
Self(OnceRef::new())
}
pub fn set(&self, v: Interned<T>) -> Result<(), ()> {
self.0.set(v.inner)
}
pub fn get(&self) -> Option<Interned<T>> {
self.0.get().map(|inner| Interned { inner })
}
pub fn get_or_init<F: FnOnce() -> Interned<T>>(&self, f: F) -> Interned<T> {
Interned {
inner: self.0.get_or_init(|| f().inner),
}
}
}