diff --git a/CHANGELOG.md b/CHANGELOG.md index 3aedcf6..2f3a6f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased + * `&I` and `&mut I` will now implement `Resolver` if `I` implements `Resolver`. + * `&mut I` will now implement `Interner` if `I` implements `Interner`. + * Added an implementation for `Arc` to implement `Resolver` and `Interner` so an `Arc` may be used alternatively to a reference to share access to the interner. + ## `v0.12.2` * `Checkpoint`s for the `GreenNodeBuilder` can now be used across node boundaries, meaning you can use them to wrap (finished) nodes in addition to just tokens. diff --git a/cstree/src/interning.rs b/cstree/src/interning.rs index 538dbd7..1c588c1 100644 --- a/cstree/src/interning.rs +++ b/cstree/src/interning.rs @@ -69,24 +69,28 @@ shared access. With the `multi_threaded_interning` feature, you can get such an [`new_threaded_interner`]. The feature also enables support for `ThreadedRodeo`, the multi-threaded interner from `lasso`. -**You can pass a reference to that interner to anything that expects an [`Interner`]!** +**You can pass a reference or an Arc to that interner to anything that expects an [`Interner`]!** While the interning methods on [`Interner`] require a `&mut self` to also work for single-threaded interners, both -[`Resolver`] and [`Interner`] will be implemented for `&interner` if `interner` is multi-threaded: +[`Resolver`] and [`Interner`] will be implemented for `&interner` and `Arc::new(interner)` if `interner` is multi-threaded: ``` # use cstree::testing::*; # use cstree::interning::*; -let interner = new_threaded_interner(); -let mut builder: GreenNodeBuilder = - GreenNodeBuilder::from_interner(&interner); +# use std::sync::Arc; +let interner = Arc::new(new_threaded_interner()); +let mut builder: GreenNodeBuilder> = + GreenNodeBuilder::from_interner(Arc::clone(&interner)); + +// or: +// let interner = new_threaded_interner(); +// let mut builder: GreenNodeBuilder = +// GreenNodeBuilder::from_interner(&interner); + # builder.start_node(Root); # builder.token(Int, "42"); # builder.finish_node(); parse(&mut builder, "42"); let (tree, cache) = builder.finish(); -// Note that we get a cache and interner back, because we passed an "owned" reference to `from_interner` -let used_interner = cache.unwrap().into_interner().unwrap(); -assert_eq!(used_interner as *const _, &interner as *const _); let int = tree.children().next().unwrap(); assert_eq!(int.as_token().unwrap().text(&interner), Some("42")); ``` @@ -177,8 +181,9 @@ pub fn new_interner() -> TokenInterner { /// Constructs a new [`Interner`] that can be used across multiple threads. /// -/// Note that you can use `&MultiThreadTokenInterner` to access interning methods through a shared reference, as well as -/// construct new syntax trees. See [the module documentation](self) for more information and examples. +/// Note that you can use `&MultiThreadedTokenInterner` and `Arc` to access interning methods +/// through a shared reference, as well as construct new syntax trees. See [the module documentation](self) for more +/// information and examples. #[cfg(feature = "multi_threaded_interning")] #[cfg_attr(doc_cfg, doc(cfg(feature = "multi_threaded_interning")))] #[inline] diff --git a/cstree/src/interning/default_interner.rs b/cstree/src/interning/default_interner.rs index c6779bf..4f5d6e2 100644 --- a/cstree/src/interning/default_interner.rs +++ b/cstree/src/interning/default_interner.rs @@ -1,6 +1,7 @@ #![cfg(not(feature = "lasso_compat"))] use core::fmt; +use std::sync::Arc as StdArc; use indexmap::IndexSet; use rustc_hash::FxBuildHasher; @@ -43,6 +44,13 @@ impl Resolver for TokenInterner { } } +impl Resolver for StdArc { + fn try_resolve(&self, key: TokenKey) -> Option<&str> { + let index = key.into_u32() as usize; + self.id_set.get_index(index).map(String::as_str) + } +} + // `TokenKey` can represent `1` to `u32::MAX` (due to the `NonNull` niche), so `u32::MAX` elements. // Set indices start at 0, so everything shifts down by 1. const N_INDICES: usize = u32::MAX as usize; diff --git a/cstree/src/interning/lasso_compat/token_interner.rs b/cstree/src/interning/lasso_compat/token_interner.rs index b952a00..35b8e75 100644 --- a/cstree/src/interning/lasso_compat/token_interner.rs +++ b/cstree/src/interning/lasso_compat/token_interner.rs @@ -87,10 +87,12 @@ pub use multi_threaded::MultiThreadedTokenInterner; mod multi_threaded { use super::*; + use std::sync::Arc as StdArc; + /// A threadsafe [`Interner`] for deduplicating [`GreenToken`](crate::green::GreenToken) strings. /// - /// Note that [`Interner`] and [`Resolver`] are also implemented for `&MultiThreadTokenInterner` so you can pass - /// `&mut &interner` in shared contexts. + /// Note that [`Interner`] and [`Resolver`] are also implemented for `&MultiThreadTokenInterner` and + /// `Arc` so you can pass a mutable reference to either of these in shared contexts. #[cfg_attr(doc_cfg, doc(cfg(feature = "multi_threaded_interning")))] pub struct MultiThreadedTokenInterner { rodeo: ThreadedRodeo, @@ -114,6 +116,5 @@ mod multi_threaded { } impl_traits!(for MultiThreadedTokenInterner, if #[cfg(feature = "multi_threaded_interning")]); - - impl_traits!(for &MultiThreadedTokenInterner, if #[cfg(feature = "multi_threaded_interning")]); + impl_traits!(for StdArc, if #[cfg(feature = "multi_threaded_interning")]); } diff --git a/cstree/src/interning/lasso_compat/traits.rs b/cstree/src/interning/lasso_compat/traits.rs index 9a09af8..9217e75 100644 --- a/cstree/src/interning/lasso_compat/traits.rs +++ b/cstree/src/interning/lasso_compat/traits.rs @@ -119,23 +119,6 @@ mod multi_threaded { compat_interner!(ThreadedRodeo where K: Hash, S: Clone if #[cfg(feature = "multi_threaded_interning")]); - #[cfg_attr(doc_cfg, doc(cfg(feature = "multi_threaded_interning")))] - impl Resolver for &lasso::ThreadedRodeo - where - K: lasso::Key + Hash, - S: BuildHasher + Clone, - { - #[inline] - fn try_resolve(&self, key: TokenKey) -> Option<&str> { - as Resolver>::try_resolve(self, key) - } - - #[inline] - fn resolve(&self, key: TokenKey) -> &str { - as Resolver>::resolve(self, key) - } - } - #[cfg_attr(doc_cfg, doc(cfg(feature = "multi_threaded_interning")))] impl Interner for &lasso::ThreadedRodeo where diff --git a/cstree/src/interning/traits.rs b/cstree/src/interning/traits.rs index cf222e4..a4c2c8e 100644 --- a/cstree/src/interning/traits.rs +++ b/cstree/src/interning/traits.rs @@ -38,13 +38,33 @@ pub trait Resolver { } } +impl Resolver for &R { + fn try_resolve(&self, key: TokenKey) -> Option<&str> { + (**self).try_resolve(key) + } + + fn resolve(&self, key: TokenKey) -> &str { + (**self).resolve(key) + } +} + +impl Resolver for &mut R { + fn try_resolve(&self, key: TokenKey) -> Option<&str> { + (**self).try_resolve(key) + } + + fn resolve(&self, key: TokenKey) -> &str { + (**self).resolve(key) + } +} + /// A full interner, which can intern new strings returning intern keys and also resolve intern keys to the interned /// value. /// /// **Note:** Because single-threaded interners may require mutable access, the methods on this trait take `&mut self`. /// In order to use a multi- (or single)-threaded interner that allows access through a shared reference, it is -/// implemented for `&`[`MultiThreadedTokenInterner`](crate::interning::MultiThreadedTokenInterner), allowing it to be -/// used with a `&mut &MultiThreadTokenInterner`. +/// implemented for `&MultiThreadedTokenInterner` and `Arc`, allowing it +/// to be used with a `&mut &MultiThreadedTokenInterner` and `&mut Arc`. pub trait Interner: Resolver { /// Represents possible ways in which interning may fail. /// For example, this might be running out of fresh intern keys, or failure to allocate sufficient space for a new @@ -65,3 +85,15 @@ pub trait Interner: Resolver { .unwrap_or_else(|_| panic!("failed to intern `{text:?}`")) } } + +impl Interner for &mut I { + type Error = I::Error; + + fn try_get_or_intern(&mut self, text: &str) -> Result { + (**self).try_get_or_intern(text) + } + + fn get_or_intern(&mut self, text: &str) -> TokenKey { + (**self).get_or_intern(text) + } +}