diff --git a/src/arenas/atomic_bucket.rs b/src/arenas/atomic_bucket.rs index 6f2abd8..bed65ee 100644 --- a/src/arenas/atomic_bucket.rs +++ b/src/arenas/atomic_bucket.rs @@ -1,4 +1,4 @@ -use crate::{LassoError, LassoErrorKind, LassoResult}; +use crate::{Internable, LassoError, LassoErrorKind, LassoResult}; use alloc::alloc::{alloc, dealloc, Layout}; use core::{ mem::{align_of, size_of}, @@ -183,13 +183,16 @@ impl UniqueBucketRef { /// /// # Safety /// - /// The returned `&'static str` (and all copies of it) must be dropped + /// The returned `&'static V` (and all copies of it) must be dropped /// before the current bucket is, as this bucket contains the backing /// memory for the string. /// Additionally, the underlying [`AtomicBucket`] must have enough room /// to store the entire slice and the given slice must be valid utf-8 data. /// - pub unsafe fn push_slice(&mut self, slice: &[u8]) -> &'static str { + pub unsafe fn push_slice(&mut self, slice: &[u8]) -> &'static V + where + V: ?Sized + Internable, + { let len = self.len(); if cfg!(debug_assertions) { @@ -214,7 +217,7 @@ impl UniqueBucketRef { // Create a string from that slice // Safety: The source string was valid utf8, so the created buffer will be as well - unsafe { core::str::from_utf8_unchecked(target) } + unsafe { V::from_slice(target) } } } diff --git a/src/arenas/bucket.rs b/src/arenas/bucket.rs index a0a3b97..e05af08 100644 --- a/src/arenas/bucket.rs +++ b/src/arenas/bucket.rs @@ -1,4 +1,4 @@ -use crate::{LassoError, LassoErrorKind, LassoResult}; +use crate::{Internable, LassoError, LassoErrorKind, LassoResult}; use alloc::alloc::{alloc, dealloc, Layout}; use core::{mem, num::NonZeroUsize, ptr::NonNull, slice}; @@ -59,9 +59,9 @@ impl Bucket { /// /// The current bucket must have room for all bytes of the slice and /// the caller promises to forget the reference before the arena is dropped. - /// Additionally, `slice` must be valid UTF-8 and should come from an `&str` + /// Additionally, `slice` must be valid for the interned type and should come from an `&V` /// - pub(crate) unsafe fn push_slice(&mut self, slice: &[u8]) -> &'static str { + pub(crate) unsafe fn push_slice(&mut self, slice: &[u8]) -> &'static V { debug_assert!(!self.is_full()); debug_assert!(slice.len() <= self.capacity.get() - self.index); @@ -77,8 +77,8 @@ impl Bucket { self.index += slice.len(); // Create a string from that slice - // Safety: The source string was valid utf8, so the created buffer will be as well - core::str::from_utf8_unchecked(target) + // Safety: The source buffer was created by a call to V::as_bytes + V::from_slice(target) } } } diff --git a/src/arenas/lockfree.rs b/src/arenas/lockfree.rs index 06d3e97..ae2e6a5 100644 --- a/src/arenas/lockfree.rs +++ b/src/arenas/lockfree.rs @@ -1,7 +1,4 @@ -use crate::{ - arenas::atomic_bucket::{AtomicBucket, AtomicBucketList}, - Capacity, LassoError, LassoErrorKind, LassoResult, MemoryLimits, -}; +use crate::{arenas::atomic_bucket::{AtomicBucket, AtomicBucketList}, Capacity, Internable, LassoError, LassoErrorKind, LassoResult, MemoryLimits}; use core::{ fmt::{self, Debug}, num::NonZeroUsize, @@ -79,12 +76,15 @@ impl LockfreeArena { /// /// The reference passed back must be dropped before the arena that created it is /// - pub unsafe fn store_str(&self, string: &str) -> LassoResult<&'static str> { + pub unsafe fn store_str(&self, string: &V) -> LassoResult<&'static V> + where + V: ?Sized + Internable, + { // If the string is empty, simply return an empty string. // This ensures that only strings with lengths greater // than zero will be allocated within the arena if string.is_empty() { - return Ok(""); + return Ok(V::empty()); } let slice = string.as_bytes(); @@ -284,7 +284,7 @@ mod tests { let mut len = 4096; for _ in 0..10 { let large_string = "a".repeat(len); - let arena_string = unsafe { arena.store_str(&large_string) }; + let arena_string = unsafe { arena.store_str::(&large_string) }; assert_eq!(arena_string, Ok(large_string.as_str())); len *= 2; diff --git a/src/arenas/single_threaded.rs b/src/arenas/single_threaded.rs index 3f61fa4..13c2d4b 100644 --- a/src/arenas/single_threaded.rs +++ b/src/arenas/single_threaded.rs @@ -1,6 +1,4 @@ -use crate::{ - arenas::bucket::Bucket, Capacity, LassoError, LassoErrorKind, LassoResult, MemoryLimits, -}; +use crate::{arenas::bucket::Bucket, Capacity, Internable, LassoError, LassoErrorKind, LassoResult, MemoryLimits}; use alloc::{format, vec, vec::Vec}; use core::{fmt, num::NonZeroUsize}; @@ -50,12 +48,15 @@ impl Arena { /// /// The reference passed back must be dropped before the arena that created it is /// - pub unsafe fn store_str(&mut self, string: &str) -> LassoResult<&'static str> { + pub unsafe fn store_str(&mut self, string: &V) -> LassoResult<&'static V> + where + V: ?Sized + Internable, + { // If the string is empty, simply return an empty string. // This ensures that only strings with lengths greater // than zero will be allocated within the arena if string.is_empty() { - return Ok(""); + return Ok(V::empty()); } let slice = string.as_bytes(); @@ -196,7 +197,7 @@ mod tests { let mut len = 4096; for _ in 0..10 { let large_string = "a".repeat(len); - let arena_string = unsafe { arena.store_str(&large_string) }; + let arena_string = unsafe { arena.store_str::(&large_string) }; assert_eq!(arena_string, Ok(large_string.as_str())); len *= 2; diff --git a/src/interface/boxed.rs b/src/interface/boxed.rs index e34be30..e059e40 100644 --- a/src/interface/boxed.rs +++ b/src/interface/boxed.rs @@ -1,40 +1,42 @@ use super::{Interner, IntoReader, IntoResolver, Reader, Resolver}; -use crate::{Key, LassoResult}; +use crate::{Internable, Key, LassoResult}; #[cfg(feature = "no-std")] use alloc::boxed::Box; -impl Interner for Box +impl Interner for Box where K: Key, - I: Interner + ?Sized + 'static, + V: ?Sized + Internable, + I: Interner + ?Sized + 'static, { #[cfg_attr(feature = "inline-more", inline)] - fn get_or_intern(&mut self, val: &str) -> K { + fn get_or_intern(&mut self, val: &V) -> K { (&mut **self).get_or_intern(val) } #[cfg_attr(feature = "inline-more", inline)] - fn try_get_or_intern(&mut self, val: &str) -> LassoResult { + fn try_get_or_intern(&mut self, val: &V) -> LassoResult { (&mut **self).try_get_or_intern(val) } #[cfg_attr(feature = "inline-more", inline)] - fn get_or_intern_static(&mut self, val: &'static str) -> K { + fn get_or_intern_static(&mut self, val: &'static V) -> K { (&mut **self).get_or_intern_static(val) } #[cfg_attr(feature = "inline-more", inline)] - fn try_get_or_intern_static(&mut self, val: &'static str) -> LassoResult { + fn try_get_or_intern_static(&mut self, val: &'static V) -> LassoResult { self.try_get_or_intern(val) } } -impl IntoReader for Box +impl IntoReader for Box where K: Key, - I: IntoReader + ?Sized + 'static, + V: ?Sized + Internable, + I: IntoReader + ?Sized + 'static, { - type Reader = >::Reader; + type Reader = >::Reader; #[cfg_attr(feature = "inline-more", inline)] #[must_use] @@ -55,28 +57,30 @@ where } } -impl Reader for Box +impl Reader for Box where K: Key, - I: Reader + ?Sized + 'static, + V: ?Sized + Internable, + I: Reader + ?Sized + 'static, { #[cfg_attr(feature = "inline-more", inline)] - fn get(&self, val: &str) -> Option { + fn get(&self, val: &V) -> Option { (&**self).get(val) } #[cfg_attr(feature = "inline-more", inline)] - fn contains(&self, val: &str) -> bool { + fn contains(&self, val: &V) -> bool { (&**self).contains(val) } } -impl IntoResolver for Box +impl IntoResolver for Box where K: Key, - I: IntoResolver + ?Sized + 'static, + V: ?Sized + Internable, + I: IntoResolver + ?Sized + 'static, { - type Resolver = >::Resolver; + type Resolver = >::Resolver; #[cfg_attr(feature = "inline-more", inline)] #[must_use] @@ -97,23 +101,24 @@ where } } -impl Resolver for Box +impl Resolver for Box where K: Key, - I: Resolver + ?Sized + 'static, + V: ?Sized + Internable, + I: Resolver + ?Sized + 'static, { #[cfg_attr(feature = "inline-more", inline)] - fn resolve<'a>(&'a self, key: &K) -> &'a str { + fn resolve<'a>(&'a self, key: &K) -> &'a V { (&**self).resolve(key) } #[cfg_attr(feature = "inline-more", inline)] - fn try_resolve<'a>(&'a self, key: &K) -> Option<&'a str> { + fn try_resolve<'a>(&'a self, key: &K) -> Option<&'a V> { (&**self).try_resolve(key) } #[cfg_attr(feature = "inline-more", inline)] - unsafe fn resolve_unchecked<'a>(&'a self, key: &K) -> &'a str { + unsafe fn resolve_unchecked<'a>(&'a self, key: &K) -> &'a V { unsafe { (&**self).resolve_unchecked(key) } } diff --git a/src/interface/mod.rs b/src/interface/mod.rs index 0d125ff..6dd6111 100644 --- a/src/interface/mod.rs +++ b/src/interface/mod.rs @@ -6,7 +6,7 @@ mod tests; mod threaded_ref; mod threaded_rodeo; -use crate::{Key, LassoResult, Spur}; +use crate::{Internable, Key, LassoResult, Spur}; #[cfg(feature = "no-std")] use alloc::boxed::Box; @@ -16,7 +16,7 @@ use alloc::boxed::Box; /// Note that because single-threaded [`Rodeo`](crate::Rodeo)s require mutable access to use, this /// trait does so as well. For use with [`ThreadedRodeo`](crate::ThreadedRodeo), the trait is /// implemented for `&ThreadedRodeo` as well to allow access through shared references. -pub trait Interner: Reader + Resolver { +pub trait Interner: Reader + Resolver { /// Get the key for a string, interning it if it does not yet exist /// /// # Panics @@ -25,10 +25,10 @@ pub trait Interner: Reader + Resolver { /// keys, this means that you've interned more strings than it can handle. (For [`Spur`] this /// means that `u32::MAX - 1` unique strings were interned) /// - fn get_or_intern(&mut self, val: &str) -> K; + fn get_or_intern(&mut self, val: &V) -> K; /// Get the key for a string, interning it if it does not yet exist - fn try_get_or_intern(&mut self, val: &str) -> LassoResult; + fn try_get_or_intern(&mut self, val: &V) -> LassoResult; /// Get the key for a static string, interning it if it does not yet exist /// @@ -40,54 +40,57 @@ pub trait Interner: Reader + Resolver { /// keys, this means that you've interned more strings than it can handle. (For [`Spur`] this /// means that `u32::MAX - 1` unique strings were interned) /// - fn get_or_intern_static(&mut self, val: &'static str) -> K; + fn get_or_intern_static(&mut self, val: &'static V) -> K; /// Get the key for a static string, interning it if it does not yet exist /// /// This will not reallocate or copy the given string - fn try_get_or_intern_static(&mut self, val: &'static str) -> LassoResult; + fn try_get_or_intern_static(&mut self, val: &'static V) -> LassoResult; } -impl Interner for &mut T +impl Interner for &mut T where - T: Interner, + V: ?Sized, + T: Interner, { #[inline] - fn get_or_intern(&mut self, val: &str) -> K { - >::get_or_intern(self, val) + fn get_or_intern(&mut self, val: &V) -> K { + >::get_or_intern(self, val) } #[inline] - fn try_get_or_intern(&mut self, val: &str) -> LassoResult { - >::try_get_or_intern(self, val) + fn try_get_or_intern(&mut self, val: &V) -> LassoResult { + >::try_get_or_intern(self, val) } #[inline] - fn get_or_intern_static(&mut self, val: &'static str) -> K { - >::get_or_intern_static(self, val) + fn get_or_intern_static(&mut self, val: &'static V) -> K { + >::get_or_intern_static(self, val) } #[inline] - fn try_get_or_intern_static(&mut self, val: &'static str) -> LassoResult { - >::try_get_or_intern_static(self, val) + fn try_get_or_intern_static(&mut self, val: &'static V) -> LassoResult { + >::try_get_or_intern_static(self, val) } } /// A generic interface over interners that can be turned into both a [`Reader`] and a [`Resolver`] /// directly. -pub trait IntoReaderAndResolver: IntoReader + IntoResolver +pub trait IntoReaderAndResolver: IntoReader + IntoResolver where K: Key, + V: ?Sized + Internable, { } /// A generic interface over interners that can be turned into a [`Reader`]. -pub trait IntoReader: Interner +pub trait IntoReader: Interner where K: Key, + V: ?Sized + Internable, { /// The type of [`Reader`] the interner will be converted into - type Reader: Reader; + type Reader: Reader; /// Consumes the current [`Interner`] and converts it into a [`Reader`] to allow /// contention-free access of the interner from multiple threads @@ -107,52 +110,55 @@ where /// A generic interface that allows using any underlying interner for /// both its reading and resolution capabilities, allowing both -/// `str -> key` and `key -> str` lookups -pub trait Reader: Resolver { +/// `V -> key` and `key -> V` lookups +pub trait Reader: Resolver { /// Get a key for the given string value if it exists - fn get(&self, val: &str) -> Option; + fn get(&self, val: &V) -> Option; /// Returns `true` if the current interner contains the given string - fn contains(&self, val: &str) -> bool; + fn contains(&self, val: &V) -> bool; } -impl Reader for &T +impl Reader for &T where - T: Reader, + V: ?Sized, + T: Reader, { #[inline] - fn get(&self, val: &str) -> Option { - >::get(self, val) + fn get(&self, val: &V) -> Option { + >::get(self, val) } #[inline] - fn contains(&self, val: &str) -> bool { - >::contains(self, val) + fn contains(&self, val: &V) -> bool { + >::contains(self, val) } } -impl Reader for &mut T +impl Reader for &mut T where - T: Reader, + V: ?Sized, + T: Reader, { #[inline] - fn get(&self, val: &str) -> Option { - >::get(self, val) + fn get(&self, val: &V) -> Option { + >::get(self, val) } #[inline] - fn contains(&self, val: &str) -> bool { - >::contains(self, val) + fn contains(&self, val: &V) -> bool { + >::contains(self, val) } } /// A generic interface over [`Reader`]s that can be turned into a [`Resolver`]. -pub trait IntoResolver: Reader +pub trait IntoResolver: Reader where K: Key, + V: ?Sized + Internable, { /// The type of [`Resolver`] the reader will be converted into - type Resolver: Resolver; + type Resolver: Resolver; /// Consumes the current [`Reader`] and makes it into a [`Resolver`], allowing /// contention-free access from multiple threads with the lowest possible memory consumption @@ -171,19 +177,19 @@ where } /// A generic interface that allows using any underlying interner only -/// for its resolution capabilities, allowing only `key -> str` lookups -pub trait Resolver { +/// for its resolution capabilities, allowing only `key -> V` lookups +pub trait Resolver { /// Resolves the given key into a string /// /// # Panics /// /// Panics if the key is not contained in the current [`Resolver`] /// - fn resolve<'a>(&'a self, key: &K) -> &'a str; + fn resolve<'a>(&'a self, key: &K) -> &'a V; /// Attempts to resolve the given key into a string, returning `None` /// if it cannot be found - fn try_resolve<'a>(&'a self, key: &K) -> Option<&'a str>; + fn try_resolve<'a>(&'a self, key: &K) -> Option<&'a V>; /// Resolves a string by its key without preforming bounds checks /// @@ -191,7 +197,7 @@ pub trait Resolver { /// /// The key must be valid for the current [`Resolver`] /// - unsafe fn resolve_unchecked<'a>(&'a self, key: &K) -> &'a str; + unsafe fn resolve_unchecked<'a>(&'a self, key: &K) -> &'a V; /// Returns `true` if the current interner contains the given key fn contains_key(&self, key: &K) -> bool; @@ -206,62 +212,64 @@ pub trait Resolver { } } -impl Resolver for &T +impl Resolver for &T where - T: Resolver, + V: ?Sized, + T: Resolver, { #[inline] - fn resolve<'a>(&'a self, key: &K) -> &'a str { - >::resolve(self, key) + fn resolve<'a>(&'a self, key: &K) -> &'a V { + >::resolve(self, key) } #[inline] - fn try_resolve<'a>(&'a self, key: &K) -> Option<&'a str> { - >::try_resolve(self, key) + fn try_resolve<'a>(&'a self, key: &K) -> Option<&'a V> { + >::try_resolve(self, key) } #[inline] - unsafe fn resolve_unchecked<'a>(&'a self, key: &K) -> &'a str { - unsafe { >::resolve_unchecked(self, key) } + unsafe fn resolve_unchecked<'a>(&'a self, key: &K) -> &'a V { + unsafe { >::resolve_unchecked(self, key) } } #[inline] fn contains_key(&self, key: &K) -> bool { - >::contains_key(self, key) + >::contains_key(self, key) } #[inline] fn len(&self) -> usize { - >::len(self) + >::len(self) } } -impl Resolver for &mut T +impl Resolver for &mut T where - T: Resolver, + V: ?Sized, + T: Resolver, { #[inline] - fn resolve<'a>(&'a self, key: &K) -> &'a str { - >::resolve(self, key) + fn resolve<'a>(&'a self, key: &K) -> &'a V { + >::resolve(self, key) } #[inline] - fn try_resolve<'a>(&'a self, key: &K) -> Option<&'a str> { - >::try_resolve(self, key) + fn try_resolve<'a>(&'a self, key: &K) -> Option<&'a V> { + >::try_resolve(self, key) } #[inline] - unsafe fn resolve_unchecked<'a>(&'a self, key: &K) -> &'a str { - unsafe { >::resolve_unchecked(self, key) } + unsafe fn resolve_unchecked<'a>(&'a self, key: &K) -> &'a V { + unsafe { >::resolve_unchecked(self, key) } } #[inline] fn contains_key(&self, key: &K) -> bool { - >::contains_key(self, key) + >::contains_key(self, key) } #[inline] fn len(&self) -> usize { - >::len(self) + >::len(self) } } diff --git a/src/interface/rodeo.rs b/src/interface/rodeo.rs index 96278dc..19ec6a6 100644 --- a/src/interface/rodeo.rs +++ b/src/interface/rodeo.rs @@ -6,45 +6,48 @@ use alloc::boxed::Box; use core::hash::BuildHasher; use interface::IntoReaderAndResolver; -impl Interner for Rodeo +impl Interner for Rodeo where K: Key, + V: ?Sized + Internable, S: BuildHasher, { #[cfg_attr(feature = "inline-more", inline)] - fn get_or_intern(&mut self, val: &str) -> K { + fn get_or_intern(&mut self, val: &V) -> K { self.get_or_intern(val) } #[cfg_attr(feature = "inline-more", inline)] - fn try_get_or_intern(&mut self, val: &str) -> LassoResult { + fn try_get_or_intern(&mut self, val: &V) -> LassoResult { self.try_get_or_intern(val) } #[cfg_attr(feature = "inline-more", inline)] - fn get_or_intern_static(&mut self, val: &'static str) -> K { + fn get_or_intern_static(&mut self, val: &'static V) -> K { self.get_or_intern_static(val) } #[cfg_attr(feature = "inline-more", inline)] - fn try_get_or_intern_static(&mut self, val: &'static str) -> LassoResult { + fn try_get_or_intern_static(&mut self, val: &'static V) -> LassoResult { self.try_get_or_intern_static(val) } } -impl IntoReaderAndResolver for Rodeo +impl IntoReaderAndResolver for Rodeo where K: Key, + V: ?Sized + Internable, S: BuildHasher, { } -impl IntoReader for Rodeo +impl IntoReader for Rodeo where K: Key, + V: ?Sized + Internable, S: BuildHasher, { - type Reader = RodeoReader; + type Reader = RodeoReader; #[cfg_attr(feature = "inline-more", inline)] #[must_use] @@ -65,28 +68,30 @@ where } } -impl Reader for Rodeo +impl Reader for Rodeo where K: Key, + V: ?Sized + Internable, S: BuildHasher, { #[cfg_attr(feature = "inline-more", inline)] - fn get(&self, val: &str) -> Option { + fn get(&self, val: &V) -> Option { self.get(val) } #[cfg_attr(feature = "inline-more", inline)] - fn contains(&self, val: &str) -> bool { + fn contains(&self, val: &V) -> bool { self.contains(val) } } -impl IntoResolver for Rodeo +impl IntoResolver for Rodeo where K: Key, + V: ?Sized + Internable, S: BuildHasher, { - type Resolver = RodeoResolver; + type Resolver = RodeoResolver; #[cfg_attr(feature = "inline-more", inline)] #[must_use] @@ -107,22 +112,23 @@ where } } -impl Resolver for Rodeo +impl Resolver for Rodeo where K: Key, + V: ?Sized + Internable, { #[cfg_attr(feature = "inline-more", inline)] - fn resolve<'a>(&'a self, key: &K) -> &'a str { + fn resolve<'a>(&'a self, key: &K) -> &'a V { self.resolve(key) } #[cfg_attr(feature = "inline-more", inline)] - fn try_resolve<'a>(&'a self, key: &K) -> Option<&'a str> { + fn try_resolve<'a>(&'a self, key: &K) -> Option<&'a V> { self.try_resolve(key) } #[cfg_attr(feature = "inline-more", inline)] - unsafe fn resolve_unchecked<'a>(&'a self, key: &K) -> &'a str { + unsafe fn resolve_unchecked<'a>(&'a self, key: &K) -> &'a V { unsafe { self.resolve_unchecked(key) } } diff --git a/src/interface/rodeo_reader.rs b/src/interface/rodeo_reader.rs index f528065..79f845f 100644 --- a/src/interface/rodeo_reader.rs +++ b/src/interface/rodeo_reader.rs @@ -1,32 +1,34 @@ //! Implementations of [`Reader`] and [`Resolver`] for [`RodeoReader`] -use crate::{IntoResolver, Key, Reader, Resolver, RodeoReader, RodeoResolver}; +use crate::{Internable, IntoResolver, Key, Reader, Resolver, RodeoReader, RodeoResolver}; #[cfg(feature = "no-std")] use alloc::boxed::Box; use core::hash::BuildHasher; -impl Reader for RodeoReader +impl Reader for RodeoReader where K: Key, + V: ?Sized + Internable, S: BuildHasher, { #[cfg_attr(feature = "inline-more", inline)] - fn get(&self, val: &str) -> Option { + fn get(&self, val: &V) -> Option { self.get(val) } #[cfg_attr(feature = "inline-more", inline)] - fn contains(&self, val: &str) -> bool { + fn contains(&self, val: &V) -> bool { self.contains(val) } } -impl IntoResolver for RodeoReader +impl IntoResolver for RodeoReader where K: Key, + V: ?Sized + Internable, S: BuildHasher, { - type Resolver = RodeoResolver; + type Resolver = RodeoResolver; #[cfg_attr(feature = "inline-more", inline)] #[must_use] @@ -47,22 +49,23 @@ where } } -impl Resolver for RodeoReader +impl Resolver for RodeoReader where K: Key, + V: ?Sized + Internable, { #[cfg_attr(feature = "inline-more", inline)] - fn resolve<'a>(&'a self, key: &K) -> &'a str { + fn resolve<'a>(&'a self, key: &K) -> &'a V { self.resolve(key) } #[cfg_attr(feature = "inline-more", inline)] - fn try_resolve<'a>(&'a self, key: &K) -> Option<&'a str> { + fn try_resolve<'a>(&'a self, key: &K) -> Option<&'a V> { self.try_resolve(key) } #[cfg_attr(feature = "inline-more", inline)] - unsafe fn resolve_unchecked<'a>(&'a self, key: &K) -> &'a str { + unsafe fn resolve_unchecked<'a>(&'a self, key: &K) -> &'a V { unsafe { self.resolve_unchecked(key) } } diff --git a/src/interface/rodeo_resolver.rs b/src/interface/rodeo_resolver.rs index 4880671..8d052f5 100644 --- a/src/interface/rodeo_resolver.rs +++ b/src/interface/rodeo_resolver.rs @@ -1,23 +1,24 @@ //! Implementations of [`Resolver`] for [`RodeoResolver`] -use crate::{Key, Resolver, RodeoResolver}; +use crate::{Internable, Key, Resolver, RodeoResolver}; -impl Resolver for RodeoResolver +impl Resolver for RodeoResolver where K: Key, + V: ?Sized + Internable, { #[cfg_attr(feature = "inline-more", inline)] - fn resolve<'a>(&'a self, key: &K) -> &'a str { + fn resolve<'a>(&'a self, key: &K) -> &'a V { self.resolve(key) } #[cfg_attr(feature = "inline-more", inline)] - fn try_resolve<'a>(&'a self, key: &K) -> Option<&'a str> { + fn try_resolve<'a>(&'a self, key: &K) -> Option<&'a V> { self.try_resolve(key) } #[cfg_attr(feature = "inline-more", inline)] - unsafe fn resolve_unchecked<'a>(&'a self, key: &K) -> &'a str { + unsafe fn resolve_unchecked<'a>(&'a self, key: &K) -> &'a V { unsafe { self.resolve_unchecked(key) } } diff --git a/src/interface/threaded_ref.rs b/src/interface/threaded_ref.rs index 422acf8..13283e6 100644 --- a/src/interface/threaded_ref.rs +++ b/src/interface/threaded_ref.rs @@ -1,26 +1,27 @@ #![cfg(feature = "multi-threaded")] -use crate::{Interner, Key, ThreadedRodeo}; +use crate::{Internable, Interner, Key, ThreadedRodeo}; use core::hash::{BuildHasher, Hash}; -impl Interner for &ThreadedRodeo +impl Interner for &ThreadedRodeo where K: Key + Hash, + V: ?Sized + Internable, S: BuildHasher + Clone, { - fn get_or_intern(&mut self, val: &str) -> K { + fn get_or_intern(&mut self, val: &V) -> K { ThreadedRodeo::get_or_intern(self, val) } - fn try_get_or_intern(&mut self, val: &str) -> crate::LassoResult { + fn try_get_or_intern(&mut self, val: &V) -> crate::LassoResult { ThreadedRodeo::try_get_or_intern(self, val) } - fn get_or_intern_static(&mut self, val: &'static str) -> K { + fn get_or_intern_static(&mut self, val: &'static V) -> K { ThreadedRodeo::get_or_intern_static(self, val) } - fn try_get_or_intern_static(&mut self, val: &'static str) -> crate::LassoResult { + fn try_get_or_intern_static(&mut self, val: &'static V) -> crate::LassoResult { ThreadedRodeo::try_get_or_intern_static(self, val) } } diff --git a/src/interface/threaded_rodeo.rs b/src/interface/threaded_rodeo.rs index 0dc66d7..8075f70 100644 --- a/src/interface/threaded_rodeo.rs +++ b/src/interface/threaded_rodeo.rs @@ -6,45 +6,48 @@ use crate::*; use alloc::boxed::Box; use core::hash::{BuildHasher, Hash}; -impl Interner for ThreadedRodeo +impl Interner for ThreadedRodeo where K: Key + Hash, + V: ?Sized + Internable, S: BuildHasher + Clone, { #[cfg_attr(feature = "inline-more", inline)] - fn get_or_intern(&mut self, val: &str) -> K { + fn get_or_intern(&mut self, val: &V) -> K { (&*self).get_or_intern(val) } #[cfg_attr(feature = "inline-more", inline)] - fn try_get_or_intern(&mut self, val: &str) -> LassoResult { + fn try_get_or_intern(&mut self, val: &V) -> LassoResult { (&*self).try_get_or_intern(val) } #[cfg_attr(feature = "inline-more", inline)] - fn get_or_intern_static(&mut self, val: &'static str) -> K { + fn get_or_intern_static(&mut self, val: &'static V) -> K { (&*self).get_or_intern_static(val) } #[cfg_attr(feature = "inline-more", inline)] - fn try_get_or_intern_static(&mut self, val: &'static str) -> LassoResult { + fn try_get_or_intern_static(&mut self, val: &'static V) -> LassoResult { (&*self).try_get_or_intern_static(val) } } -impl IntoReaderAndResolver for ThreadedRodeo +impl IntoReaderAndResolver for ThreadedRodeo where K: Key + Hash, + V: ?Sized + Internable, S: BuildHasher + Clone, { } -impl IntoReader for ThreadedRodeo +impl IntoReader for ThreadedRodeo where K: Key + Hash, + V: ?Sized + Internable, S: BuildHasher + Clone, { - type Reader = RodeoReader; + type Reader = RodeoReader; #[cfg_attr(feature = "inline-more", inline)] #[must_use] @@ -65,28 +68,30 @@ where } } -impl Reader for ThreadedRodeo +impl Reader for ThreadedRodeo where K: Key + Hash, + V: ?Sized + Internable, S: BuildHasher + Clone, { #[cfg_attr(feature = "inline-more", inline)] - fn get(&self, val: &str) -> Option { + fn get(&self, val: &V) -> Option { self.get(val) } #[cfg_attr(feature = "inline-more", inline)] - fn contains(&self, val: &str) -> bool { + fn contains(&self, val: &V) -> bool { self.contains(val) } } -impl IntoResolver for ThreadedRodeo +impl IntoResolver for ThreadedRodeo where K: Key + Hash, + V: ?Sized + Internable, S: BuildHasher + Clone, { - type Resolver = RodeoResolver; + type Resolver = RodeoResolver; #[cfg_attr(feature = "inline-more", inline)] #[must_use] @@ -107,25 +112,26 @@ where } } -impl Resolver for ThreadedRodeo +impl Resolver for ThreadedRodeo where K: Key + Hash, + V: ?Sized + Internable, S: BuildHasher + Clone, { #[cfg_attr(feature = "inline-more", inline)] - fn resolve<'a>(&'a self, key: &K) -> &'a str { + fn resolve<'a>(&'a self, key: &K) -> &'a V { self.resolve(key) } #[cfg_attr(feature = "inline-more", inline)] - fn try_resolve<'a>(&'a self, key: &K) -> Option<&'a str> { + fn try_resolve<'a>(&'a self, key: &K) -> Option<&'a V> { self.try_resolve(key) } /// [`ThreadedRodeo`] does not actually have a `resolve_unchecked()` method, /// so this just forwards to the normal [`ThreadedRodeo::resolve()`] method #[cfg_attr(feature = "inline-more", inline)] - unsafe fn resolve_unchecked<'a>(&'a self, key: &K) -> &'a str { + unsafe fn resolve_unchecked<'a>(&'a self, key: &K) -> &'a V { self.resolve(key) } diff --git a/src/lib.rs b/src/lib.rs index 7a5733a..beebfbb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -446,7 +446,7 @@ pub use keys::{Key, LargeSpur, MicroSpur, MiniSpur, Spur}; pub use reader::RodeoReader; pub use resolver::RodeoResolver; pub use rodeo::Rodeo; -pub use util::{Capacity, Iter, LassoError, LassoErrorKind, LassoResult, MemoryLimits, Strings}; +pub use util::{Capacity, Iter, LassoError, LassoErrorKind, LassoResult, MemoryLimits, Strings, Internable}; compile! { if #[all(feature = "multi-threaded", not(feature = "no-std"))] { diff --git a/src/reader.rs b/src/reader.rs index f77ccb2..e547e06 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -1,14 +1,7 @@ -use crate::{ - arenas::AnyArena, - hasher::RandomState, - keys::{Key, Spur}, - resolver::RodeoResolver, - util::{Iter, Strings}, - Rodeo, -}; +use crate::{arenas::AnyArena, hasher::RandomState, keys::{Key, Spur}, resolver::RodeoResolver, util::{Iter, Strings}, Rodeo, Internable}; use alloc::vec::Vec; use core::{ - hash::{BuildHasher, Hash, Hasher}, + hash::{BuildHasher, Hasher}, ops::Index, }; use hashbrown::HashMap; @@ -22,16 +15,16 @@ use hashbrown::HashMap; /// [`Rodeo`]: crate::Rodeo /// [`ThreadedRodeo`]: crate::ThreadedRodeo #[derive(Debug)] -pub struct RodeoReader { +pub struct RodeoReader { // The logic behind this arrangement is more heavily documented inside of // `Rodeo` itself map: HashMap, hasher: S, - pub(crate) strings: Vec<&'static str>, + pub(crate) strings: Vec<&'static V>, __arena: AnyArena, } -impl RodeoReader { +impl RodeoReader { /// Creates a new RodeoReader /// /// # Safety @@ -42,7 +35,7 @@ impl RodeoReader { pub(crate) unsafe fn new( map: HashMap, hasher: S, - strings: Vec<&'static str>, + strings: Vec<&'static V>, arena: AnyArena, ) -> Self { Self { @@ -73,11 +66,12 @@ impl RodeoReader { #[cfg_attr(feature = "inline-more", inline)] pub fn get(&self, val: T) -> Option where - T: AsRef, + T: AsRef, S: BuildHasher, K: Key, + V: Internable, { - let string_slice: &str = val.as_ref(); + let string_slice: &V = val.as_ref(); // Make a hash of the requested string let hash = { @@ -90,7 +84,7 @@ impl RodeoReader { // Get the map's entry that the string should occupy let entry = self.map.raw_entry().from_hash(hash, |key| { // Safety: The index given by `key` will be in bounds of the strings vector - let key_string: &str = unsafe { index_unchecked!(self.strings, key.into_usize()) }; + let key_string: &V = unsafe { index_unchecked!(self.strings, key.into_usize()) }; // Compare the requested string against the key's string string_slice == key_string @@ -119,9 +113,10 @@ impl RodeoReader { #[cfg_attr(feature = "inline-more", inline)] pub fn contains(&self, val: T) -> bool where - T: AsRef, + T: AsRef, S: BuildHasher, K: Key, + V: Internable, { self.get(val).is_some() } @@ -173,7 +168,7 @@ impl RodeoReader { /// /// [`Key`]: crate::Key #[cfg_attr(feature = "inline-more", inline)] - pub fn resolve<'a>(&'a self, key: &K) -> &'a str + pub fn resolve<'a>(&'a self, key: &K) -> &'a V where K: Key, { @@ -205,7 +200,7 @@ impl RodeoReader { /// /// [`Key`]: crate::Key #[cfg_attr(feature = "inline-more", inline)] - pub fn try_resolve<'a>(&'a self, key: &K) -> Option<&'a str> + pub fn try_resolve<'a>(&'a self, key: &K) -> Option<&'a V> where K: Key, { @@ -245,7 +240,7 @@ impl RodeoReader { /// /// [`Key`]: crate::Key #[cfg_attr(feature = "inline-more", inline)] - pub unsafe fn resolve_unchecked<'a>(&'a self, key: &K) -> &'a str + pub unsafe fn resolve_unchecked<'a>(&'a self, key: &K) -> &'a V where K: Key, { @@ -293,13 +288,13 @@ impl RodeoReader { /// Returns an iterator over the interned strings and their key values #[cfg_attr(feature = "inline-more", inline)] - pub fn iter(&self) -> Iter<'_, K> { + pub fn iter(&self) -> Iter<'_, K, V> { Iter::from_reader(self) } /// Returns an iterator over the interned strings #[cfg_attr(feature = "inline-more", inline)] - pub fn strings(&self) -> Strings<'_, K> { + pub fn strings(&self) -> Strings<'_, K, V> { Strings::from_reader(self) } @@ -326,7 +321,7 @@ impl RodeoReader { /// [`RodeoResolver`]: crate::RodeoResolver #[cfg_attr(feature = "inline-more", inline)] #[must_use] - pub fn into_resolver(self) -> RodeoResolver { + pub fn into_resolver(self) -> RodeoResolver { let RodeoReader { strings, __arena, .. } = self; @@ -337,12 +332,12 @@ impl RodeoReader { } } -unsafe impl Sync for RodeoReader {} -unsafe impl Send for RodeoReader {} +unsafe impl Sync for RodeoReader {} +unsafe impl Send for RodeoReader {} -impl<'a, K: Key, S> IntoIterator for &'a RodeoReader { - type Item = (K, &'a str); - type IntoIter = Iter<'a, K>; +impl<'a, K: Key, V: ?Sized + Internable, S> IntoIterator for &'a RodeoReader { + type Item = (K, &'a V); + type IntoIter = Iter<'a, K, V>; #[cfg_attr(feature = "inline-more", inline)] fn into_iter(self) -> Self::IntoIter { @@ -350,12 +345,13 @@ impl<'a, K: Key, S> IntoIterator for &'a RodeoReader { } } -impl Index for RodeoReader +impl Index for RodeoReader where K: Key, + V: ?Sized + Internable, S: BuildHasher, { - type Output = str; + type Output = V; #[cfg_attr(feature = "inline-more", inline)] fn index(&self, idx: K) -> &Self::Output { @@ -363,25 +359,25 @@ where } } -impl Eq for RodeoReader {} +impl Eq for RodeoReader {} -impl PartialEq for RodeoReader { +impl PartialEq for RodeoReader { #[cfg_attr(feature = "inline-more", inline)] fn eq(&self, other: &Self) -> bool { self.strings == other.strings } } -impl PartialEq> for RodeoReader { +impl PartialEq> for RodeoReader { #[cfg_attr(feature = "inline-more", inline)] - fn eq(&self, other: &RodeoResolver) -> bool { + fn eq(&self, other: &RodeoResolver) -> bool { self.strings == other.strings } } -impl PartialEq> for RodeoReader { +impl PartialEq> for RodeoReader { #[cfg_attr(feature = "inline-more", inline)] - fn eq(&self, other: &Rodeo) -> bool { + fn eq(&self, other: &Rodeo) -> bool { self.strings == other.strings } } @@ -389,7 +385,6 @@ impl PartialEq> for RodeoReader { compile! { if #[feature = "serialize"] { use crate::{Capacity, arenas::Arena}; - use alloc::string::String; use core::num::NonZeroUsize; use hashbrown::hash_map::RawEntryMut; use serde::{ @@ -400,25 +395,31 @@ compile! { } #[cfg(feature = "serialize")] -impl Serialize for RodeoReader { +impl Serialize for RodeoReader +where + V: ?Sized + Internable +{ #[cfg_attr(feature = "inline-more", inline)] fn serialize(&self, serializer: S) -> Result where S: Serializer, { - // Serialize all of self as a `Vec` - self.strings.serialize(serializer) + // Serialize all of self as a `Vec>` + self.strings.iter() + .map(|v| v.as_bytes()) + .collect::>() + .serialize(serializer) } } #[cfg(feature = "serialize")] -impl<'de, K: Key, S: BuildHasher + Default> Deserialize<'de> for RodeoReader { +impl<'de, K: Key, V: ?Sized + Internable, S: BuildHasher + Default> Deserialize<'de> for RodeoReader { #[cfg_attr(feature = "inline-more", inline)] fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { - let vector: Vec = Vec::deserialize(deserializer)?; + let vector: Vec> = Vec::deserialize(deserializer)?; let capacity = { let total_bytes = vector.iter().map(|s| s.len()).sum::(); let total_bytes = @@ -436,7 +437,7 @@ impl<'de, K: Key, S: BuildHasher + Default> Deserialize<'de> for RodeoReader Deserialize<'de> for RodeoReader Deserialize<'de> for RodeoReader { +pub struct RodeoResolver { /// Vector of strings mapped to key indexes that allows key to string resolution - pub(crate) strings: Vec<&'static str>, + pub(crate) strings: Vec<&'static V>, /// The arena that contains all the strings /// /// This is not touched, but *must* be kept since every string in `self.strings` @@ -27,7 +22,7 @@ pub struct RodeoResolver { __key: PhantomData, } -impl RodeoResolver { +impl RodeoResolver { /// Creates a new RodeoResolver /// /// # Safety @@ -35,7 +30,7 @@ impl RodeoResolver { /// The references inside of `strings` must be absolutely unique, meaning /// that no other references to those strings exist /// - pub(crate) unsafe fn new(strings: Vec<&'static str>, arena: AnyArena) -> Self { + pub(crate) unsafe fn new(strings: Vec<&'static V>, arena: AnyArena) -> Self { Self { strings, __arena: arena, @@ -65,7 +60,7 @@ impl RodeoResolver { /// /// [`Key`]: crate::Key #[cfg_attr(feature = "inline-more", inline)] - pub fn resolve<'a>(&'a self, key: &K) -> &'a str + pub fn resolve<'a>(&'a self, key: &K) -> &'a V where K: Key, { @@ -98,7 +93,7 @@ impl RodeoResolver { /// /// [`Key`]: crate::Key #[cfg_attr(feature = "inline-more", inline)] - pub fn try_resolve<'a>(&'a self, key: &K) -> Option<&'a str> + pub fn try_resolve<'a>(&'a self, key: &K) -> Option<&'a V> where K: Key, { @@ -138,7 +133,7 @@ impl RodeoResolver { /// /// [`Key`]: crate::Key #[cfg_attr(feature = "inline-more", inline)] - pub unsafe fn resolve_unchecked<'a>(&'a self, key: &K) -> &'a str + pub unsafe fn resolve_unchecked<'a>(&'a self, key: &K) -> &'a V where K: Key, { @@ -211,23 +206,23 @@ impl RodeoResolver { /// Returns an iterator over the interned strings and their key values #[cfg_attr(feature = "inline-more", inline)] - pub fn iter(&self) -> Iter<'_, K> { + pub fn iter(&self) -> Iter<'_, K, V> { Iter::from_resolver(self) } /// Returns an iterator over the interned strings #[cfg_attr(feature = "inline-more", inline)] - pub fn strings(&self) -> Strings<'_, K> { + pub fn strings(&self) -> Strings<'_, K, V> { Strings::from_resolver(self) } } -unsafe impl Send for RodeoResolver {} -unsafe impl Sync for RodeoResolver {} +unsafe impl Send for RodeoResolver {} +unsafe impl Sync for RodeoResolver {} -impl<'a, K: Key> IntoIterator for &'a RodeoResolver { - type Item = (K, &'a str); - type IntoIter = Iter<'a, K>; +impl<'a, K: Key, V: ?Sized + Internable> IntoIterator for &'a RodeoResolver { + type Item = (K, &'a V); + type IntoIter = Iter<'a, K, V>; #[cfg_attr(feature = "inline-more", inline)] fn into_iter(self) -> Self::IntoIter { @@ -235,8 +230,8 @@ impl<'a, K: Key> IntoIterator for &'a RodeoResolver { } } -impl Index for RodeoResolver { - type Output = str; +impl Index for RodeoResolver { + type Output = V; #[cfg_attr(feature = "inline-more", inline)] fn index(&self, idx: K) -> &Self::Output { @@ -244,25 +239,25 @@ impl Index for RodeoResolver { } } -impl Eq for RodeoResolver {} +impl Eq for RodeoResolver {} -impl PartialEq for RodeoResolver { +impl PartialEq for RodeoResolver { #[cfg_attr(feature = "inline-more", inline)] fn eq(&self, other: &Self) -> bool { self.strings == other.strings } } -impl PartialEq> for RodeoResolver { +impl PartialEq> for RodeoResolver { #[cfg_attr(feature = "inline-more", inline)] - fn eq(&self, other: &RodeoReader) -> bool { + fn eq(&self, other: &RodeoReader) -> bool { self.strings == other.strings } } -impl PartialEq> for RodeoResolver { +impl PartialEq> for RodeoResolver { #[cfg_attr(feature = "inline-more", inline)] - fn eq(&self, other: &Rodeo) -> bool { + fn eq(&self, other: &Rodeo) -> bool { self.strings == other.strings } } @@ -270,7 +265,6 @@ impl PartialEq> for RodeoResolver { compile! { if #[feature = "serialize"] { use crate::{Capacity, arenas::Arena}; - use alloc::string::String; use core::num::NonZeroUsize; use serde::{ de::{Deserialize, Deserializer}, @@ -280,25 +274,31 @@ compile! { } #[cfg(feature = "serialize")] -impl Serialize for RodeoResolver { +impl Serialize for RodeoResolver +where + V: ?Sized + Internable, +{ #[cfg_attr(feature = "inline-more", inline)] fn serialize(&self, serializer: S) -> Result where S: Serializer, { - // Serialize all of self as a `Vec` - self.strings.serialize(serializer) + // Serialize all of self as a `Vec>` + self.strings.iter() + .map(|v| v.as_bytes()) + .collect::>() + .serialize(serializer) } } #[cfg(feature = "serialize")] -impl<'de, K: Key> Deserialize<'de> for RodeoResolver { +impl<'de, K: Key, V: ?Sized + Internable> Deserialize<'de> for RodeoResolver { #[cfg_attr(feature = "inline-more", inline)] fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { - let vector: Vec = Vec::deserialize(deserializer)?; + let vector: Vec> = Vec::deserialize(deserializer)?; let capacity = { let total_bytes = vector.iter().map(|s| s.len()).sum::(); let total_bytes = @@ -314,7 +314,7 @@ impl<'de, K: Key> Deserialize<'de> for RodeoResolver { for string in vector { let allocated = unsafe { arena - .store_str(&string) + .store_str(V::from_slice(&string)) .expect("failed to allocate enough memory") }; diff --git a/src/rodeo.rs b/src/rodeo.rs index ca80fcc..be222da 100644 --- a/src/rodeo.rs +++ b/src/rodeo.rs @@ -1,18 +1,11 @@ -use crate::{ - arenas::{AnyArena, Arena}, - hasher::RandomState, - keys::{Key, Spur}, - reader::RodeoReader, - resolver::RodeoResolver, - util::{Iter, Strings}, - Capacity, LassoError, LassoErrorKind, LassoResult, MemoryLimits, -}; +use crate::{arenas::{AnyArena, Arena}, hasher::RandomState, keys::{Key, Spur}, reader::RodeoReader, resolver::RodeoResolver, util::{Iter, Strings}, Capacity, LassoError, LassoErrorKind, LassoResult, MemoryLimits, Internable}; use alloc::vec::Vec; use core::{ - hash::{BuildHasher, Hash, Hasher}, + hash::{BuildHasher, Hasher}, iter::FromIterator, ops::Index, }; +use core::marker::PhantomData; use hashbrown::{hash_map::RawEntryMut, HashMap}; /// A string interner that caches strings quickly with a minimal memory footprint, @@ -23,8 +16,8 @@ use hashbrown::{hash_map::RawEntryMut, HashMap}; /// [`Spur`]: crate::Spur /// [`RandomState`]: https://doc.rust-lang.org/std/collections/hash_map/struct.RandomState.html #[derive(Debug)] -pub struct Rodeo { - /// Map that allows `str` -> `key` resolution +pub struct Rodeo { + /// Map that allows `V` -> `key` resolution /// /// This must be a `HashMap` (for now) since `raw_api`s are only available for maps and not sets. /// The value of the map is `()` since the key is symbolically hashed as the string it represents and @@ -42,15 +35,17 @@ pub struct Rodeo { /// The hasher of the map. This is stored outside of the map so that we can use /// custom hashing on the keys of the map without the map itself trying to do something else hasher: S, - /// Vec that allows `key` -> `str` resolution - pub(crate) strings: Vec<&'static str>, + /// Vec that allows `key` -> `V` resolution + pub(crate) strings: Vec<&'static V>, /// The arena that holds all allocated strings arena: Arena, + phantom: PhantomData, } -impl Rodeo +impl Rodeo where K: Key, + V: ?Sized + Internable, { /// Create a new Rodeo /// @@ -154,9 +149,10 @@ where } } -impl Rodeo +impl Rodeo where K: Key, + V: ?Sized + Internable, S: BuildHasher, { /// Creates an empty Rodeo which will use the given hasher for its internal hashmap @@ -167,7 +163,7 @@ where /// use lasso::{Spur, Rodeo}; /// use std::collections::hash_map::RandomState; /// - /// let rodeo: Rodeo = Rodeo::with_hasher(RandomState::new()); + /// let rodeo: Rodeo = Rodeo::with_hasher(RandomState::new()); /// ``` /// #[cfg_attr(feature = "inline-more", inline)] @@ -189,7 +185,7 @@ where /// use lasso::{Spur, Capacity, Rodeo}; /// use std::collections::hash_map::RandomState; /// - /// let rodeo: Rodeo = Rodeo::with_capacity_and_hasher(Capacity::for_strings(10), RandomState::new()); + /// let rodeo: Rodeo = Rodeo::with_capacity_and_hasher(Capacity::for_strings(10), RandomState::new()); /// ``` /// /// [`Capacity`]: crate::Capacity @@ -212,7 +208,7 @@ where /// use lasso::{Spur, Capacity, MemoryLimits, Rodeo}; /// use std::collections::hash_map::RandomState; /// - /// let rodeo: Rodeo = Rodeo::with_capacity_memory_limits_and_hasher( + /// let rodeo: Rodeo = Rodeo::with_capacity_memory_limits_and_hasher( /// Capacity::for_strings(10), /// MemoryLimits::for_memory_usage(4096), /// RandomState::new(), @@ -236,6 +232,7 @@ where strings: Vec::with_capacity(strings), arena: Arena::new(bytes, max_memory_usage) .expect("failed to allocate memory for interner"), + phantom: PhantomData, } } @@ -267,7 +264,7 @@ where #[cfg_attr(feature = "inline-more", inline)] pub fn get_or_intern(&mut self, val: T) -> K where - T: AsRef, + T: AsRef, { self.try_get_or_intern(val) .expect("Failed to get or intern string") @@ -294,16 +291,17 @@ where #[cfg_attr(feature = "inline-more", inline)] pub fn try_get_or_intern(&mut self, val: T) -> LassoResult where - T: AsRef, + T: AsRef, { let Self { map, hasher, strings, arena, + phantom: _, } = self; - let string_slice: &str = val.as_ref(); + let string_slice: &V = val.as_ref(); // Make a hash of the requested string let hash = { @@ -316,7 +314,7 @@ where // Get the map's entry that the string should occupy let entry = map.raw_entry_mut().from_hash(hash, |key| { // Safety: The index given by `key` will be in bounds of the strings vector - let key_string: &str = unsafe { index_unchecked!(strings, key.into_usize()) }; + let key_string: &V = unsafe { index_unchecked!(strings, key.into_usize()) }; // Compare the requested string against the key's string string_slice == key_string @@ -341,7 +339,7 @@ where // Insert the key with the hash of the string that it points to, reusing the hash we made earlier entry.insert_with_hasher(hash, key, (), |key| { - let key_string: &str = unsafe { index_unchecked!(strings, key.into_usize()) }; + let key_string: &V = unsafe { index_unchecked!(strings, key.into_usize()) }; let mut state = hasher.build_hasher(); key_string.hash(&mut state); @@ -383,7 +381,7 @@ where /// ``` /// #[cfg_attr(feature = "inline-more", inline)] - pub fn get_or_intern_static(&mut self, string: &'static str) -> K { + pub fn get_or_intern_static(&mut self, string: &'static V) -> K { self.try_get_or_intern_static(string) .expect("Failed to get or intern static string") } @@ -409,7 +407,7 @@ where /// ``` /// #[cfg_attr(feature = "inline-more", inline)] - pub fn try_get_or_intern_static(&mut self, string: &'static str) -> LassoResult { + pub fn try_get_or_intern_static(&mut self, string: &'static V) -> LassoResult { let Self { map, hasher, @@ -428,7 +426,7 @@ where // Get the map's entry that the string should occupy let entry = map.raw_entry_mut().from_hash(hash, |key| { // Safety: The index given by `key` will be in bounds of the strings vector - let key_string: &str = unsafe { index_unchecked!(strings, key.into_usize()) }; + let key_string: &V = unsafe { index_unchecked!(strings, key.into_usize()) }; // Compare the requested string against the key's string string == key_string @@ -449,7 +447,7 @@ where // Insert the key with the hash of the string that it points to, reusing the hash we made earlier entry.insert_with_hasher(hash, key, (), |key| { - let key_string: &str = unsafe { index_unchecked!(strings, key.into_usize()) }; + let key_string: &V = unsafe { index_unchecked!(strings, key.into_usize()) }; let mut state = hasher.build_hasher(); key_string.hash(&mut state); @@ -482,9 +480,9 @@ where #[cfg_attr(feature = "inline-more", inline)] pub fn get(&self, val: T) -> Option where - T: AsRef, + T: AsRef, { - let string_slice: &str = val.as_ref(); + let string_slice: &V = val.as_ref(); // Make a hash of the requested string let hash = { @@ -497,7 +495,7 @@ where // Get the map's entry that the string should occupy let entry = self.map.raw_entry().from_hash(hash, |key| { // Safety: The index given by `key` will be in bounds of the strings vector - let key_string: &str = unsafe { index_unchecked!(self.strings, key.into_usize()) }; + let key_string: &V = unsafe { index_unchecked!(self.strings, key.into_usize()) }; // Compare the requested string against the string_slice == key_string @@ -524,15 +522,16 @@ where #[cfg_attr(feature = "inline-more", inline)] pub fn contains(&self, val: T) -> bool where - T: AsRef, + T: AsRef, { self.get(val).is_some() } } -impl Rodeo +impl Rodeo where K: Key, + V: ?Sized + Internable, { /// Returns `true` if the given key exists in the current interner /// @@ -574,7 +573,7 @@ where /// ``` /// #[cfg_attr(feature = "inline-more", inline)] - pub fn resolve<'a>(&'a self, key: &K) -> &'a str { + pub fn resolve<'a>(&'a self, key: &K) -> &'a V { // Safety: The call to get_unchecked's safety relies on the Key::into_usize impl // being symmetric and the caller having not fabricated a key. If the impl is sound // and symmetric, then it will succeed, as the usize used to create it is a valid @@ -600,7 +599,7 @@ where /// ``` /// #[cfg_attr(feature = "inline-more", inline)] - pub fn try_resolve<'a>(&'a self, key: &K) -> Option<&'a str> { + pub fn try_resolve<'a>(&'a self, key: &K) -> Option<&'a V> { // Safety: The call to get_unchecked's safety relies on the Key::into_usize impl // being symmetric and the caller having not fabricated a key. If the impl is sound // and symmetric, then it will succeed, as the usize used to create it is a valid @@ -634,12 +633,12 @@ where /// ``` /// #[cfg_attr(feature = "inline-more", inline)] - pub unsafe fn resolve_unchecked<'a>(&'a self, key: &K) -> &'a str { + pub unsafe fn resolve_unchecked<'a>(&'a self, key: &K) -> &'a V { unsafe { self.strings.get_unchecked(key.into_usize()) } } } -impl Rodeo { +impl Rodeo { /// Gets the number of interned strings /// /// # Example @@ -694,13 +693,13 @@ impl Rodeo { /// Returns an iterator over the interned strings and their key values #[cfg_attr(feature = "inline-more", inline)] - pub fn iter(&self) -> Iter<'_, K> { + pub fn iter(&self) -> Iter<'_, K, V> { Iter::from_rodeo(self) } /// Returns an iterator over the interned strings #[cfg_attr(feature = "inline-more", inline)] - pub fn strings(&self) -> Strings<'_, K> { + pub fn strings(&self) -> Strings<'_, K, V> { Strings::from_rodeo(self) } @@ -726,7 +725,7 @@ impl Rodeo { } } -impl Rodeo { +impl Rodeo { /// Consumes the current Rodeo, returning a [`RodeoReader`] to allow contention-free access of the interner /// from multiple threads /// @@ -748,12 +747,13 @@ impl Rodeo { /// [`RodeoReader`]: crate::RodeoReader #[cfg_attr(feature = "inline-more", inline)] #[must_use] - pub fn into_reader(self) -> RodeoReader { + pub fn into_reader(self) -> RodeoReader { let Self { map, hasher, strings, arena, + phantom: _, } = self; // Safety: No other references outside of `map` and `strings` to the interned strings exist @@ -781,7 +781,7 @@ impl Rodeo { /// [`RodeoResolver`]: crate::RodeoResolver #[cfg_attr(feature = "inline-more", inline)] #[must_use] - pub fn into_resolver(self) -> RodeoResolver { + pub fn into_resolver(self) -> RodeoResolver { let Rodeo { strings, arena, .. } = self; // Safety: No other references to the strings exist @@ -793,19 +793,23 @@ impl Rodeo { /// /// [`Spur`]: crate::Spur /// [`RandomState`]: index.html#cargo-features -impl Default for Rodeo { +impl Default for Rodeo +// where +// V: ?Sized + Internable, +{ #[cfg_attr(feature = "inline-more", inline)] fn default() -> Self { Self::new() } } -unsafe impl Send for Rodeo {} +unsafe impl Send for Rodeo {} -impl FromIterator for Rodeo +impl FromIterator for Rodeo where - Str: AsRef, + Str: AsRef, K: Key, + V: ?Sized + Internable, S: BuildHasher + Default, { #[cfg_attr(feature = "inline-more", inline)] @@ -821,19 +825,20 @@ where ); for string in iter { - interner.get_or_intern(string.as_ref()); + interner.get_or_intern(string); } interner } } -impl Index for Rodeo +impl Index for Rodeo where K: Key, + V: ?Sized + Internable, S: BuildHasher, { - type Output = str; + type Output = V; #[cfg_attr(feature = "inline-more", inline)] fn index(&self, idx: K) -> &Self::Output { @@ -841,11 +846,12 @@ where } } -impl Extend for Rodeo +impl Extend for Rodeo where K: Key, + V: ?Sized + Internable, S: BuildHasher, - T: AsRef, + T: AsRef, { #[cfg_attr(feature = "inline-more", inline)] fn extend(&mut self, iter: I) @@ -853,14 +859,14 @@ where I: IntoIterator, { for s in iter { - self.get_or_intern(s.as_ref()); + self.get_or_intern(s); } } } -impl<'a, K: Key, S> IntoIterator for &'a Rodeo { - type Item = (K, &'a str); - type IntoIter = Iter<'a, K>; +impl<'a, K: Key, V: ?Sized + Internable, S> IntoIterator for &'a Rodeo { + type Item = (K, &'a V); + type IntoIter = Iter<'a, K, V>; #[cfg_attr(feature = "inline-more", inline)] fn into_iter(self) -> Self::IntoIter { @@ -868,32 +874,31 @@ impl<'a, K: Key, S> IntoIterator for &'a Rodeo { } } -impl Eq for Rodeo {} +impl Eq for Rodeo {} -impl PartialEq for Rodeo { +impl PartialEq for Rodeo { #[cfg_attr(feature = "inline-more", inline)] fn eq(&self, other: &Self) -> bool { self.strings == other.strings } } -impl PartialEq> for Rodeo { +impl PartialEq> for Rodeo { #[cfg_attr(feature = "inline-more", inline)] - fn eq(&self, other: &RodeoReader) -> bool { + fn eq(&self, other: &RodeoReader) -> bool { self.strings == other.strings } } -impl PartialEq> for Rodeo { +impl PartialEq> for Rodeo { #[cfg_attr(feature = "inline-more", inline)] - fn eq(&self, other: &RodeoResolver) -> bool { + fn eq(&self, other: &RodeoResolver) -> bool { self.strings == other.strings } } compile! { if #[feature = "serialize"] { - use alloc::string::String; use core::num::NonZeroUsize; use serde::{ de::{Deserialize, Deserializer}, @@ -903,25 +908,31 @@ compile! { } #[cfg(feature = "serialize")] -impl Serialize for Rodeo { +impl Serialize for Rodeo +where + V: ?Sized + Internable, +{ #[cfg_attr(feature = "inline-more", inline)] fn serialize(&self, serializer: S) -> Result where S: Serializer, { - // Serialize all of self as a `Vec` - self.strings.serialize(serializer) + // Serialize all of self as a `Vec>` + self.strings.iter() + .map(|v| v.as_bytes()) + .collect::>() + .serialize(serializer) } } #[cfg(feature = "serialize")] -impl<'de, K: Key, S: BuildHasher + Default> Deserialize<'de> for Rodeo { +impl<'de, K: Key, V: ?Sized + Internable, S: BuildHasher + Default> Deserialize<'de> for Rodeo { #[cfg_attr(feature = "inline-more", inline)] fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { - let vector: Vec = Vec::deserialize(deserializer)?; + let vector: Vec> = Vec::deserialize(deserializer)?; let capacity = { let total_bytes = vector.iter().map(|s| s.len()).sum::(); let total_bytes = @@ -939,7 +950,7 @@ impl<'de, K: Key, S: BuildHasher + Default> Deserialize<'de> for Rodeo { for (key, string) in vector.into_iter().enumerate() { let allocated = unsafe { arena - .store_str(&string) + .store_str(V::from_slice(&string)) .expect("failed to allocate enough memory") }; @@ -953,7 +964,7 @@ impl<'de, K: Key, S: BuildHasher + Default> Deserialize<'de> for Rodeo { // Get the map's entry that the string should occupy let entry = map.raw_entry_mut().from_hash(hash, |key: &K| { // Safety: The index given by `key` will be in bounds of the strings vector - let key_string: &str = unsafe { index_unchecked!(strings, key.into_usize()) }; + let key_string: &V = unsafe { index_unchecked!(strings, key.into_usize()) }; // Compare the requested string against the key's string allocated == key_string @@ -973,7 +984,7 @@ impl<'de, K: Key, S: BuildHasher + Default> Deserialize<'de> for Rodeo { // Insert the key with the hash of the string that it points to, reusing the hash we made earlier entry.insert_with_hasher(hash, key, (), |key| { - let key_string: &str = + let key_string: &V = unsafe { index_unchecked!(strings, key.into_usize()) }; let mut state = hasher.build_hasher(); @@ -990,6 +1001,7 @@ impl<'de, K: Key, S: BuildHasher + Default> Deserialize<'de> for Rodeo { hasher, strings, arena, + phantom: PhantomData, }) } } @@ -1032,13 +1044,13 @@ mod tests { #[test] fn with_hasher() { - let mut rodeo: Rodeo = Rodeo::with_hasher(RandomState::new()); + let mut rodeo: Rodeo = Rodeo::with_hasher(RandomState::new()); let key = rodeo.get_or_intern("Test"); assert_eq!("Test", rodeo.resolve(&key)); #[cfg(not(miri))] { - let mut rodeo: Rodeo = + let mut rodeo: Rodeo = Rodeo::with_hasher(ahash::RandomState::new()); let key = rodeo.get_or_intern("Test"); assert_eq!("Test", rodeo.resolve(&key)); @@ -1047,7 +1059,7 @@ mod tests { #[test] fn with_capacity_and_hasher() { - let mut rodeo: Rodeo = + let mut rodeo: Rodeo = Rodeo::with_capacity_and_hasher(Capacity::for_strings(10), RandomState::new()); assert_eq!(rodeo.capacity(), 10); @@ -1066,7 +1078,7 @@ mod tests { #[cfg(not(miri))] { - let mut rodeo: Rodeo = Rodeo::with_capacity_and_hasher( + let mut rodeo: Rodeo = Rodeo::with_capacity_and_hasher( Capacity::for_strings(10), ahash::RandomState::new(), ); @@ -1373,7 +1385,7 @@ mod tests { #[test] fn with_capacity_memory_limits_and_hasher() { - let mut rodeo: Rodeo = Rodeo::with_capacity_memory_limits_and_hasher( + let mut rodeo: Rodeo = Rodeo::with_capacity_memory_limits_and_hasher( Capacity::default(), MemoryLimits::default(), RandomState::new(), diff --git a/src/threaded_rodeo.rs b/src/threaded_rodeo.rs index bdcdbf7..7778bde 100644 --- a/src/threaded_rodeo.rs +++ b/src/threaded_rodeo.rs @@ -1,11 +1,4 @@ -use crate::{ - arenas::{AnyArena, LockfreeArena}, - hasher::RandomState, - keys::{Key, Spur}, - reader::RodeoReader, - resolver::RodeoResolver, - Capacity, LassoError, LassoErrorKind, LassoResult, MemoryLimits, Rodeo, -}; +use crate::{arenas::{AnyArena, LockfreeArena}, hasher::RandomState, keys::{Key, Spur}, reader::RodeoReader, resolver::RodeoResolver, Capacity, LassoError, LassoErrorKind, LassoResult, MemoryLimits, Rodeo, Internable}; use core::{ fmt::{Debug, Formatter, Result as FmtResult}, hash::{BuildHasher, Hash, Hasher}, @@ -37,14 +30,14 @@ macro_rules! index_unchecked_mut { /// [`Spur`]: crate::Spur /// [`ahash::RandomState`]: https://docs.rs/ahash/0.3.2/ahash/struct.RandomState.html /// [`RandomState`]: index.html#cargo-features -pub struct ThreadedRodeo { +pub struct ThreadedRodeo { // TODO: Should this be migrated over to the scheme that `Rodeo` uses for string storage? // Need benchmarks to see the perf impact of two dashmap lookups and see if that's worth // the storage impact of extra string pointers lying around /// Map that allows str to key resolution - map: DashMap<&'static str, K, S>, + map: DashMap<&'static V, K, S>, /// Map that allows key to str resolution - pub(crate) strings: DashMap, + pub(crate) strings: DashMap, /// The current key value key: AtomicUsize, /// The arena where all strings are stored @@ -53,9 +46,10 @@ pub struct ThreadedRodeo { // TODO: More parity functions with std::HashMap -impl ThreadedRodeo +impl ThreadedRodeo where K: Key + Hash, + V: ?Sized + Internable, { /// Create a new ThreadedRodeo /// @@ -172,9 +166,10 @@ where } } -impl ThreadedRodeo +impl ThreadedRodeo where K: Key + Hash, + V: ?Sized + Internable, S: BuildHasher + Clone, { /// Creates an empty ThreadedRodeo which will use the given hasher for its internal hashmap @@ -185,7 +180,7 @@ where /// use lasso::{Spur, ThreadedRodeo}; /// use std::collections::hash_map::RandomState; /// - /// let rodeo: ThreadedRodeo = ThreadedRodeo::with_hasher(RandomState::new()); + /// let rodeo: ThreadedRodeo = ThreadedRodeo::with_hasher(RandomState::new()); /// ``` /// #[cfg_attr(feature = "inline-more", inline)] @@ -207,7 +202,7 @@ where /// use lasso::{Spur, Capacity, ThreadedRodeo}; /// use std::collections::hash_map::RandomState; /// - /// let rodeo: ThreadedRodeo = ThreadedRodeo::with_capacity_and_hasher(Capacity::for_strings(10), RandomState::new()); + /// let rodeo: ThreadedRodeo = ThreadedRodeo::with_capacity_and_hasher(Capacity::for_strings(10), RandomState::new()); /// ``` /// /// [`Capacity`]: crate::Capacity @@ -230,7 +225,7 @@ where /// use lasso::{Spur, Capacity, MemoryLimits, ThreadedRodeo}; /// use std::collections::hash_map::RandomState; /// - /// let rodeo: ThreadedRodeo = ThreadedRodeo::with_capacity_memory_limits_and_hasher( + /// let rodeo: ThreadedRodeo = ThreadedRodeo::with_capacity_memory_limits_and_hasher( /// Capacity::for_strings(10), /// MemoryLimits::for_memory_usage(4096), /// RandomState::new(), @@ -284,7 +279,7 @@ where #[cfg_attr(feature = "inline-more", inline)] pub fn get_or_intern(&self, val: T) -> K where - T: AsRef, + T: AsRef, { self.try_get_or_intern(val) .expect("Failed to get or intern string") @@ -311,7 +306,7 @@ where #[cfg_attr(feature = "inline-more", inline)] pub fn try_get_or_intern(&self, val: T) -> LassoResult where - T: AsRef, + T: AsRef, { let string_slice = val.as_ref(); @@ -319,7 +314,7 @@ where Ok(*key) } else { // Safety: The drop impl removes all references before the arena is dropped - let string: &'static str = unsafe { self.arena.store_str(string_slice)? }; + let string: &'static V = unsafe { self.arena.store_str(string_slice)? }; let make_new_key = || { K::try_from_usize(self.key.fetch_add(1, Ordering::SeqCst)) @@ -364,7 +359,7 @@ where /// ``` /// #[cfg_attr(feature = "inline-more", inline)] - pub fn get_or_intern_static(&self, string: &'static str) -> K { + pub fn get_or_intern_static(&self, string: &'static V) -> K { self.try_get_or_intern_static(string) .expect("Failed to get or intern static string") } @@ -390,7 +385,7 @@ where /// ``` /// #[cfg_attr(feature = "inline-more", inline)] - pub fn try_get_or_intern_static(&self, string: &'static str) -> LassoResult { + pub fn try_get_or_intern_static(&self, string: &'static V) -> LassoResult { if let Some(key) = self.map.get(string) { Ok(*key) } else { @@ -428,7 +423,7 @@ where #[cfg_attr(feature = "inline-more", inline)] pub fn get(&self, val: T) -> Option where - T: AsRef, + T: AsRef, { self.map.get(val.as_ref()).map(|k| *k) } @@ -451,7 +446,7 @@ where #[cfg_attr(feature = "inline-more", inline)] pub fn contains(&self, val: T) -> bool where - T: AsRef, + T: AsRef, { self.get(val).is_some() } @@ -496,7 +491,7 @@ where /// ``` /// #[cfg_attr(feature = "inline-more", inline)] - pub fn resolve<'a>(&'a self, key: &K) -> &'a str { + pub fn resolve<'a>(&'a self, key: &K) -> &'a V { *self.strings.get(key).expect("Key out of bounds") } @@ -515,7 +510,7 @@ where /// ``` /// #[cfg_attr(feature = "inline-more", inline)] - pub fn try_resolve<'a>(&'a self, key: &K) -> Option<&'a str> { + pub fn try_resolve<'a>(&'a self, key: &K) -> Option<&'a V> { self.strings.get(key).map(|s| *s) } @@ -574,13 +569,13 @@ where /// Returns an iterator over the interned strings and their key values #[cfg_attr(feature = "inline-more", inline)] - pub fn iter(&self) -> Iter<'_, K, S> { + pub fn iter(&self) -> Iter<'_, K, V, S> { Iter::new(self) } /// Returns an iterator over the interned strings #[cfg_attr(feature = "inline-more", inline)] - pub fn strings(&self) -> Strings<'_, K, S> { + pub fn strings(&self) -> Strings<'_, K, V, S> { Strings::new(self) } @@ -627,12 +622,12 @@ where /// [`RodeoReader`]: crate::RodeoReader #[cfg_attr(feature = "inline-more", inline)] #[must_use] - pub fn into_reader(self) -> RodeoReader { + pub fn into_reader(self) -> RodeoReader { // Take the strings vec from the old lasso - let strings: Vec<&'static str> = { + let strings: Vec<&'static V> = { let mut strings = iter::from_fn(|| Some(None)) .take(self.strings.len()) - .collect::>>(); + .collect::>>(); for shard in self.strings.shards() { for (key, val) in shard.write().drain() { @@ -653,7 +648,7 @@ where for shard in self.map.shards() { for (string, key) in shard.write().drain() { - let string: &str = string; + let string: &V = string; // Hash the string to use as the key's hash (See `Rodeo`'s documentation for details) let hash = { @@ -666,7 +661,7 @@ where // Get the entry of the hashmap and insert the key with our new, custom hash let entry = map.raw_entry_mut().from_hash(hash, |key| { // Safety: The index given by `key` will be in bounds of the strings vector - let key_string: &str = + let key_string: &V = unsafe { index_unchecked!(strings, key.into_usize()) }; // Compare the requested string against the key's string @@ -681,7 +676,7 @@ where RawEntryMut::Vacant(entry) => { // Insert the key with the hash of the string that it points to, reusing the hash we made earlier entry.insert_with_hasher(hash, *key.get(), (), |key| { - let key_string: &str = + let key_string: &V = unsafe { index_unchecked!(strings, key.into_usize()) }; let mut state = hasher.build_hasher(); @@ -722,10 +717,10 @@ where /// [`RodeoResolver`]: crate::RodeoResolver #[cfg_attr(feature = "inline-more", inline)] #[must_use] - pub fn into_resolver(self) -> RodeoResolver { + pub fn into_resolver(self) -> RodeoResolver { let mut strings = iter::from_fn(|| Some(None)) .take(self.strings.len()) - .collect::>>(); + .collect::>>(); for shard in self.strings.shards() { for (key, val) in shard.write().drain() { @@ -750,16 +745,17 @@ where /// /// [`Spur`]: crate::Spur /// [`RandomState`]: index.html#cargo-features -impl Default for ThreadedRodeo { +impl Default for ThreadedRodeo { #[cfg_attr(feature = "inline-more", inline)] fn default() -> Self { Self::new() } } -impl Debug for ThreadedRodeo +impl Debug for ThreadedRodeo where K: Key + Hash + Debug, + V: ?Sized + Internable + Debug, S: BuildHasher + Clone, { #[cfg_attr(feature = "inline-more", inline)] @@ -772,13 +768,14 @@ where } } -unsafe impl Sync for ThreadedRodeo {} -unsafe impl Send for ThreadedRodeo {} +unsafe impl Sync for ThreadedRodeo {} +unsafe impl Send for ThreadedRodeo {} -impl FromIterator for ThreadedRodeo +impl FromIterator for ThreadedRodeo where - Str: AsRef, + Str: AsRef, K: Key + Hash, + V: ?Sized + Internable, S: BuildHasher + Clone + Default, { #[cfg_attr(feature = "inline-more", inline)] @@ -801,12 +798,13 @@ where } } -impl Index for ThreadedRodeo +impl Index for ThreadedRodeo where K: Key + Hash, + V: ?Sized + Internable, S: BuildHasher + Clone, { - type Output = str; + type Output = V; #[cfg_attr(feature = "inline-more", inline)] fn index(&self, idx: K) -> &Self::Output { @@ -814,11 +812,12 @@ where } } -impl Extend for ThreadedRodeo +impl Extend for ThreadedRodeo where K: Key + Hash, + V: ?Sized + Internable, S: BuildHasher + Clone, - T: AsRef, + T: AsRef, { #[cfg_attr(feature = "inline-more", inline)] fn extend(&mut self, iter: I) @@ -831,16 +830,18 @@ where } } -impl Eq for ThreadedRodeo +impl Eq for ThreadedRodeo where K: Eq + Hash, + V: ?Sized + Internable, S: Clone + BuildHasher, { } -impl PartialEq for ThreadedRodeo +impl PartialEq for ThreadedRodeo where K: Eq + Hash, + V: ?Sized + Internable, S: Clone + BuildHasher, { #[cfg_attr(feature = "inline-more", inline)] @@ -856,13 +857,14 @@ where } } -impl PartialEq> for ThreadedRodeo +impl PartialEq> for ThreadedRodeo where K: Eq + Hash + Key, + V: ?Sized + Internable, S: Clone + BuildHasher, { #[cfg_attr(feature = "inline-more", inline)] - fn eq(&self, other: &Rodeo) -> bool { + fn eq(&self, other: &Rodeo) -> bool { self.strings.len() == other.strings.len() && other.strings.iter().enumerate().all(|(key, string)| { K::try_from_usize(key) @@ -873,13 +875,14 @@ where } } -impl PartialEq> for ThreadedRodeo +impl PartialEq> for ThreadedRodeo where K: Eq + Hash + Key, + V: ?Sized + Internable, S: Clone + BuildHasher, { #[cfg_attr(feature = "inline-more", inline)] - fn eq(&self, other: &RodeoReader) -> bool { + fn eq(&self, other: &RodeoReader) -> bool { self.strings.len() == other.strings.len() && other.strings.iter().enumerate().all(|(key, string)| { K::try_from_usize(key) @@ -890,13 +893,14 @@ where } } -impl PartialEq> for ThreadedRodeo +impl PartialEq> for ThreadedRodeo where K: Eq + Hash + Key, + V: ?Sized + Internable, S: Clone + BuildHasher, { #[cfg_attr(feature = "inline-more", inline)] - fn eq(&self, other: &RodeoResolver) -> bool { + fn eq(&self, other: &RodeoResolver) -> bool { self.strings.len() == other.strings.len() && other.strings.iter().enumerate().all(|(key, string)| { K::try_from_usize(key) @@ -909,7 +913,6 @@ where compile! { if #[feature = "serialize"] { - use alloc::string::String; use core::num::NonZeroUsize; use serde::{ de::{Deserialize, Deserializer}, @@ -919,9 +922,10 @@ compile! { } #[cfg(feature = "serialize")] -impl Serialize for ThreadedRodeo +impl Serialize for ThreadedRodeo where K: Copy + Eq + Hash + Serialize, + V: ?Sized + Internable, H: Clone + BuildHasher, { #[cfg_attr(feature = "inline-more", inline)] @@ -929,10 +933,13 @@ where where S: Serializer, { - // Serialize all of self as a `HashMap` + // Serialize all of self as a `HashMap>` let mut map = HashMap::with_capacity(self.map.len()); for entry in self.map.iter() { - map.insert(*entry.key(), entry.value().to_owned()); + map.insert( + entry.value().to_owned(), + entry.key().as_bytes(), + ); } map.serialize(serializer) @@ -940,9 +947,10 @@ where } #[cfg(feature = "serialize")] -impl<'de, K, S> Deserialize<'de> for ThreadedRodeo +impl<'de, K, V, S> Deserialize<'de> for ThreadedRodeo where K: Key + Eq + Hash + Deserialize<'de>, + V: ?Sized + Internable, S: BuildHasher + Clone + Default, { #[cfg_attr(feature = "inline-more", inline)] @@ -950,9 +958,10 @@ where where D: Deserializer<'de>, { - let deser_map: HashMap = HashMap::deserialize(deserializer)?; + let deser_map: HashMap> = HashMap::deserialize(deserializer)?; + let capacity = { - let total_bytes = deser_map.keys().map(|s| s.len()).sum::(); + let total_bytes = deser_map.values().map(|s| s.len()).sum::(); let total_bytes = NonZeroUsize::new(total_bytes).unwrap_or_else(|| Capacity::default().bytes()); @@ -966,14 +975,14 @@ where let arena = LockfreeArena::new(capacity.bytes, usize::max_value()) .expect("failed to allocate memory for interner"); - for (string, key) in deser_map { + for (key, string) in deser_map { if key.into_usize() > highest { highest = key.into_usize(); } let allocated = unsafe { arena - .store_str(&string) + .store_str(V::from_slice(&string)) .expect("failed to allocate enough memory") }; @@ -992,29 +1001,31 @@ where /// An iterator over an interner's strings and keys #[must_use = "iterators are lazy and do nothing unless consumed"] -pub struct Iter<'a, K, S> { - iter: dashmap::iter::Iter<'a, K, &'static str, S, DashMap>, +pub struct Iter<'a, K, V: ?Sized + 'static, S> { + iter: dashmap::iter::Iter<'a, K, &'static V, S, DashMap>, } -impl<'a, K, S> Iter<'a, K, S> +impl<'a, K, V, S> Iter<'a, K, V, S> where K: Key + Hash, + V: ?Sized + Internable, S: BuildHasher + Clone, { #[cfg_attr(feature = "inline-more", inline)] - pub(crate) fn new(rodeo: &'a ThreadedRodeo) -> Self { + pub(crate) fn new(rodeo: &'a ThreadedRodeo) -> Self { Self { iter: rodeo.strings.iter(), } } } -impl<'a, K, S> Iterator for Iter<'a, K, S> +impl<'a, K, V, S> Iterator for Iter<'a, K, V, S> where K: Key + Hash, + V: ?Sized + Internable, S: BuildHasher + Clone, { - type Item = (K, &'a str); + type Item = (K, &'a V); #[cfg_attr(feature = "inline-more", inline)] fn next(&mut self) -> Option { @@ -1027,7 +1038,7 @@ where } } -impl Debug for Iter<'_, K, S> { +impl Debug for Iter<'_, K, V, S> { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { f.debug_struct("Iter").finish_non_exhaustive() } @@ -1036,29 +1047,31 @@ impl Debug for Iter<'_, K, S> { /// An iterator over an interner's strings #[derive(Debug)] #[must_use = "iterators are lazy and do nothing unless consumed"] -pub struct Strings<'a, K, S> { - iter: Iter<'a, K, S>, +pub struct Strings<'a, K, V: ?Sized + 'static, S> { + iter: Iter<'a, K, V, S>, } -impl<'a, K, S> Strings<'a, K, S> +impl<'a, K, V, S> Strings<'a, K, V, S> where K: Key + Hash, + V: ?Sized + Internable, S: BuildHasher + Clone, { #[cfg_attr(feature = "inline-more", inline)] - pub(crate) fn new(rodeo: &'a ThreadedRodeo) -> Self { + pub(crate) fn new(rodeo: &'a ThreadedRodeo) -> Self { Self { iter: Iter::new(rodeo), } } } -impl<'a, K, S> Iterator for Strings<'a, K, S> +impl<'a, K, V, S> Iterator for Strings<'a, K, V, S> where K: Key + Hash, + V: ?Sized + Internable, S: BuildHasher + Clone, { - type Item = &'a str; + type Item = &'a V; #[cfg_attr(feature = "inline-more", inline)] fn next(&mut self) -> Option { @@ -1105,7 +1118,7 @@ mod tests { #[test] fn with_hasher() { - let rodeo: ThreadedRodeo = + let rodeo: ThreadedRodeo = ThreadedRodeo::with_hasher(RandomState::new()); let key = rodeo.get_or_intern("Test"); @@ -1114,7 +1127,7 @@ mod tests { #[test] fn with_capacity_and_hasher() { - let rodeo: ThreadedRodeo = + let rodeo: ThreadedRodeo = ThreadedRodeo::with_capacity_and_hasher(Capacity::for_strings(10), RandomState::new()); let key = rodeo.get_or_intern("Test"); @@ -1547,7 +1560,7 @@ mod tests { #[test] fn with_capacity_memory_limits_and_hasher() { - let rodeo: ThreadedRodeo = + let rodeo: ThreadedRodeo = ThreadedRodeo::with_capacity_memory_limits_and_hasher( Capacity::default(), MemoryLimits::default(), diff --git a/src/util.rs b/src/util.rs index 9e49fcb..dac7739 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,5 +1,8 @@ use crate::{keys::Key, reader::RodeoReader, resolver::RodeoResolver, rodeo::Rodeo}; use core::{fmt, iter, marker::PhantomData, num::NonZeroUsize, slice}; +use core::hash::Hash; +use std::ffi::OsStr; +use std::path::Path; /// A continence type for an error from an interner pub type LassoResult = core::result::Result; @@ -196,14 +199,14 @@ impl Default for MemoryLimits { /// An iterator over an interner's strings and keys #[derive(Clone, Debug)] #[must_use = "iterators are lazy and do nothing unless consumed"] -pub struct Iter<'a, K> { - iter: iter::Enumerate>, +pub struct Iter<'a, K, V: ?Sized> { + iter: iter::Enumerate>, __key: PhantomData, } -impl<'a, K> Iter<'a, K> { +impl<'a, K, V: ?Sized> Iter<'a, K, V> { #[cfg_attr(feature = "inline-more", inline)] - pub(crate) fn from_rodeo(rodeo: &'a Rodeo) -> Self { + pub(crate) fn from_rodeo(rodeo: &'a Rodeo) -> Self { Self { iter: rodeo.strings.iter().enumerate(), __key: PhantomData, @@ -211,7 +214,7 @@ impl<'a, K> Iter<'a, K> { } #[cfg_attr(feature = "inline-more", inline)] - pub(crate) fn from_reader(rodeo: &'a RodeoReader) -> Self { + pub(crate) fn from_reader(rodeo: &'a RodeoReader) -> Self { Self { iter: rodeo.strings.iter().enumerate(), __key: PhantomData, @@ -219,7 +222,7 @@ impl<'a, K> Iter<'a, K> { } #[cfg_attr(feature = "inline-more", inline)] - pub(crate) fn from_resolver(rodeo: &'a RodeoResolver) -> Self { + pub(crate) fn from_resolver(rodeo: &'a RodeoResolver) -> Self { Self { iter: rodeo.strings.iter().enumerate(), __key: PhantomData, @@ -227,9 +230,10 @@ impl<'a, K> Iter<'a, K> { } } -fn iter_element<'a, K>((key, string): (usize, &&'a str)) -> (K, &'a str) +fn iter_element<'a, K, V>((key, string): (usize, &&'a V)) -> (K, &'a V) where K: Key, + V: ?Sized + Internable, { ( K::try_from_usize(key).unwrap_or_else(|| unreachable!()), @@ -237,11 +241,12 @@ where ) } -impl<'a, K> Iterator for Iter<'a, K> +impl<'a, K, V> Iterator for Iter<'a, K, V> where K: Key, + V: ?Sized + Internable, { - type Item = (K, &'a str); + type Item = (K, &'a V); #[cfg_attr(feature = "inline-more", inline)] fn next(&mut self) -> Option { @@ -254,26 +259,27 @@ where } } -impl<'a, K> DoubleEndedIterator for Iter<'a, K> +impl<'a, K, V> DoubleEndedIterator for Iter<'a, K, V> where K: Key, + V: ?Sized + Internable, { #[cfg_attr(feature = "inline-more", inline)] - fn next_back(&mut self) -> Option<(K, &'a str)> { + fn next_back(&mut self) -> Option<(K, &'a V)> { self.iter.next_back().map(iter_element) } #[cfg_attr(feature = "inline-more", inline)] - fn nth_back(&mut self, n: usize) -> Option<(K, &'a str)> { + fn nth_back(&mut self, n: usize) -> Option<(K, &'a V)> { self.iter.nth_back(n).map(iter_element) } } // iter::Enumerate is exact-size if its underlying iterator is exact-size, which slice::Iter is. -impl<'a, K: Key> ExactSizeIterator for Iter<'a, K> {} +impl<'a, K: Key, V: ?Sized + Internable> ExactSizeIterator for Iter<'a, K, V> {} // iter::Enumerate is fused if its underlying iterator is fused, which slice::Iter is. -impl<'a, K: Key> iter::FusedIterator for Iter<'a, K> {} +impl<'a, K: Key, V: ?Sized + Internable> iter::FusedIterator for Iter<'a, K, V> {} // #[derive(Debug)] // pub struct LockedIter<'a, K: Key> { @@ -300,14 +306,14 @@ impl<'a, K: Key> iter::FusedIterator for Iter<'a, K> {} /// An iterator over an interner's strings #[derive(Clone, Debug)] #[must_use = "iterators are lazy and do nothing unless consumed"] -pub struct Strings<'a, K> { - iter: slice::Iter<'a, &'a str>, +pub struct Strings<'a, K, V: ?Sized> { + iter: slice::Iter<'a, &'a V>, __key: PhantomData, } -impl<'a, K> Strings<'a, K> { +impl<'a, K, V: ?Sized> Strings<'a, K, V> { #[cfg_attr(feature = "inline-more", inline)] - pub(crate) fn from_rodeo(rodeo: &'a Rodeo) -> Self { + pub(crate) fn from_rodeo(rodeo: &'a Rodeo) -> Self { Self { iter: rodeo.strings.iter(), __key: PhantomData, @@ -315,7 +321,7 @@ impl<'a, K> Strings<'a, K> { } #[cfg_attr(feature = "inline-more", inline)] - pub(crate) fn from_reader(rodeo: &'a RodeoReader) -> Self { + pub(crate) fn from_reader(rodeo: &'a RodeoReader) -> Self { Self { iter: rodeo.strings.iter(), __key: PhantomData, @@ -323,7 +329,7 @@ impl<'a, K> Strings<'a, K> { } #[cfg_attr(feature = "inline-more", inline)] - pub(crate) fn from_resolver(rodeo: &'a RodeoResolver) -> Self { + pub(crate) fn from_resolver(rodeo: &'a RodeoResolver) -> Self { Self { iter: rodeo.strings.iter(), __key: PhantomData, @@ -331,8 +337,8 @@ impl<'a, K> Strings<'a, K> { } } -impl<'a, K> Iterator for Strings<'a, K> { - type Item = &'a str; +impl<'a, K, V: ?Sized> Iterator for Strings<'a, K, V> { + type Item = &'a V; #[cfg_attr(feature = "inline-more", inline)] fn next(&mut self) -> Option { @@ -345,26 +351,120 @@ impl<'a, K> Iterator for Strings<'a, K> { } } -impl<'a, K> DoubleEndedIterator for Strings<'a, K> -where - K: Key, -{ +impl<'a, K, V: ?Sized> DoubleEndedIterator for Strings<'a, K, V> { #[cfg_attr(feature = "inline-more", inline)] - fn next_back(&mut self) -> Option<&'a str> { + fn next_back(&mut self) -> Option<&'a V> { self.iter.next_back().copied() } #[cfg_attr(feature = "inline-more", inline)] - fn nth_back(&mut self, n: usize) -> Option<&'a str> { + fn nth_back(&mut self, n: usize) -> Option<&'a V> { self.iter.nth_back(n).copied() } } // slice::Iter is exact-size. -impl<'a, K> ExactSizeIterator for Strings<'a, K> {} +impl<'a, K, V: ?Sized> ExactSizeIterator for Strings<'a, K, V> {} // slice::Iter is fused. -impl<'a, K: Key> iter::FusedIterator for Strings<'a, K> {} +impl<'a, K, V: ?Sized> iter::FusedIterator for Strings<'a, K, V> {} + +/// Trait for types that can be interned within a `Rodeo` +pub trait Internable: Hash + Eq + AsRef { + /// A reference to an empty instance of this type + fn empty() -> &'static Self; + + /// Create this type from a byte-slice stored in a `Rodeo` + /// + /// # Safety + /// + /// This should be used only with byte arrays returned from `Internable::as_bytes`, to + /// ensure the provided bytes match the validity requirements of the implementor. + unsafe fn from_slice(slice: &[u8]) -> &Self; + + /// Check whether an instance to intern is empty + fn is_empty(&self) -> bool; + + /// Convert this type into bytes to store in the `Rodeo` + fn as_bytes(&self) -> &[u8]; +} + +impl Internable for [u8] { + fn empty() -> &'static Self { + &[] + } + + unsafe fn from_slice(slice: &[u8]) -> &Self { + slice + } + + fn is_empty(&self) -> bool { + self.is_empty() + } + + fn as_bytes(&self) -> &[u8] { + self + } +} + +impl Internable for str { + fn empty() -> &'static Self { + "" + } + + unsafe fn from_slice(slice: &[u8]) -> &Self { + // SAFETY: The source string was valid utf8, so the created buffer will be as well + unsafe { core::str::from_utf8_unchecked(slice) } + } + + fn is_empty(&self) -> bool { + self.is_empty() + } + + fn as_bytes(&self) -> &[u8] { + self.as_bytes() + } +} + +impl Internable for Path { + fn empty() -> &'static Self { + Path::new("") + } + + unsafe fn from_slice(slice: &[u8]) -> &Self { + // SAFETY: The source buffer was created from a call to `OsStr::as_bytes` + unsafe { Path::new(OsStr::from_slice(slice)) } + } + + fn is_empty(&self) -> bool { + self.as_os_str().is_empty() + } + + fn as_bytes(&self) -> &[u8] { + self.as_os_str().as_bytes() + } +} + +impl Internable for OsStr { + fn empty() -> &'static Self { + OsStr::new("") + } + + unsafe fn from_slice(slice: &[u8]) -> &Self { + // SAFETY: The source string was valid utf8, so the created buffer will be as well + unsafe { OsStr::new(core::str::from_utf8_unchecked(slice)) } + } + + fn is_empty(&self) -> bool { + self.is_empty() + } + + fn as_bytes(&self) -> &[u8] { + self.to_str() + .unwrap() + .as_bytes() + } +} macro_rules! compile { ($(