Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,8 @@
//! [benchmarks]: https://github.com/ibraheemdev/papaya/blob/master/BENCHMARKS.md

#![deny(
missing_debug_implementations,
missing_docs,
// TODO missing_debug_implementations,
// TODO missing_docs,
dead_code,
unsafe_op_in_unsafe_fn
)]
Expand All @@ -241,3 +241,8 @@ pub use map::{
};
pub use seize::{Guard, LocalGuard, OwnedGuard};
pub use set::{HashSet, HashSetBuilder, HashSetRef};

/// A low-level hash table interface.
pub mod table {
pub use crate::raw::table::{HashTable, InsertResult, IntoIter, Iter, IterMut};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is missing Dealloc, which makes it impossible to use HashTable

Copy link
Copy Markdown
Contributor

@conradludgate conradludgate Jul 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems Operation and MapGuard are missing too

}
141 changes: 128 additions & 13 deletions src/map.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::raw::utils::MapGuard;
use crate::raw::{self, InsertResult};
use crate::raw::{map, map::InsertResult};
use crate::Equivalent;
use seize::{Collector, Guard, LocalGuard, OwnedGuard};

Expand All @@ -14,7 +14,7 @@ use std::marker::PhantomData;
/// [`HashMap::guard`] or using the [`HashMap::pin`] API. See the [crate-level documentation](crate#usage)
/// for details.
pub struct HashMap<K, V, S = RandomState> {
raw: raw::HashMap<K, V, S>,
raw: map::HashMap<K, V, S>,
}

// Safety: `HashMap` acts as a single-threaded collection on a single thread.
Expand All @@ -32,7 +32,7 @@ unsafe impl<K: Send, V: Send, S: Send> Send for HashMap<K, V, S> {}
// Additionally, `HashMap` owns its `seize::Collector` and never exposes it,
// so multiple threads cannot be involved in reclamation without sharing the
// `HashMap` itself. If this was not true, we would require stricter bounds
// on `HashMap` operations themselves.
// on the `HashMap` operations themselves.
unsafe impl<K: Send + Sync, V: Send + Sync, S: Sync> Sync for HashMap<K, V, S> {}

/// A builder for a [`HashMap`].
Expand Down Expand Up @@ -131,7 +131,7 @@ impl<K, V, S> HashMapBuilder<K, V, S> {
/// Construct a [`HashMap`] from the builder, using the configured options.
pub fn build(self) -> HashMap<K, V, S> {
HashMap {
raw: raw::HashMap::new(self.capacity, self.hasher, self.collector, self.resize_mode),
raw: map::HashMap::new(self.capacity, self.hasher, self.collector, self.resize_mode),
}
}
}
Expand Down Expand Up @@ -298,7 +298,7 @@ impl<K, V, S> HashMap<K, V, S> {
/// ```
pub fn with_capacity_and_hasher(capacity: usize, hash_builder: S) -> HashMap<K, V, S> {
HashMap {
raw: raw::HashMap::new(
raw: map::HashMap::new(
capacity,
hash_builder,
Collector::default(),
Expand Down Expand Up @@ -1006,6 +1006,35 @@ where
}
}

/// An iterator visiting all key-value pairs in arbitrary order, with mutable references
/// to the values. The iterator element type is `(&K, &mut V)`.
///
/// # Examples
///
/// ```
/// use papaya::HashMap;
///
/// let mut map = HashMap::from([
/// ("a", 1),
/// ("b", 2),
/// ("c", 3),
/// ]);
///
/// // Update all values.
/// for (_, val) in map.iter_mut() {
/// *val *= 2;
/// }
///
/// for (key, val) in map.pin().iter() {
/// println!("key: {key} val: {val}");
/// }
#[inline]
pub fn iter_mut(&mut self) -> IterMut<'_, K, V> {
IterMut {
raw: self.raw.iter_mut(),
}
}

/// An iterator visiting all keys in arbitrary order.
/// The iterator element type is `&K`.
///
Expand Down Expand Up @@ -1071,6 +1100,32 @@ where
}
}

impl<K, V, S> IntoIterator for HashMap<K, V, S> {
type Item = (K, V);
type IntoIter = IntoIter<K, V>;

/// Creates a consuming iterator, that is, one that moves each key-value
/// pair out of the map in arbitrary order. The map cannot be used after
/// calling this.
///
/// ```
/// use papaya::HashMap;
///
/// let map = HashMap::from([
/// ("a", 1),
/// ("b", 2),
/// ("c", 3),
/// ]);
///
/// let v: Vec<(&str, i32)> = map.into_iter().collect();
/// ```
fn into_iter(self) -> Self::IntoIter {
IntoIter {
raw: self.raw.into_iter(),
}
}
}

/// An operation to perform on given entry in a [`HashMap`].
///
/// See [`HashMap::compute`] for details.
Expand Down Expand Up @@ -1165,7 +1220,8 @@ where
S: BuildHasher,
{
fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {
// from `hashbrown::HashMap::extend`:
// From `hashbrown::HashMap::extend`:
//
// Keys may be already present or show multiple times in the iterator.
// Reserve the entire hint lower bound if the map is empty.
// Otherwise reserve half the hint (rounded up), so the map
Expand Down Expand Up @@ -1572,11 +1628,11 @@ where
}
}

/// An iterator over a map's entries.
/// An iterator over the entries of a `HashMap`.
///
/// This struct is created by the [`iter`](HashMap::iter) method on [`HashMap`]. See its documentation for details.
pub struct Iter<'g, K, V, G> {
raw: raw::Iter<'g, K, V, MapGuard<G>>,
raw: map::Iter<'g, K, V, MapGuard<G>>,
}

impl<'g, K: 'g, V: 'g, G> Iterator for Iter<'g, K, V, G>
Expand All @@ -1591,18 +1647,48 @@ where
}
}

impl<K, V, G> Clone for Iter<'_, K, V, G> {
fn clone(&self) -> Self {
Self {
raw: self.raw.clone(),
}
}
}

impl<K, V, G> fmt::Debug for Iter<'_, K, V, G>
where
K: fmt::Debug,
V: fmt::Debug,
G: Guard,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list()
.entries(Iter {
raw: self.raw.clone(),
})
.finish()
f.debug_list().entries(self.raw.clone()).finish()
}
}

/// A mutable iterator over the entries of a `HashMap`.
///
/// This struct is created by the [`iter_mut`](HashMap::iter_mut) method on [`HashMap`]. See its documentation for details.
pub struct IterMut<'map, K, V> {
raw: map::IterMut<'map, K, V>,
}

impl<'map, K, V> Iterator for IterMut<'map, K, V> {
type Item = (&'map K, &'map mut V);

#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.raw.next()
}
}

impl<K, V> fmt::Debug for IterMut<'_, K, V>
where
K: fmt::Debug,
V: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.raw.iter()).finish()
}
}

Expand Down Expand Up @@ -1667,3 +1753,32 @@ where
f.debug_tuple("Values").field(&self.iter).finish()
}
}

/// An owned iterator over the entries of a `HashMap`.
///
/// This `struct` is created by the [`into_iter`] method on [`HashMap`]
/// (provided by the [`IntoIterator`] trait). See its documentation for more.
///
/// [`into_iter`]: IntoIterator::into_iter
pub struct IntoIter<K, V> {
pub(crate) raw: map::IntoIter<K, V>,
}

impl<K, V> Iterator for IntoIter<K, V> {
type Item = (K, V);

#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.raw.next()
}
}

impl<K, V> fmt::Debug for IntoIter<K, V>
where
K: fmt::Debug,
V: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.raw.iter()).finish()
}
}
24 changes: 22 additions & 2 deletions src/raw/alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ use std::marker::PhantomData;
use std::sync::atomic::{AtomicPtr, AtomicU8, Ordering};
use std::{alloc, mem, ptr};

use super::{probe, State};
use super::probe;
use super::table::{meta, State};

// A hash-table laid out in a single allocation.
//
Expand Down Expand Up @@ -88,7 +89,7 @@ impl<T> Table<T> {
// Initialize the meta table.
ptr.add(mem::size_of::<TableLayout<T>>())
.cast::<u8>()
.write_bytes(super::meta::EMPTY, len);
.write_bytes(meta::EMPTY, len);
}

Table {
Expand Down Expand Up @@ -157,6 +158,25 @@ impl<T> Table<T> {
}
}

// Returns the entry at the given index.
//
// # Safety
//
// The index must be in-bounds for the length of the table.
#[inline]
pub unsafe fn entry_mut(&mut self, i: usize) -> *mut T {
debug_assert!(i < self.len());

// Safety: The caller guarantees the index is in-bounds.
let entry = unsafe {
let meta = self.raw.add(mem::size_of::<TableLayout<T>>());
let entries = meta.add(self.len()).cast::<AtomicPtr<T>>();
&mut *entries.add(i)
};

*entry.get_mut()
}

/// Returns the length of the table.
#[inline]
pub fn len(&self) -> usize {
Expand Down
Loading
Loading