mirror of
https://github.com/aphrody-labs/oxc.git
synced 2026-10-15 14:49:36 +00:00
feat(allocator): construct ArenaHashSet with any Default hasher (#26372)
This PR is inspired by #26333, addresses the repeated `with_hasher_in(IdentBuildHasher, allocator)` call, and removes the extra `'ident` lifetime. `ArenaHashMap::new_in` works with any `Default` hasher, so `ArenaIdentHashMap::new_in(allocator)` picks `IdentBuildHasher` automatically. `ArenaHashSet` only has `new_in`, `with_capacity_in` and `from_iter_in` for `FxBuildHasher`, so an `Ident`-keyed arena set has to call `with_hasher_in(IdentBuildHasher, allocator)` every time (see #26333). Mirror the map's `S: Default` impl on the set and add an `ArenaIdentHashSet<'alloc>` alias next to `ArenaIdentHashMap`. Nothing calls the `FxBuildHasher`-only constructors today, so no other code changes. The alias takes a single lifetime, same as `ArenaIdentHashMap`. A separate lifetime for the `Ident` keys is not needed, and dropping it is sound: both `ArenaHashSet` and `Ident` are covariant, so when the idents are borrowed from somewhere other than the arena (for example `Scoping` in the mangler), the borrow checker shrinks `'alloc` to the shorter of the two borrows. The set can never outlive the strings its idents point to.
This commit is contained in:
1 parent
f7acdc0bd9
commit
32bdc5b1d1
3 files changed
+29
-6
No files matched your search
@@ -119,15 +119,17 @@ impl<'alloc, T, S> HashSet<'alloc, T, S> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Methods that use the default [`FxBuildHasher`].
|
||||
impl<'alloc, T> HashSet<'alloc, T> {
|
||||
/// Methods for any hasher that implements [`Default`].
|
||||
///
|
||||
/// This includes [`FxBuildHasher`] and any custom hasher (e.g. `IdentBuildHasher`).
|
||||
impl<'alloc, T, S: Default> HashSet<'alloc, T, S> {
|
||||
/// Creates an empty [`HashSet`]. It will be allocated with the given allocator.
|
||||
///
|
||||
/// The hash set is initially created with a capacity of 0, so it will not allocate
|
||||
/// until it is first inserted into.
|
||||
#[inline(always)]
|
||||
pub fn new_in(allocator: &'alloc Allocator) -> Self {
|
||||
Self::with_hasher_in(FxBuildHasher, allocator)
|
||||
Self::with_hasher_in(S::default(), allocator)
|
||||
}
|
||||
|
||||
/// Creates an empty [`HashSet`] with the specified capacity. It will be allocated with the given allocator.
|
||||
@@ -136,7 +138,7 @@ impl<'alloc, T> HashSet<'alloc, T> {
|
||||
/// If capacity is 0, the hash set will not allocate.
|
||||
#[inline(always)]
|
||||
pub fn with_capacity_in(capacity: usize, allocator: &'alloc Allocator) -> Self {
|
||||
Self::with_capacity_and_hasher_in(capacity, FxBuildHasher, allocator)
|
||||
Self::with_capacity_and_hasher_in(capacity, S::default(), allocator)
|
||||
}
|
||||
|
||||
/// Create a new [`HashSet`] whose elements are taken from an iterator and allocated in the given `allocator`.
|
||||
@@ -146,6 +148,7 @@ impl<'alloc, T> HashSet<'alloc, T> {
|
||||
pub fn from_iter_in<I: IntoIterator<Item = T>>(iter: I, allocator: &'alloc Allocator) -> Self
|
||||
where
|
||||
T: Eq + Hash,
|
||||
S: BuildHasher,
|
||||
{
|
||||
const { Self::ASSERT_T_IS_NOT_DROP };
|
||||
|
||||
@@ -160,7 +163,7 @@ impl<'alloc, T> HashSet<'alloc, T> {
|
||||
// e.g. filter iterators.
|
||||
let capacity = iter.size_hint().0;
|
||||
let set =
|
||||
InnerHashSet::with_capacity_and_hasher_in(capacity, FxBuildHasher, allocator.arena());
|
||||
InnerHashSet::with_capacity_and_hasher_in(capacity, S::default(), allocator.arena());
|
||||
// Wrap in `ManuallyDrop` *before* calling `for_each`, so compiler doesn't insert unnecessary code
|
||||
// to drop the `FxHashSet` in case of a panic in iterator's `next` method
|
||||
let mut set = ManuallyDrop::new(set);
|
||||
|
||||
@@ -542,6 +542,10 @@ pub type ArenaIdentHashMap<'alloc, V> =
|
||||
/// Hash set of [`Ident`], using precomputed ident hash.
|
||||
pub type IdentHashSet<'a> = hashbrown::HashSet<Ident<'a>, IdentBuildHasher>;
|
||||
|
||||
/// Arena-allocated hash set of [`Ident`], using precomputed ident hash.
|
||||
pub type ArenaIdentHashSet<'alloc> =
|
||||
oxc_allocator::ArenaHashSet<'alloc, Ident<'alloc>, IdentBuildHasher>;
|
||||
|
||||
/// Creates an [`Ident<'static>`] for a string literal, evaluated at compile time.
|
||||
///
|
||||
/// ```
|
||||
@@ -852,4 +856,20 @@ mod test {
|
||||
assert_eq!(map.get("hello"), Some(&42));
|
||||
assert_eq!(map.get(&Ident::from("hello")), Some(&42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arena_ident_hashset() {
|
||||
let allocator = Allocator::new();
|
||||
let mut set = ArenaIdentHashSet::new_in(&allocator);
|
||||
set.insert(Ident::from_in("hello", &allocator));
|
||||
assert!(set.contains("hello"));
|
||||
assert!(set.contains(&Ident::from("hello")));
|
||||
assert!(!set.contains("world"));
|
||||
|
||||
let set =
|
||||
ArenaIdentHashSet::from_iter_in([Ident::from("foo"), Ident::from("bar")], &allocator);
|
||||
assert_eq!(set.len(), 2);
|
||||
assert!(set.contains("foo"));
|
||||
assert!(set.contains("bar"));
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ mod ident_hasher;
|
||||
mod str;
|
||||
|
||||
pub use compact_str::{CompactStr, MAX_INLINE_LEN};
|
||||
pub use ident::{ArenaIdentHashMap, Ident, IdentHashMap, IdentHashSet};
|
||||
pub use ident::{ArenaIdentHashMap, ArenaIdentHashSet, Ident, IdentHashMap, IdentHashSet};
|
||||
pub use ident_hasher::{IdentBuildHasher, IdentHasher};
|
||||
pub use str::{Str, Str as ArenaStr};
|
||||
|
||||
|
||||
Reference in new issue
Block a user