1168 lines
34 KiB
Rust
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),
|
|
}
|
|
}
|
|
}
|