use bun_alloc::core_alloc::Allocator;
use core::hash::{Hash, Hasher};
use bun_alloc::core_alloc::Global;
use bun_alloc::{DefaultAlloc, HashbrownAllocator};
use core::marker::PhantomData;
use core::ops::{Deref, DerefMut};
use bun_alloc::AllocError;
#[inline]
pub fn hash_string(s: &[u8]) -> u32 {
bun_wyhash::hash(s) as u32 }
pub trait ArrayHashContext<K: ?Sized>: Default {
fn hash(&self, key: &K) -> u32;
fn eql(&self, a: &K, b: &K, b_index: usize) -> bool;
}
pub trait ArrayHashAdapter<Q: ?Sized, K> {
fn hash(&self, key: &Q) -> u32;
fn eql(&self, a: &Q, b: &K, b_index: usize) -> bool;
}
#[derive(Default, Clone, Copy)]
pub struct AutoContext;
impl<K: Hash + Eq + ?Sized> ArrayHashContext<K> for AutoContext {
#[inline]
fn hash(&self, key: &K) -> u32 {
use core::hash::Hasher;
let mut h = rustc_hash::FxHasher::default();
key.hash(&mut h);
h.finish() as u32 }
#[inline]
fn eql(&self, a: &K, b: &K, _b_index: usize) -> bool {
a == b
}
}
#[derive(Default, Clone, Copy)]
pub struct StringContext;
impl ArrayHashContext<[u8]> for StringContext {
#[inline]
fn hash(&self, key: &[u8]) -> u32 {
hash_string(key)
}
#[inline]
fn eql(&self, a: &[u8], b: &[u8], _b_index: usize) -> bool {
a == b
}
}
#[derive(Default, Clone, Copy)]
pub struct CaseInsensitiveAsciiStringContext;
impl CaseInsensitiveAsciiStringContext {
pub fn hash_bytes(s: &[u8]) -> u32 {
bun_wyhash::hash_ascii_lowercase(0, s) as u32 }
#[inline]
pub fn pre(input: &[u8]) -> CaseInsensitiveAsciiPrehashed<'_> {
CaseInsensitiveAsciiPrehashed {
value: Self::hash_bytes(input),
input,
}
}
}
pub struct CaseInsensitiveAsciiPrehashed<'a> {
pub value: u32,
pub input: &'a [u8],
}
impl<'a> CaseInsensitiveAsciiPrehashed<'a> {
#[inline]
pub fn hash(&self, s: &[u8]) -> u32 {
if core::ptr::eq(s.as_ptr(), self.input.as_ptr()) && s.len() == self.input.len() {
return self.value;
}
CaseInsensitiveAsciiStringContext::hash_bytes(s)
}
#[inline]
pub fn eql(&self, a: &[u8], b: &[u8]) -> bool {
bun_core::strings::eql_case_insensitive_ascii_check_length(a, b)
}
}
#[derive(Clone, Copy)]
pub struct BoxedSliceContext<C>(C);
impl<C: Default> Default for BoxedSliceContext<C> {
#[inline]
fn default() -> Self {
Self(C::default())
}
}
impl<C: ArrayHashContext<[u8]>, A: Allocator> ArrayHashContext<bun_alloc::core_alloc::AllocBox<[u8], A>>
for BoxedSliceContext<C>
{
#[inline]
fn hash(&self, key: &bun_alloc::core_alloc::AllocBox<[u8], A>) -> u32 {
self.0.hash(&**key)
}
#[inline]
fn eql(&self, a: &bun_alloc::core_alloc::AllocBox<[u8], A>, b: &bun_alloc::core_alloc::AllocBox<[u8], A>, b_index: usize) -> bool {
self.0.eql(&**a, &**b, b_index)
}
}
impl ArrayHashContext<[u8]> for CaseInsensitiveAsciiStringContext {
#[inline]
fn hash(&self, key: &[u8]) -> u32 {
Self::hash_bytes(key)
}
#[inline]
fn eql(&self, a: &[u8], b: &[u8], _b_index: usize) -> bool {
bun_core::strings::eql_case_insensitive_ascii_check_length(a, b)
}
}
pub struct GetOrPutResult<'a, K, V> {
pub found_existing: bool,
pub index: usize,
pub key_ptr: &'a mut K,
pub value_ptr: &'a mut V,
}
pub struct KV<K, V> {
pub key: K,
pub value: V,
}
pub struct Entry<'a, K, V> {
pub key_ptr: &'a mut K,
pub value_ptr: &'a mut V,
}
pub struct Iter<'a, K, V> {
keys: *mut K,
values: *mut V,
len: usize,
index: usize,
_marker: PhantomData<&'a mut [(K, V)]>,
}
impl<'a, K, V> Iter<'a, K, V> {
#[inline]
pub fn reset(&mut self) {
self.index = 0;
}
}
impl<'a, K, V> Iterator for Iter<'a, K, V> {
type Item = Entry<'a, K, V>;
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.len {
return None;
}
let i = self.index;
self.index += 1;
unsafe {
Some(Entry {
key_ptr: &mut *self.keys.add(i),
value_ptr: &mut *self.values.add(i),
})
}
}
}
pub trait ArrayHashMapExt {
type Key;
type Value;
type Iterator<'a>: Iterator<Item = Entry<'a, Self::Key, Self::Value>>
where
Self: 'a;
fn iterator(&mut self) -> Self::Iterator<'_>;
}
const INDEX_THRESHOLD: usize = 8;
#[inline(always)]
const fn spread_hash(h: u32) -> u64 {
let h = h as u64;
h | (h.wrapping_mul(0x9E37_79B9).wrapping_shl(32))
}
#[inline]
fn index_rehasher(hashes: &[u32]) -> impl Fn(&u32) -> u64 + '_ {
move |&j| spread_hash(hashes[j as usize])
}
#[inline(never)]
fn index_insert_unique<A: MapAllocator>(
index: &mut hashbrown::HashTable<u32, IndexAlloc<A>>,
hashes: &[u32],
i: u32,
h: u32,
) {
index.insert_unique(spread_hash(h), i, index_rehasher(hashes));
}
#[inline(never)]
fn index_reserve<A: MapAllocator>(
index: &mut hashbrown::HashTable<u32, IndexAlloc<A>>,
hashes: &[u32],
target: usize,
) {
let extra = target.saturating_sub(index.len());
if extra != 0 {
index.reserve(extra, index_rehasher(hashes));
}
}
#[cold]
#[inline(never)]
fn rebuild_index_from_hashes<A: MapAllocator>(
hashes: &[u32],
capacity: usize,
) -> bun_alloc::core_alloc::AllocBox<hashbrown::HashTable<u32, IndexAlloc<A>>, A> {
let mut table = hashbrown::HashTable::with_capacity_in(
capacity.max(hashes.len()),
IndexAlloc(A::default()),
);
for (i, &h) in hashes.iter().enumerate() {
table.insert_unique(spread_hash(h), i as u32, index_rehasher(hashes));
}
bun_alloc::core_alloc::AllocBox::new_in(table, A::default())
}
pub trait MapAllocator: Allocator + Clone + Default {}
impl<A: Allocator + Clone + Default> MapAllocator for A {}
#[derive(Clone, Copy, Default)]
struct IndexAlloc<A>(A);
unsafe impl<A: Allocator> allocator_api2::alloc::Allocator for IndexAlloc<A> {
#[inline]
fn allocate(
&self,
layout: core::alloc::Layout,
) -> Result<core::ptr::NonNull<[u8]>, allocator_api2::alloc::AllocError> {
self.0
.allocate(layout)
.map_err(|_| allocator_api2::alloc::AllocError)
}
#[inline]
unsafe fn deallocate(&self, ptr: core::ptr::NonNull<u8>, layout: core::alloc::Layout) {
unsafe { self.0.deallocate(ptr, layout) }
}
}
pub struct ArrayHashMap<K, V, C = AutoContext, A: MapAllocator = Global> {
keys: bun_alloc::core_alloc::AllocVec<K, A>,
values: bun_alloc::core_alloc::AllocVec<V, A>,
hashes: bun_alloc::core_alloc::AllocVec<u32, A>,
index: Option<bun_alloc::core_alloc::AllocBox<hashbrown::HashTable<u32, IndexAlloc<A>>, A>>,
ctx: C,
#[cfg(debug_assertions)]
pointer_stability: core::sync::atomic::AtomicBool,
}
impl<K, V, C: Default, A: MapAllocator> Default for ArrayHashMap<K, V, C, A> {
fn default() -> Self {
Self::new()
}
}
impl<K: Clone, V: Clone, C: Default, A: MapAllocator> ArrayHashMap<K, V, C, A> {
pub fn clone(&self) -> Result<Self, AllocError> {
Ok(Self {
keys: self.keys.clone(),
values: self.values.clone(),
hashes: self.hashes.clone(),
index: self.index.clone(),
ctx: C::default(),
#[cfg(debug_assertions)]
pointer_stability: core::sync::atomic::AtomicBool::new(false),
})
}
}
impl<K, V, C: Default, A: MapAllocator> ArrayHashMap<K, V, C, A> {
pub fn new() -> Self {
Self {
keys: bun_alloc::core_alloc::AllocVec::new_in(A::default()),
values: bun_alloc::core_alloc::AllocVec::new_in(A::default()),
hashes: bun_alloc::core_alloc::AllocVec::new_in(A::default()),
index: None,
ctx: C::default(),
#[cfg(debug_assertions)]
pointer_stability: core::sync::atomic::AtomicBool::new(false),
}
}
pub fn with_capacity(n: usize) -> Self {
let mut m = Self::new();
m.reserve(n);
m
}
}
impl<K, V, C, A: MapAllocator> ArrayHashMap<K, V, C, A> {
#[inline]
pub fn count(&self) -> usize {
self.keys.len()
}
#[inline]
pub fn len(&self) -> usize {
self.keys.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.keys.is_empty()
}
#[inline]
pub fn capacity(&self) -> usize {
self.keys.capacity()
}
pub fn pop(&mut self) -> Option<KV<K, V>> {
let key = self.keys.pop()?;
let value = self.values.pop().unwrap();
let h = self.hashes.pop().unwrap();
self.index_remove_tail(self.keys.len(), h);
Some(KV { key, value })
}
pub fn clear_and_free(&mut self) {
self.keys = bun_alloc::core_alloc::AllocVec::new_in(A::default());
self.values = bun_alloc::core_alloc::AllocVec::new_in(A::default());
self.hashes = bun_alloc::core_alloc::AllocVec::new_in(A::default());
self.index = None;
}
pub fn ensure_total_capacity(&mut self, n: usize) -> Result<(), AllocError> {
let need = n.saturating_sub(self.keys.len());
self.keys.reserve(need);
self.values.reserve(need);
self.hashes.reserve(need);
self.reserve_index_to_capacity();
Ok(())
}
pub unsafe fn set_entries_len(&mut self, n: usize) {
debug_assert!(n <= self.keys.capacity());
debug_assert!(n <= self.values.capacity());
debug_assert!(n <= self.hashes.capacity());
unsafe {
self.keys.set_len(n);
self.values.set_len(n);
self.hashes.set_len(n);
}
self.drop_index();
}
#[inline]
pub fn ensure_total_capacity_context<Ctx>(
&mut self,
n: usize,
_ctx: Ctx,
) -> Result<(), AllocError> {
self.ensure_total_capacity(n)
}
pub fn put_assume_capacity_context(
&mut self,
key: K,
value: V,
hash: impl Fn(&K) -> u32,
eql: impl Fn(&K, &K, usize) -> bool,
) {
let h = hash(&key);
if let Some(i) = self.find_hash(h, |k, idx| eql(&key, k, idx)) {
self.keys[i] = key;
self.values[i] = value;
return;
}
self.push_entry(key, value, h);
}
pub fn ensure_unused_capacity(&mut self, additional: usize) -> Result<(), AllocError> {
self.keys.reserve(additional);
self.values.reserve(additional);
self.hashes.reserve(additional);
self.reserve_index_to_capacity();
Ok(())
}
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.keys.reserve(additional);
self.values.reserve(additional);
self.hashes.reserve(additional);
self.reserve_index_to_capacity();
}
#[inline]
fn reserve_index_to_capacity(&mut self) {
let cap = self.keys.capacity();
if let Some(index) = self.index.as_deref_mut() {
index_reserve(index, &self.hashes, cap);
}
}
pub fn shrink_and_free(&mut self, new_len: usize) {
if self.index.is_some() {
for i in new_len..self.hashes.len() {
let h = self.hashes[i];
self.index_remove_tail(i, h);
}
}
self.keys.truncate(new_len);
self.values.truncate(new_len);
self.hashes.truncate(new_len);
self.keys.shrink_to_fit();
self.values.shrink_to_fit();
self.hashes.shrink_to_fit();
if self.keys.len() <= INDEX_THRESHOLD {
self.index = None;
}
}
#[inline]
pub fn lock_pointers(&self) {
#[cfg(debug_assertions)]
{
use core::sync::atomic::Ordering::Relaxed;
debug_assert!(
!self.pointer_stability.load(Relaxed),
"ArrayHashMap pointers already locked",
);
self.pointer_stability.store(true, Relaxed);
}
}
#[inline]
pub fn unlock_pointers(&self) {
#[cfg(debug_assertions)]
self.pointer_stability
.store(false, core::sync::atomic::Ordering::Relaxed);
}
#[inline]
pub fn keys(&self) -> &[K] {
&self.keys
}
#[inline]
pub fn keys_mut(&mut self) -> &mut [K] {
&mut self.keys
}
#[inline]
pub fn values(&self) -> &[V] {
&self.values
}
#[inline]
pub fn values_mut(&mut self) -> &mut [V] {
&mut self.values
}
pub fn iterator(&mut self) -> Iter<'_, K, V> {
Iter {
keys: self.keys.as_mut_ptr(),
values: self.values.as_mut_ptr(),
len: self.keys.len(),
index: 0,
_marker: PhantomData,
}
}
pub fn clear_retaining_capacity(&mut self) {
self.keys.clear();
self.values.clear();
self.hashes.clear();
self.index = None;
}
#[inline]
pub fn clear(&mut self) {
self.clear_retaining_capacity();
}
#[inline]
pub fn iter(&self) -> core::iter::Zip<core::slice::Iter<'_, K>, core::slice::Iter<'_, V>> {
self.keys.iter().zip(self.values.iter())
}
#[inline]
pub fn get_index_adapted_raw<F: Fn(&K, usize) -> bool>(&self, h: u32, eq: F) -> Option<usize> {
self.find_hash(h, eq)
}
#[inline]
fn find_hash<F: Fn(&K, usize) -> bool>(&self, h: u32, eq: F) -> Option<usize> {
if let Some(index) = self.index.as_deref() {
let hashes = self.hashes.as_ptr();
let keys = self.keys.as_ptr();
return index
.find(spread_hash(h), |&i| {
let i = i as usize;
unsafe { *hashes.add(i) == h && eq(&*keys.add(i), i) }
})
.map(|&i| i as usize);
}
for (i, &stored) in self.hashes.iter().enumerate() {
if stored == h && eq(&self.keys[i], i) {
return Some(i);
}
}
None
}
#[inline]
fn push_entry(&mut self, key: K, value: V, h: u32) -> usize {
let i = self.keys.len();
self.keys.push(key);
self.values.push(value);
self.hashes.push(h);
match self.index.as_deref_mut() {
Some(index) => index_insert_unique(index, &self.hashes, i as u32, h),
None if i >= INDEX_THRESHOLD => self.rebuild_index(),
None => {}
}
i
}
#[cold]
fn rebuild_index(&mut self) {
self.index = Some(rebuild_index_from_hashes(
&self.hashes,
self.keys.capacity(),
));
}
#[inline]
fn drop_index(&mut self) {
self.index = None;
}
#[inline]
fn index_remove_tail(&mut self, tail: usize, tail_hash: u32) {
let Some(index) = self.index.as_deref_mut() else {
return;
};
if let Ok(slot) = index.find_entry(spread_hash(tail_hash), |&i| i as usize == tail) {
slot.remove();
}
}
#[inline]
fn index_swap_remove(&mut self, removed: usize, removed_hash: u32) {
let Some(index) = self.index.as_deref_mut() else {
return;
};
if let Ok(slot) = index.find_entry(spread_hash(removed_hash), |&i| i as usize == removed) {
slot.remove();
}
let old_last = self.keys.len();
if old_last != removed {
let moved_hash = self.hashes[removed];
if let Some(slot) = index.find_mut(spread_hash(moved_hash), |&i| i as usize == old_last)
{
*slot = removed as u32;
}
}
}
pub fn sort(&mut self, mut less_than: impl FnMut(&[K], &[V], usize, usize) -> bool) {
let len = self.keys.len();
if len < 2 {
return;
}
let mut perm: Vec<usize> = (0..len).collect();
{
let keys = &self.keys[..];
let values = &self.values[..];
perm.sort_by(|&a, &b| {
if less_than(keys, values, a, b) {
core::cmp::Ordering::Less
} else if less_than(keys, values, b, a) {
core::cmp::Ordering::Greater
} else {
core::cmp::Ordering::Equal
}
});
}
let had_index = self.index.is_some();
self.drop_index();
let mut visited = vec![false; len];
for start in 0..len {
if visited[start] || perm[start] == start {
continue;
}
let mut i = start;
while !visited[i] {
visited[i] = true;
let j = perm[i];
if j == start {
break;
}
self.keys.swap(i, j);
self.values.swap(i, j);
self.hashes.swap(i, j);
i = j;
}
}
if had_index {
self.rebuild_index();
}
}
fn gop_at(&mut self, index: usize, found_existing: bool) -> GetOrPutResult<'_, K, V> {
let (key_ptr, value_ptr) = unsafe {
(
&mut *self.keys.as_mut_ptr().add(index),
&mut *self.values.as_mut_ptr().add(index),
)
};
GetOrPutResult {
found_existing,
index,
key_ptr,
value_ptr,
}
}
pub fn get_index_mut(&mut self, index: usize) -> Option<(&mut K, &mut V)> {
if index >= self.keys.len() {
return None;
}
Some((&mut self.keys[index], &mut self.values[index]))
}
pub fn swap_remove_at(&mut self, index: usize) -> (K, V) {
let k = self.keys.swap_remove(index);
let v = self.values.swap_remove(index);
let h = self.hashes.swap_remove(index);
self.index_swap_remove(index, h);
(k, v)
}
#[inline]
pub fn get_index_adapted<Q: ?Sized, Ad>(&self, key: &Q, adapter: &Ad) -> Option<usize>
where
Ad: ArrayHashAdapter<Q, K>,
{
let h = adapter.hash(key);
self.find_hash(h, |k, idx| adapter.eql(key, k, idx))
}
#[inline]
pub fn get_adapted<Q: ?Sized, Ad>(&self, key: &Q, adapter: &Ad) -> Option<&V>
where
Ad: ArrayHashAdapter<Q, K>,
{
self.get_index_adapted(key, adapter)
.map(|i| &self.values[i])
}
#[inline]
pub fn get_ptr_adapted<Q: ?Sized, Ad>(&mut self, key: &Q, adapter: &Ad) -> Option<&mut V>
where
Ad: ArrayHashAdapter<Q, K>,
{
let i = self.get_index_adapted(key, adapter)?;
Some(&mut self.values[i])
}
#[inline]
pub fn contains_adapted<Q: ?Sized, Ad>(&self, key: &Q, adapter: &Ad) -> bool
where
Ad: ArrayHashAdapter<Q, K>,
{
self.get_index_adapted(key, adapter).is_some()
}
}
impl<K, V, C: ArrayHashContext<K>, A: MapAllocator> ArrayHashMap<K, V, C, A> {
#[inline]
pub fn get_index(&self, key: &K) -> Option<usize> {
let h = self.ctx.hash(key);
self.find_hash(h, |k, i| self.ctx.eql(key, k, i))
}
#[inline]
pub fn contains(&self, key: &K) -> bool {
self.get_index(key).is_some()
}
#[inline]
pub fn contains_key(&self, key: &K) -> bool {
self.contains(key)
}
pub fn get(&self, key: &K) -> Option<&V> {
self.get_index(key).map(|i| &self.values[i])
}
pub fn get_ptr_mut(&mut self, key: &K) -> Option<&mut V> {
let i = self.get_index(key)?;
Some(&mut self.values[i])
}
pub fn re_index(&mut self) -> Result<(), AllocError> {
for (i, k) in self.keys.iter().enumerate() {
self.hashes[i] = self.ctx.hash(k);
}
self.drop_index();
if self.keys.len() > INDEX_THRESHOLD {
self.rebuild_index();
}
Ok(())
}
pub fn put(&mut self, key: K, value: V) -> Result<(), AllocError> {
let h = self.ctx.hash(&key);
if let Some(i) = self.find_hash(h, |k, idx| self.ctx.eql(&key, k, idx)) {
self.values[i] = value;
} else {
self.push_entry(key, value, h);
}
Ok(())
}
pub fn put_no_clobber(&mut self, key: K, value: V) -> Result<(), AllocError> {
let h = self.ctx.hash(&key);
debug_assert!(
self.find_hash(h, |k, idx| self.ctx.eql(&key, k, idx))
.is_none(),
"put_no_clobber: key already present",
);
self.push_entry(key, value, h);
Ok(())
}
pub fn put_assume_capacity(&mut self, key: K, value: V) {
let _ = self.put(key, value);
}
pub fn insert(&mut self, key: K, value: V) -> Option<V> {
let h = self.ctx.hash(&key);
if let Some(i) = self.find_hash(h, |k, idx| self.ctx.eql(&key, k, idx)) {
Some(core::mem::replace(&mut self.values[i], value))
} else {
self.push_entry(key, value, h);
None
}
}
pub fn swap_remove(&mut self, key: &K) -> bool {
let Some(i) = self.get_index(key) else {
return false;
};
self.swap_remove_at(i);
true
}
pub fn fetch_swap_remove(&mut self, key: &K) -> Option<(K, V)> {
let i = self.get_index(key)?;
Some(self.swap_remove_at(i))
}
#[inline]
pub fn ordered_remove(&mut self, key: &K) -> bool {
self.remove(key).is_some()
}
pub fn remove(&mut self, key: &K) -> Option<V> {
let i = self.get_index(key)?;
self.keys.remove(i);
self.hashes.remove(i);
self.drop_index();
if self.keys.len() > INDEX_THRESHOLD {
self.rebuild_index();
}
Some(self.values.remove(i))
}
#[inline]
pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
self.get_ptr_mut(key)
}
pub fn entry(&mut self, key: K) -> MapEntry<'_, K, V, C, A> {
let h = self.ctx.hash(&key);
if let Some(idx) = self.find_hash(h, |k, i| self.ctx.eql(&key, k, i)) {
MapEntry::Occupied(OccupiedEntry { map: self, idx })
} else {
MapEntry::Vacant(VacantEntry {
map: self,
key,
hash: h,
})
}
}
}
pub enum MapEntry<'a, K, V, C, A: MapAllocator = Global> {
Occupied(OccupiedEntry<'a, K, V, C, A>),
Vacant(VacantEntry<'a, K, V, C, A>),
}
pub struct OccupiedEntry<'a, K, V, C, A: MapAllocator = Global> {
map: &'a mut ArrayHashMap<K, V, C, A>,
idx: usize,
}
impl<'a, K, V, C, A: MapAllocator> OccupiedEntry<'a, K, V, C, A> {
#[inline]
pub fn get(&self) -> &V {
&self.map.values[self.idx]
}
#[inline]
pub fn get_mut(&mut self) -> &mut V {
&mut self.map.values[self.idx]
}
#[inline]
pub fn into_mut(self) -> &'a mut V {
&mut self.map.values[self.idx]
}
#[inline]
pub fn key(&self) -> &K {
&self.map.keys[self.idx]
}
#[inline]
pub fn index(&self) -> usize {
self.idx
}
pub fn insert(&mut self, value: V) -> V {
core::mem::replace(&mut self.map.values[self.idx], value)
}
pub fn swap_remove(self) -> V {
self.map.swap_remove_at(self.idx).1
}
}
pub struct VacantEntry<'a, K, V, C, A: MapAllocator = Global> {
map: &'a mut ArrayHashMap<K, V, C, A>,
key: K,
hash: u32,
}
impl<'a, K, V, C, A: MapAllocator> VacantEntry<'a, K, V, C, A> {
#[inline]
pub fn key(&self) -> &K {
&self.key
}
pub fn insert(self, value: V) -> &'a mut V {
let i = self.map.push_entry(self.key, value, self.hash);
&mut self.map.values[i]
}
}
impl<'a, K, V, C, A: MapAllocator> MapEntry<'a, K, V, C, A> {
pub fn or_insert(self, default: V) -> &'a mut V {
match self {
MapEntry::Occupied(o) => o.into_mut(),
MapEntry::Vacant(v) => v.insert(default),
}
}
pub fn or_insert_with<F: FnOnce() -> V>(self, f: F) -> &'a mut V {
match self {
MapEntry::Occupied(o) => o.into_mut(),
MapEntry::Vacant(v) => v.insert(f()),
}
}
}
impl<K, V: Default, C: ArrayHashContext<K>, A: MapAllocator> ArrayHashMap<K, V, C, A> {
pub fn get_or_put(&mut self, key: K) -> Result<GetOrPutResult<'_, K, V>, AllocError> {
let h = self.ctx.hash(&key);
if let Some(i) = self.find_hash(h, |k, idx| self.ctx.eql(&key, k, idx)) {
return Ok(self.gop_at(i, true));
}
let i = self.push_entry(key, V::default(), h);
Ok(self.gop_at(i, false))
}
pub fn get_or_put_assume_capacity(&mut self, key: K) -> GetOrPutResult<'_, K, V> {
let h = self.ctx.hash(&key);
if let Some(i) = self.find_hash(h, |k, idx| self.ctx.eql(&key, k, idx)) {
return self.gop_at(i, true);
}
let i = self.push_entry(key, V::default(), h);
self.gop_at(i, false)
}
pub fn get_or_put_value(
&mut self,
key: K,
value: V,
) -> Result<GetOrPutResult<'_, K, V>, AllocError> {
let gop = self.get_or_put(key)?;
if !gop.found_existing {
*gop.value_ptr = value;
}
let i = gop.index;
let found = gop.found_existing;
Ok(self.gop_at(i, found))
}
}
impl<K: Default, V: Default, C, A: MapAllocator> ArrayHashMap<K, V, C, A> {
pub fn get_or_put_adapted<Q: ?Sized, Ad>(
&mut self,
key: &Q,
adapter: &Ad,
) -> Result<GetOrPutResult<'_, K, V>, AllocError>
where
Ad: ArrayHashAdapter<Q, K>,
{
let h = adapter.hash(key);
if let Some(i) = self.find_hash(h, |k, idx| adapter.eql(key, k, idx)) {
return Ok(self.gop_at(i, true));
}
let i = self.push_entry(K::default(), V::default(), h);
Ok(self.gop_at(i, false))
}
#[inline]
pub fn get_or_put_context_adapted<Q: ?Sized, Ad>(
&mut self,
key: &Q,
adapter: &Ad,
_ctx: C,
) -> Result<GetOrPutResult<'_, K, V>, AllocError>
where
Ad: ArrayHashAdapter<Q, K>,
{
self.get_or_put_adapted(key, adapter)
}
}
impl<K, V, C, A: MapAllocator> ArrayHashMapExt for ArrayHashMap<K, V, C, A> {
type Key = K;
type Value = V;
type Iterator<'a>
= Iter<'a, K, V>
where
Self: 'a;
fn iterator(&mut self) -> Iter<'_, K, V> {
ArrayHashMap::iterator(self)
}
}
pub struct StringArrayHashMap<V, C = StringContext, A: MapAllocator = Global> {
inner: ArrayHashMap<bun_alloc::core_alloc::AllocBox<[u8], A>, V, BoxedSliceContext<C>, A>,
ctx: C,
}
pub type CaseInsensitiveAsciiStringArrayHashMap<V> =
StringArrayHashMap<V, CaseInsensitiveAsciiStringContext>;
impl<V, C: Default, A: MapAllocator> Default for StringArrayHashMap<V, C, A> {
fn default() -> Self {
Self {
inner: ArrayHashMap::new(),
ctx: C::default(),
}
}
}
impl<V: Clone, C: Default, A: MapAllocator> StringArrayHashMap<V, C, A> {
pub fn clone(&self) -> Result<Self, AllocError> {
Ok(Self {
inner: self.inner.clone()?,
ctx: C::default(),
})
}
}
impl<V, C, A: MapAllocator> Deref for StringArrayHashMap<V, C, A> {
type Target = ArrayHashMap<bun_alloc::core_alloc::AllocBox<[u8], A>, V, BoxedSliceContext<C>, A>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<V, C, A: MapAllocator> DerefMut for StringArrayHashMap<V, C, A> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl<V, C: ArrayHashContext<[u8]> + Default, A: MapAllocator> StringArrayHashMap<V, C, A> {
pub fn new() -> Self {
Self::default()
}
pub fn with_capacity(n: usize) -> Self {
let mut m = Self::default();
m.reserve(n);
m
}
#[inline]
fn find(&self, key: &[u8]) -> Option<usize> {
let h = self.ctx.hash(key);
self.inner.find_hash(h, |k, i| self.ctx.eql(key, k, i))
}
#[inline]
pub fn get_index(&self, key: &[u8]) -> Option<usize> {
self.find(key)
}
#[inline]
pub fn contains(&self, key: &[u8]) -> bool {
self.find(key).is_some()
}
#[inline]
pub fn contains_key(&self, key: &[u8]) -> bool {
self.contains(key)
}
pub fn get(&self, key: &[u8]) -> Option<&V> {
self.find(key).map(|i| &self.inner.values[i])
}
pub fn get_ptr_mut(&mut self, key: &[u8]) -> Option<&mut V> {
let i = self.find(key)?;
Some(&mut self.inner.values[i])
}
#[inline]
pub fn get_mut(&mut self, key: &[u8]) -> Option<&mut V> {
self.get_ptr_mut(key)
}
pub fn insert(&mut self, key: &[u8], value: V) -> Option<V> {
let h = self.ctx.hash(key);
if let Some(i) = self.inner.find_hash(h, |k, idx| self.ctx.eql(key, k, idx)) {
Some(core::mem::replace(&mut self.inner.values[i], value))
} else {
self.inner.push_entry(box_key::<A>(key), value, h);
None
}
}
pub fn put(&mut self, key: &[u8], value: V) -> Result<(), AllocError> {
let h = self.ctx.hash(key);
if let Some(i) = self.inner.find_hash(h, |k, idx| self.ctx.eql(key, k, idx)) {
self.inner.values[i] = value;
} else {
self.inner.push_entry(box_key::<A>(key), value, h);
}
Ok(())
}
pub fn put_assume_capacity(&mut self, key: &[u8], value: V) {
let _ = self.put(key, value);
}
pub fn swap_remove(&mut self, key: &[u8]) -> bool {
let Some(i) = self.find(key) else {
return false;
};
self.inner.swap_remove_at(i);
true
}
pub fn fetch_swap_remove(&mut self, key: &[u8]) -> Option<KV<bun_alloc::core_alloc::AllocBox<[u8], A>, V>> {
let i = self.find(key)?;
let (k, v) = self.inner.swap_remove_at(i);
Some(KV { key: k, value: v })
}
pub fn re_index(&mut self) -> Result<(), AllocError> {
for (i, k) in self.inner.keys.iter().enumerate() {
self.inner.hashes[i] = self.ctx.hash(k);
}
self.inner.drop_index();
if self.inner.keys.len() > INDEX_THRESHOLD {
self.inner.rebuild_index();
}
Ok(())
}
}
impl<V: Default, C: ArrayHashContext<[u8]> + Default, A: MapAllocator> StringArrayHashMap<V, C, A> {
pub fn get_or_put(
&mut self,
key: &[u8],
) -> Result<GetOrPutResult<'_, bun_alloc::core_alloc::AllocBox<[u8], A>, V>, AllocError> {
let h = self.ctx.hash(key);
if let Some(i) = self.inner.find_hash(h, |k, idx| self.ctx.eql(key, k, idx)) {
return Ok(self.inner.gop_at(i, true));
}
let i = self.inner.push_entry(box_key::<A>(key), V::default(), h);
Ok(self.inner.gop_at(i, false))
}
pub fn get_or_put_value(
&mut self,
key: &[u8],
value: V,
) -> Result<GetOrPutResult<'_, bun_alloc::core_alloc::AllocBox<[u8], A>, V>, AllocError> {
let h = self.ctx.hash(key);
if let Some(i) = self.inner.find_hash(h, |k, idx| self.ctx.eql(key, k, idx)) {
return Ok(self.inner.gop_at(i, true));
}
let i = self.inner.push_entry(box_key::<A>(key), value, h);
Ok(self.inner.gop_at(i, false))
}
}
impl<V, C, A: MapAllocator> ArrayHashMapExt for StringArrayHashMap<V, C, A> {
type Key = bun_alloc::core_alloc::AllocBox<[u8], A>;
type Value = V;
type Iterator<'a>
= Iter<'a, bun_alloc::core_alloc::AllocBox<[u8], A>, V>
where
Self: 'a;
fn iterator(&mut self) -> Iter<'_, bun_alloc::core_alloc::AllocBox<[u8], A>, V> {
self.inner.iterator()
}
}
#[derive(Clone)]
pub struct StringHashMap<V, A: Allocator + HashbrownAllocator + Clone + Default = DefaultAlloc> {
inner: hashbrown::HashMap<StringHashMapKey<A>, V, bun_wyhash::BuildHasher, A>,
}
pub type StringHashMapInner<V, A = DefaultAlloc> =
hashbrown::HashMap<StringHashMapKey<A>, V, bun_wyhash::BuildHasher, A>;
pub struct StringHashMapKey<A: Allocator + Default = DefaultAlloc> {
ptr: core::ptr::NonNull<u8>,
len_tag: usize,
_alloc: PhantomData<bun_alloc::core_alloc::AllocBox<[u8], A>>,
}
const SHMK_OWNED_BIT: usize = 1 << (usize::BITS - 1);
const _: () = assert!(
core::mem::size_of::<StringHashMapKey<DefaultAlloc>>() == 2 * core::mem::size_of::<usize>()
);
unsafe impl<A: Allocator + Default + Send> Send for StringHashMapKey<A> {}
unsafe impl<A: Allocator + Default + Sync> Sync for StringHashMapKey<A> {}
impl<A: Allocator + Default> StringHashMapKey<A> {
#[inline(always)]
const fn packed_len(&self) -> usize {
self.len_tag & !SHMK_OWNED_BIT
}
#[inline(always)]
const fn is_owned(&self) -> bool {
self.len_tag & SHMK_OWNED_BIT != 0
}
#[inline]
pub const fn borrowed(s: &'static [u8]) -> Self {
let ptr = unsafe { core::ptr::NonNull::new_unchecked(s.as_ptr().cast_mut()) };
Self {
ptr,
len_tag: s.len(),
_alloc: PhantomData,
}
}
#[inline]
pub fn owned(b: bun_alloc::core_alloc::AllocBox<[u8], A>) -> Self {
let len = b.len();
debug_assert!(
len & SHMK_OWNED_BIT == 0,
"slice len cannot exceed isize::MAX"
);
let (raw, _alloc) = bun_alloc::core_alloc::AllocBox::into_raw_with_allocator(b);
let ptr = unsafe { core::ptr::NonNull::new_unchecked(raw.cast::<u8>()) };
Self {
ptr,
len_tag: len | SHMK_OWNED_BIT,
_alloc: PhantomData,
}
}
}
impl<A: Allocator + Default> Drop for StringHashMapKey<A> {
#[inline]
fn drop(&mut self) {
if self.is_owned() {
let len = self.packed_len();
unsafe {
let slice = core::ptr::slice_from_raw_parts_mut(self.ptr.as_ptr(), len);
drop(bun_alloc::core_alloc::AllocBox::<[u8], A>::from_raw_in(slice, A::default()));
}
}
}
}
impl<A: Allocator + Default> Deref for StringHashMapKey<A> {
type Target = [u8];
#[inline]
fn deref(&self) -> &[u8] {
unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.packed_len()) }
}
}
impl<A: Allocator + Default> core::borrow::Borrow<[u8]> for StringHashMapKey<A> {
#[inline]
fn borrow(&self) -> &[u8] {
self
}
}
impl<A: Allocator + Default> AsRef<[u8]> for StringHashMapKey<A> {
#[inline]
fn as_ref(&self) -> &[u8] {
self
}
}
impl<A: Allocator + Default> Hash for StringHashMapKey<A> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state)
}
}
impl<A: Allocator + Default> PartialEq for StringHashMapKey<A> {
#[inline]
fn eq(&self, other: &Self) -> bool {
**self == **other
}
}
impl<A: Allocator + Default> Eq for StringHashMapKey<A> {}
impl<A: Allocator + Default> Clone for StringHashMapKey<A> {
#[inline]
fn clone(&self) -> Self {
if self.is_owned() {
Self::owned(box_key::<A>(self))
} else {
Self {
ptr: self.ptr,
len_tag: self.len_tag,
_alloc: PhantomData,
}
}
}
}
impl<A: Allocator + Default> From<bun_alloc::core_alloc::AllocBox<[u8], A>> for StringHashMapKey<A> {
#[inline]
fn from(b: bun_alloc::core_alloc::AllocBox<[u8], A>) -> Self {
Self::owned(b)
}
}
impl<A: Allocator + Default> From<&'static [u8]> for StringHashMapKey<A> {
#[inline]
fn from(s: &'static [u8]) -> Self {
Self::borrowed(s)
}
}
impl<V, A: Allocator + HashbrownAllocator + Clone + Default> Default for StringHashMap<V, A> {
fn default() -> Self {
Self {
inner: hashbrown::HashMap::with_hasher_in(
bun_wyhash::BuildHasher::default(),
A::default(),
),
}
}
}
impl<V, A: Allocator + HashbrownAllocator + Clone + Default> Deref for StringHashMap<V, A> {
type Target = StringHashMapInner<V, A>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<V, A: Allocator + HashbrownAllocator + Clone + Default> DerefMut for StringHashMap<V, A> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
#[inline]
fn box_key<A: Allocator + Default>(key: &[u8]) -> bun_alloc::core_alloc::AllocBox<[u8], A> {
let mut v = bun_alloc::core_alloc::AllocVec::with_capacity_in(key.len(), A::default());
v.extend_from_slice(key);
v.into_boxed_slice()
}
#[inline]
fn owned_key<A: Allocator + Default>(key: &[u8]) -> StringHashMapKey<A> {
StringHashMapKey::owned(box_key::<A>(key))
}
impl<V, A: Allocator + HashbrownAllocator + Clone + Default> StringHashMap<V, A> {
#[inline]
pub const fn new_in(alloc: A) -> Self {
Self {
inner: hashbrown::HashMap::with_hasher_in(core::hash::BuildHasherDefault::new(), alloc),
}
}
}
impl<V, A: Allocator + HashbrownAllocator + Clone + Default> StringHashMap<V, A> {
pub fn new() -> Self {
Self::default()
}
pub fn with_capacity(n: usize) -> Self {
Self {
inner: hashbrown::HashMap::with_capacity_and_hasher_in(
n,
bun_wyhash::BuildHasher::default(),
A::default(),
),
}
}
#[inline]
pub fn count(&self) -> usize {
self.inner.len()
}
#[inline]
pub fn values(&self) -> hashbrown::hash_map::Values<'_, StringHashMapKey<A>, V> {
self.inner.values()
}
#[inline]
pub fn values_mut(&mut self) -> hashbrown::hash_map::ValuesMut<'_, StringHashMapKey<A>, V> {
self.inner.values_mut()
}
pub fn ensure_total_capacity(&mut self, n: usize) -> Result<(), AllocError> {
let need = n.saturating_sub(self.inner.len());
self.inner.reserve(need);
Ok(())
}
pub fn ensure_unused_capacity(&mut self, additional: usize) -> Result<(), AllocError> {
self.inner.reserve(additional);
Ok(())
}
pub fn put(&mut self, key: &[u8], value: V) -> Result<(), AllocError> {
self.inner.insert(owned_key::<A>(key), value);
Ok(())
}
#[inline]
pub fn put_static_key(&mut self, key: &'static [u8], value: V) -> Result<(), AllocError> {
self.inner.insert(StringHashMapKey::borrowed(key), value);
Ok(())
}
#[inline]
pub fn hash_key(&self, key: &[u8]) -> u64 {
use core::hash::BuildHasher;
self.inner.hasher().hash_one(key)
}
#[inline]
pub fn get_hashed(&self, hash: u64, key: &[u8]) -> Option<&V> {
self.inner
.raw_entry()
.from_key_hashed_nocheck(hash, key)
.map(|(_, v)| v)
}
#[inline]
pub fn put_static_key_hashed(
&mut self,
hash: u64,
key: &'static [u8],
value: V,
) -> Result<(), AllocError> {
use hashbrown::hash_map::RawEntryMut;
match self
.inner
.raw_entry_mut()
.from_key_hashed_nocheck(hash, key)
{
RawEntryMut::Occupied(mut e) => {
e.insert(value);
}
RawEntryMut::Vacant(e) => {
e.insert_hashed_nocheck(hash, StringHashMapKey::borrowed(key), value);
}
}
Ok(())
}
#[inline]
pub unsafe fn put_borrowed(&mut self, key: &[u8], value: V) -> Result<(), AllocError> {
let key: &'static [u8] = unsafe { &*std::ptr::from_ref::<[u8]>(key) };
self.inner.insert(StringHashMapKey::borrowed(key), value);
Ok(())
}
pub fn put_owned(&mut self, key: bun_alloc::core_alloc::AllocBox<[u8], A>, value: V) -> Result<(), AllocError> {
self.inner.try_reserve(1).map_err(|_| AllocError)?;
self.inner.insert(StringHashMapKey::owned(key), value);
Ok(())
}
#[inline]
pub fn put_assume_capacity(&mut self, key: &[u8], value: V) {
self.inner.insert(owned_key::<A>(key), value);
}
pub fn put_no_clobber(&mut self, key: &[u8], value: V) -> Result<(), AllocError> {
let prev = self.inner.insert(owned_key::<A>(key), value);
debug_assert!(prev.is_none(), "put_no_clobber: key already present");
Ok(())
}
#[inline]
pub fn get_adapted<C>(&self, key: &[u8], _adapter: &C) -> Option<&V> {
self.inner.get(key)
}
#[inline]
pub fn contains_adapted<C>(&self, key: &[u8], _adapter: &C) -> bool {
self.inner.contains_key(key)
}
}
pub use crate::hash_map::GetOrPutResult as StringHashMapGetOrPut;
impl<V: Default, A: Allocator + HashbrownAllocator + Clone + Default> StringHashMap<V, A> {
pub fn get_or_put(&mut self, key: &[u8]) -> Result<StringHashMapGetOrPut<'_, V>, AllocError> {
Ok(self.get_or_put_context_adapted(key, ()))
}
pub fn get_or_put_value(&mut self, key: &[u8], value: V) -> Result<&mut V, AllocError> {
Ok(self.inner.entry(owned_key::<A>(key)).or_insert(value))
}
pub fn get_or_put_context_adapted<C>(
&mut self,
key: &[u8],
_adapter: C,
) -> StringHashMapGetOrPut<'_, V> {
use hashbrown::hash_map::Entry as HbEntry;
match self.inner.entry(owned_key::<A>(key)) {
HbEntry::Occupied(o) => StringHashMapGetOrPut {
found_existing: true,
value_ptr: o.into_mut(),
},
HbEntry::Vacant(v) => StringHashMapGetOrPut {
found_existing: false,
value_ptr: v.insert(V::default()),
},
}
}
#[inline]
pub unsafe fn get_or_put_borrowed(&mut self, key: &[u8]) -> StringHashMapGetOrPut<'_, V> {
use hashbrown::hash_map::EntryRef;
let key: &'static [u8] = unsafe { &*std::ptr::from_ref::<[u8]>(key) };
match self.inner.entry_ref(key) {
EntryRef::Occupied(o) => StringHashMapGetOrPut {
found_existing: true,
value_ptr: o.into_mut(),
},
EntryRef::Vacant(v) => StringHashMapGetOrPut {
found_existing: false,
value_ptr: v.insert(V::default()),
},
}
}
}
#[allow(non_snake_case)]
pub mod StringHashMapContext {
#[inline]
pub fn eql(a: &[u8], b: &[u8]) -> bool {
a == b
}
#[inline]
pub fn pre(input: &[u8]) -> super::string_hash_map::Prehashed<'_> {
super::string_hash_map::Prehashed {
value: bun_wyhash::hash(input),
input,
}
}
pub use super::string_hash_map::{Prehashed, PrehashedCaseInsensitive, hash};
}
pub mod string_hash_map {
#[inline]
pub fn hash(s: &[u8]) -> u64 {
bun_wyhash::hash(s)
}
#[derive(Clone, Copy)]
pub struct Prehashed<'a> {
pub value: u64,
pub input: &'a [u8],
}
impl<'a> Prehashed<'a> {
#[inline]
pub fn new(input: &'a [u8]) -> Self {
Self {
value: hash(input),
input,
}
}
#[inline]
pub fn hash(&self, s: &[u8]) -> u64 {
if core::ptr::eq(s.as_ptr(), self.input.as_ptr()) && s.len() == self.input.len() {
return self.value;
}
hash(s)
}
#[inline]
pub fn eql(&self, a: &[u8], b: &[u8]) -> bool {
a == b
}
}
pub struct PrehashedCaseInsensitive {
pub value: u64,
pub input: Box<[u8]>,
}
impl PrehashedCaseInsensitive {
pub fn init(input: &[u8]) -> Self {
let mut out = vec![0u8; input.len()].into_boxed_slice();
bun_core::strings::copy_lowercase(input, &mut out);
Self {
value: hash(&out),
input: out,
}
}
#[inline]
pub fn hash(&self, s: &[u8]) -> u64 {
if core::ptr::eq(s.as_ptr(), self.input.as_ptr()) && s.len() == self.input.len() {
return self.value;
}
hash(s)
}
#[inline]
pub fn eql(&self, a: &[u8], b: &[u8]) -> bool {
bun_core::strings::eql_case_insensitive_ascii_check_length(a, b)
}
}
pub type GetOrPutResult<'a, V> = super::StringHashMapGetOrPut<'a, V>;
}
#[derive(Default)]
pub struct StringSet {
pub map: StringArrayHashMap<()>,
}
impl StringSet {
#[inline]
pub fn new() -> Self {
Self::default()
}
#[inline]
pub fn init() -> Self {
Self::default()
}
pub fn clone(&self) -> Result<Self, AllocError> {
Ok(Self {
map: self.map.clone()?,
})
}
#[inline]
pub fn is_empty(&self) -> bool {
self.map.count() == 0
}
#[inline]
pub fn count(&self) -> usize {
self.map.count()
}
#[inline]
#[cfg(bao_nightly)]
pub fn keys(&self) -> &[Box<[u8]>] {
self.map.keys()
}
#[cfg(not(bao_nightly))]
pub fn keys(&self) -> &[bun_alloc::core_alloc::AllocBox<[u8], bun_alloc::core_alloc::Global>] {
self.map.keys()
}
pub fn insert(&mut self, key: &[u8]) -> Result<(), AllocError> {
let _ = self.map.get_or_put(key)?;
Ok(())
}
#[inline]
pub fn contains(&self, key: &[u8]) -> bool {
self.map.contains(key)
}
#[inline]
pub fn swap_remove(&mut self, key: &[u8]) -> bool {
self.map.swap_remove(key)
}
pub fn clear_and_free(&mut self) {
self.map.clear_retaining_capacity();
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct StringHashMapUnownedKey {
pub hash: u64,
pub len: usize,
}
impl StringHashMapUnownedKey {
#[inline]
pub fn init(s: &[u8]) -> Self {
Self {
hash: bun_wyhash::hash(s),
len: s.len(),
}
}
}
pub mod string_hash_map_unowned {
pub use super::StringHashMapUnownedKey as Key;
#[derive(Default, Clone, Copy)]
pub struct Adapter;
impl Adapter {
#[inline]
pub fn hash(self, key: &Key) -> u64 {
key.hash
}
#[inline]
pub fn eql(self, a: &Key, b: &Key) -> bool {
a.hash == b.hash && a.len == b.len
}
}
}
#[cfg(test)]
mod index_tests {
use super::*;
#[test]
fn indexed_lookup_agrees_with_linear() {
let mut m: ArrayHashMap<u64, u64> = ArrayHashMap::new();
for i in 0..1000u64 {
assert!(m.put(i.wrapping_mul(2654435761), i).is_ok());
}
for i in 0..1000u64 {
let k = i.wrapping_mul(2654435761);
assert_eq!(m.get(&k), Some(&i));
}
assert_eq!(m.get(&1), None);
assert!(m.swap_remove(&0));
assert_eq!(m.get(&0), None);
for i in 1..1000u64 {
let k = i.wrapping_mul(2654435761);
assert_eq!(m.get(&k), Some(&i));
}
let gop = m.get_or_put(2654435761).unwrap();
assert!(gop.found_existing);
assert_eq!(*gop.value_ptr, 1);
}
#[test]
fn string_map_indexed() {
let mut m: StringArrayHashMap<usize> = StringArrayHashMap::new();
let keys: Vec<String> = (0..200).map(|i| format!("key{i}")).collect();
for (i, k) in keys.iter().enumerate() {
m.put(k.as_bytes(), i).unwrap();
}
for (i, k) in keys.iter().enumerate() {
assert_eq!(m.get(k.as_bytes()), Some(&i));
}
assert_eq!(m.get(b"missing"), None);
}
}