mirror of
https://github.com/RGBCube/cstree
synced 2025-07-27 00:57:44 +00:00
Implement Resolver and Interner for Arc<MultiThreadedTokenInterner> (#72)
* Implement traits for Arc<MultiThreadedTokenInterner> * implement Resolver for Arc<Resolver>, update docs
This commit is contained in:
parent
e90300f4fc
commit
fb8cc54104
6 changed files with 66 additions and 33 deletions
|
@ -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<MultiThreadedTokenInterner>` 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.
|
||||
|
|
|
@ -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<MySyntax, &MultiThreadedTokenInterner> =
|
||||
GreenNodeBuilder::from_interner(&interner);
|
||||
# use std::sync::Arc;
|
||||
let interner = Arc::new(new_threaded_interner());
|
||||
let mut builder: GreenNodeBuilder<MySyntax, Arc<MultiThreadedTokenInterner>> =
|
||||
GreenNodeBuilder::from_interner(Arc::clone(&interner));
|
||||
|
||||
// or:
|
||||
// let interner = new_threaded_interner();
|
||||
// let mut builder: GreenNodeBuilder<MySyntax, &MultiThreadedTokenInterner> =
|
||||
// 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<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.
|
||||
#[cfg(feature = "multi_threaded_interning")]
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "multi_threaded_interning")))]
|
||||
#[inline]
|
||||
|
|
|
@ -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<TokenKey> for TokenInterner {
|
|||
}
|
||||
}
|
||||
|
||||
impl Resolver<TokenKey> for StdArc<TokenInterner> {
|
||||
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;
|
||||
|
|
|
@ -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<MultiThreadTokenInterner>` 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<TokenKey, FxBuildHasher>,
|
||||
|
@ -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<MultiThreadedTokenInterner>, if #[cfg(feature = "multi_threaded_interning")]);
|
||||
}
|
||||
|
|
|
@ -119,23 +119,6 @@ mod multi_threaded {
|
|||
|
||||
compat_interner!(ThreadedRodeo<K, S> where K: Hash, S: Clone if #[cfg(feature = "multi_threaded_interning")]);
|
||||
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "multi_threaded_interning")))]
|
||||
impl<K, S> Resolver<TokenKey> for &lasso::ThreadedRodeo<K, S>
|
||||
where
|
||||
K: lasso::Key + Hash,
|
||||
S: BuildHasher + Clone,
|
||||
{
|
||||
#[inline]
|
||||
fn try_resolve(&self, key: TokenKey) -> Option<&str> {
|
||||
<lasso::ThreadedRodeo<K, S> as Resolver<TokenKey>>::try_resolve(self, key)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn resolve(&self, key: TokenKey) -> &str {
|
||||
<lasso::ThreadedRodeo<K, S> as Resolver<TokenKey>>::resolve(self, key)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "multi_threaded_interning")))]
|
||||
impl<K, S> Interner<TokenKey> for &lasso::ThreadedRodeo<K, S>
|
||||
where
|
||||
|
|
|
@ -38,13 +38,33 @@ pub trait Resolver<Key: InternKey = TokenKey> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: Resolver> 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<R: Resolver> 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<MultiThreadedTokenInterner>`, allowing it
|
||||
/// to be used with a `&mut &MultiThreadedTokenInterner` and `&mut Arc<MultiThreadTokenInterner>`.
|
||||
pub trait Interner<Key: InternKey = TokenKey>: Resolver<Key> {
|
||||
/// 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<Key: InternKey = TokenKey>: Resolver<Key> {
|
|||
.unwrap_or_else(|_| panic!("failed to intern `{text:?}`"))
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Interner> Interner for &mut I {
|
||||
type Error = I::Error;
|
||||
|
||||
fn try_get_or_intern(&mut self, text: &str) -> Result<TokenKey, Self::Error> {
|
||||
(**self).try_get_or_intern(text)
|
||||
}
|
||||
|
||||
fn get_or_intern(&mut self, text: &str) -> TokenKey {
|
||||
(**self).get_or_intern(text)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue