use std::borrow::Cow; use std::collections::hash_map::RandomState; use std::fmt::Debug; use std::hash::{BuildHasher, Hash}; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; use crate::global::GlobalPool; use crate::pool::{Pool, PoolKindSealed, Poolable}; use crate::{PoolKind, Pooled}; /// A pooled string that belongs to a [`StringPool`]. pub type SharedString = Pooled, S>; /// A pooled path that belongs to a [`PathPool`]. pub type SharedPath = Pooled, S>; /// A pooled buffer that belongs to a [`BufferPool`]. pub type SharedBuffer = Pooled, S>, S>; /// A string interning pool that manages [`SharedString`]s. /// /// Each [`StringPool`] has its own storage. When comparing [`SharedString`]s /// from separate pools, the full string comparison function must be used. pub type StringPool = SharedPool; /// A path interning pool that manages [`SharedPath`]s. /// /// Each [`PathPool`] has its own storage. When comparing [`SharedPath`]s /// from separate pools, the full string comparison function must be used. pub type PathPool = SharedPool; /// A path interning pool that manages [`SharedBuffer`]s. /// /// Each [`BufferPool`] has its own storage. When comparing [`SharedBuffer`]s /// from separate pools, the full string comparison function must be used. pub type BufferPool = SharedPool, S>; /// A shared pool of values that ensures only one copy of any given value exists /// at any time. /// /// To retrieve a [`Pooled`] value, use [`SharedPool::get()`], which is /// implemented for these types: /// /// - [`String`]/[`&str`](str) /// - [`PathBuf`]/[`&Path`](Path) /// - [`Vec`]/`&[u8]` #[derive(Debug)] pub struct SharedPool(Arc>>) where T: Poolable + Debug + Clone + Eq + PartialEq + Hash + Ord + PartialOrd, S: BuildHasher; impl SharedPool where T: Poolable + Debug + Clone + Eq + PartialEq + Hash + Ord + PartialOrd, S: BuildHasher, { /// Returns a collection of the currently pooled items. #[must_use] pub fn pooled(&self) -> C where C: FromIterator>, { self.with_active_symbols(|pool| { pool.active .iter() .map(|data| Pooled(data.clone())) .collect() }) } } impl SharedPool where S: BuildHasher, { /// Creates a new pool using the provided [`BuildHasher`] for hashing /// values. #[must_use] pub fn with_hasher(hasher: S) -> Self { Self::with_capacity_and_hasher(0, hasher) } /// Creates a new pool using the provided [`BuildHasher`] for hashing /// values. The pool will have enough capacity to allow inserting /// `initial_capacity` pooled entries without reallocation. #[must_use] pub fn with_capacity_and_hasher(initial_capacity: usize, hasher: S) -> Self { Self(Arc::new(Mutex::new(Pool::with_capacity_and_hasher( initial_capacity, hasher, )))) } /// Returns a copy of an existing [`SharedString`] if one is found. /// Otherwise, a new [`SharedString`] is created and returned. /// /// While any copies of the returned [`SharedString`] are still allocated, /// calling this function is guaranteed to return a copy of the same string. #[must_use] pub fn get<'a, V>(&self, value: V) -> SharedString where V: Into>, { let value = value.into(); self.with_active_symbols(|symbols| symbols.get(value, self)) } } impl SharedPool where S: BuildHasher, { /// Returns a copy of an existing [`SharedPath`] if one is found. Otherwise, /// a new [`SharedPath`] is created and returned. /// /// While any copies of the returned [`SharedPath`] are still allocated, /// calling this function is guaranteed to return a copy of the same path. #[must_use] pub fn get<'a, V>(&self, value: V) -> SharedPath where V: Into>, { let value = value.into(); self.with_active_symbols(|symbols| symbols.get(value, self)) } } impl SharedPool, S> where S: BuildHasher, { /// Returns a copy of an existing [`SharedBuffer`] if one is found. Otherwise, /// a new [`SharedBuffer`] is created and returned. /// /// While any copies of the returned [`SharedBuffer`] are still allocated, /// calling this function is guaranteed to return a copy of the same buffer. #[must_use] pub fn get<'a, V>(&self, value: V) -> SharedBuffer where V: Into>, { let value = value.into(); self.with_active_symbols(|symbols| symbols.get(value, self)) } } impl Clone for SharedPool where T: Poolable + Debug + Clone + Eq + PartialEq + Hash + Ord + PartialOrd, S: BuildHasher, { fn clone(&self) -> Self { Self(self.0.clone()) } } impl PoolKind for SharedPool where T: Poolable + Debug + Clone + Eq + PartialEq + Hash + Ord + PartialOrd, S: BuildHasher, { } impl PoolKindSealed for SharedPool where T: Poolable + Debug + Clone + Eq + PartialEq + Hash + Ord + PartialOrd, S: BuildHasher, { type Owned = T; type Pooled = T::Boxed; fn with_active_symbols(&self, logic: impl FnOnce(&mut Pool) -> R) -> R { let mut symbols = self.0.lock().expect("poisoned"); logic(&mut symbols) } fn address_of(&self) -> *const () { Arc::as_ptr(&self.0).cast() } } impl PartialEq for SharedPool where T: Poolable + Debug + Clone + Eq + PartialEq + Hash + Ord + PartialOrd, S: BuildHasher, { fn eq(&self, other: &SharedPool) -> bool { Arc::ptr_eq(&self.0, &other.0) } } impl PartialEq<&'static GlobalPool> for SharedPool where T: Poolable + Debug + Clone + Eq + PartialEq + Hash + Ord + PartialOrd, S: BuildHasher, S2: BuildHasher, { fn eq(&self, _other: &&'static GlobalPool) -> bool { false } } impl PartialEq> for &'static GlobalPool where T: Poolable + Debug + Clone + Eq + PartialEq + Hash + Ord + PartialOrd, S: BuildHasher, S2: BuildHasher, { fn eq(&self, _other: &SharedPool) -> bool { false } } impl Default for SharedPool where T: Poolable + Debug + Clone + Eq + PartialEq + Hash + Ord + PartialOrd, { fn default() -> Self { Self(Arc::default()) } }