Skip to content

Commit

Permalink
Rename get_each_mut to get_many_mut and align API with the stdlib
Browse files Browse the repository at this point in the history
The standard library will be gaining a similar [ability for
slices][]. This updates Hashbrown's API to match that PR and use only
stable `MaybeUninit` functions.

[ability for slices]: rust-lang/rust#83608
  • Loading branch information
shepmaster committed Nov 7, 2021
1 parent f169c25 commit c9c361a
Show file tree
Hide file tree
Showing 3 changed files with 244 additions and 122 deletions.
14 changes: 0 additions & 14 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,20 +129,6 @@ pub enum TryReserveError {
},
}

/// The error type for [`RawTable::get_each_mut`](crate::raw::RawTable::get_each_mut),
/// [`HashMap::get_each_mut`], and [`HashMap::get_each_key_value_mut`].
#[cfg(feature = "nightly")]
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum UnavailableMutError {
/// The requested entry is not present in the table.
Absent,
/// The requested entry is present, but a mutable reference to it was already created and
/// returned from this call to `get_each_mut` or `get_each_key_value_mut`.
///
/// Includes the index of the existing mutable reference in the returned array.
Duplicate(usize),
}

/// Wrapper around `Bump` which allows it to be used as an allocator for
/// `HashMap`, `HashSet` and `RawTable`.
///
Expand Down
262 changes: 201 additions & 61 deletions src/map.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use crate::raw::{Allocator, Bucket, Global, RawDrain, RawIntoIter, RawIter, RawTable};
use crate::TryReserveError;
#[cfg(feature = "nightly")]
use crate::UnavailableMutError;
use core::borrow::Borrow;
use core::fmt::{self, Debug};
use core::hash::{BuildHasher, Hash};
Expand Down Expand Up @@ -1172,109 +1170,251 @@ where

/// Attempts to get mutable references to `N` values in the map at once.
///
/// Returns an array of length `N` with the results of each query. For soundness,
/// at most one mutable reference will be returned to any value. An
/// `Err(UnavailableMutError::Duplicate(i))` in the returned array indicates that a suitable
/// key-value pair exists, but a mutable reference to the value already occurs at index `i` in
/// the returned array.
///
/// This method is available only if the `nightly` feature is enabled.
/// Returns an array of length `N` with the results of each query. For soundness, at most one
/// mutable reference will be returned to any value. `None` will be returned if any of the
/// keys are duplicates or missing.
///
/// # Examples
///
/// ```
/// use hashbrown::{HashMap, UnavailableMutError};
/// use hashbrown::HashMap;
///
/// let mut libraries = HashMap::new();
/// libraries.insert("Bodleian Library".to_string(), 1602);
/// libraries.insert("Athenæum".to_string(), 1807);
/// libraries.insert("Herzogin-Anna-Amalia-Bibliothek".to_string(), 1691);
/// libraries.insert("Library of Congress".to_string(), 1800);
///
/// let got = libraries.get_each_mut([
/// let got = libraries.get_many_mut([
/// "Athenæum",
/// "Library of Congress",
/// ]);
/// assert_eq!(
/// got,
/// Some([
/// &mut 1807,
/// &mut 1800,
/// ]),
/// );
///
/// // Missing keys result in None
/// let got = libraries.get_many_mut([
/// "Athenæum",
/// "New York Public Library",
/// ]);
/// assert_eq!(got, None);
///
/// // Duplicate keys result in None
/// let got = libraries.get_many_mut([
/// "Athenæum",
/// "Athenæum",
/// ]);
/// assert_eq!(got, None);
/// ```
pub fn get_many_mut<Q: ?Sized, const N: usize>(&mut self, ks: [&Q; N]) -> Option<[&'_ mut V; N]>
where
K: Borrow<Q>,
Q: Hash + Eq,
{
self.get_many_mut_inner(ks).map(|res| res.map(|(_, v)| v))
}

/// Attempts to get mutable references to `N` values in the map at once, without validating that
/// the values are unique.
///
/// Returns an array of length `N` with the results of each query. `None` will be returned if
/// any of the keys are missing.
///
/// For a safe alternative see [`get_many_mut`].
///
/// # Safety
///
/// Calling this method with overlapping keys is *[undefined behavior]* even if the resulting
/// references are not used.
///
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
///
/// # Examples
///
/// ```
/// use hashbrown::HashMap;
///
/// let mut libraries = HashMap::new();
/// libraries.insert("Bodleian Library".to_string(), 1602);
/// libraries.insert("Athenæum".to_string(), 1807);
/// libraries.insert("Herzogin-Anna-Amalia-Bibliothek".to_string(), 1691);
/// libraries.insert("Library of Congress".to_string(), 1800);
///
/// let got = libraries.get_many_mut([
/// "Athenæum",
/// "Library of Congress",
/// ]);
/// assert_eq!(
/// got,
/// [
/// Ok(&mut 1807),
/// Err(UnavailableMutError::Absent),
/// Err(UnavailableMutError::Duplicate(0)),
/// Ok(&mut 1800),
/// ]
/// Some([
/// &mut 1807,
/// &mut 1800,
/// ]),
/// );
///
/// // Missing keys result in None
/// let got = libraries.get_many_mut([
/// "Athenæum",
/// "New York Public Library",
/// ]);
/// assert_eq!(got, None);
/// ```
#[cfg(feature = "nightly")]
pub fn get_each_mut<Q: ?Sized, const N: usize>(
pub unsafe fn get_many_unchecked_mut<Q: ?Sized, const N: usize>(
&mut self,
ks: [&Q; N],
) -> [Result<&'_ mut V, UnavailableMutError>; N]
) -> Option<[&'_ mut V; N]>
where
K: Borrow<Q>,
Q: Hash + Eq,
{
self.get_each_inner_mut(ks).map(|res| res.map(|(_, v)| v))
self.get_many_unchecked_mut_inner(ks)
.map(|res| res.map(|(_, v)| v))
}

/// Attempts to get mutable references to `N` values in the map at once, with immutable
/// references to the corresponding keys.
///
/// Returns an array of length `N` with the results of each query. For soundness,
/// at most one mutable reference will be returned to any value. An
/// `Err(UnavailableMutError::Duplicate(i))` in the returned array indicates that a suitable
/// key-value pair exists, but a mutable reference to the value already occurs at index `i` in
/// the returned array.
///
/// This method is available only if the `nightly` feature is enabled.
/// Returns an array of length `N` with the results of each query. For soundness, at most one
/// mutable reference will be returned to any value. `None` will be returned if any of the keys
/// are duplicates or missing.
///
/// # Examples
///
/// ```
/// use hashbrown::{HashMap, UnavailableMutError};
/// use hashbrown::HashMap;
///
/// let mut libraries = HashMap::new();
/// libraries.insert("Bodleian Library".to_string(), 1602);
/// libraries.insert("Athenæum".to_string(), 1807);
/// libraries.insert("Herzogin-Anna-Amalia-Bibliothek".to_string(), 1691);
/// libraries.insert("Library of Congress".to_string(), 1800);
///
/// let got = libraries.get_each_key_value_mut([
/// let got = libraries.get_many_key_value_mut([
/// "Bodleian Library",
/// "Herzogin-Anna-Amalia-Bibliothek",
/// "Herzogin-Anna-Amalia-Bibliothek",
/// ]);
/// assert_eq!(
/// got,
/// Some([
/// (&"Bodleian Library".to_string(), &mut 1602),
/// (&"Herzogin-Anna-Amalia-Bibliothek".to_string(), &mut 1691),
/// ]),
/// );
/// // Missing keys result in None
/// let got = libraries.get_many_key_value_mut([
/// "Bodleian Library",
/// "Gewandhaus",
/// ]);
/// assert_eq!(got, None);
///
/// // Duplicate keys result in None
/// let got = libraries.get_many_key_value_mut([
/// "Bodleian Library",
/// "Herzogin-Anna-Amalia-Bibliothek",
/// "Herzogin-Anna-Amalia-Bibliothek",
/// ]);
/// assert_eq!(got, None);
/// ```
pub fn get_many_key_value_mut<Q: ?Sized, const N: usize>(
&mut self,
ks: [&Q; N],
) -> Option<[(&'_ K, &'_ mut V); N]>
where
K: Borrow<Q>,
Q: Hash + Eq,
{
self.get_many_mut_inner(ks)
.map(|res| res.map(|(k, v)| (&*k, v)))
}

/// Attempts to get mutable references to `N` values in the map at once, with immutable
/// references to the corresponding keys, without validating that the values are unique.
///
/// Returns an array of length `N` with the results of each query. `None` will be returned if
/// any of the keys are missing.
///
/// For a safe alternative see [`get_many_key_value_mut`].
///
/// # Safety
///
/// Calling this method with overlapping keys is *[undefined behavior]* even if the resulting
/// references are not used.
///
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
///
/// # Examples
///
/// ```
/// use hashbrown::HashMap;
///
/// let mut libraries = HashMap::new();
/// libraries.insert("Bodleian Library".to_string(), 1602);
/// libraries.insert("Athenæum".to_string(), 1807);
/// libraries.insert("Herzogin-Anna-Amalia-Bibliothek".to_string(), 1691);
/// libraries.insert("Library of Congress".to_string(), 1800);
///
/// let got = libraries.get_many_key_value_mut([
/// "Bodleian Library",
/// "Herzogin-Anna-Amalia-Bibliothek",
/// ]);
/// assert_eq!(
/// got,
/// [
/// Ok((&"Bodleian Library".to_string(), &mut 1602)),
/// Ok((&"Herzogin-Anna-Amalia-Bibliothek".to_string(), &mut 1691)),
/// Err(UnavailableMutError::Duplicate(1)),
/// Err(UnavailableMutError::Absent),
/// ]
/// Some([
/// (&"Bodleian Library".to_string(), &mut 1602),
/// (&"Herzogin-Anna-Amalia-Bibliothek".to_string(), &mut 1691),
/// ]),
/// );
/// // Missing keys result in None
/// let got = libraries.get_many_key_value_mut([
/// "Bodleian Library",
/// "Gewandhaus",
/// ]);
/// assert_eq!(got, None);
/// ```
#[cfg(feature = "nightly")]
pub fn get_each_key_value_mut<Q: ?Sized, const N: usize>(
pub unsafe fn get_many_key_value_unchecked_mut<Q: ?Sized, const N: usize>(
&mut self,
ks: [&Q; N],
) -> [Result<(&'_ K, &'_ mut V), UnavailableMutError>; N]
) -> Option<[(&'_ K, &'_ mut V); N]>
where
K: Borrow<Q>,
Q: Hash + Eq,
{
self.get_each_inner_mut(ks)
self.get_many_unchecked_mut_inner(ks)
.map(|res| res.map(|(k, v)| (&*k, v)))
}

#[cfg(feature = "nightly")]
fn get_each_inner_mut<Q: ?Sized, const N: usize>(
fn get_many_mut_inner<Q: ?Sized, const N: usize>(
&mut self,
ks: [&Q; N],
) -> [Result<&'_ mut (K, V), UnavailableMutError>; N]
) -> Option<[&'_ mut (K, V); N]>
where
K: Borrow<Q>,
Q: Hash + Eq,
{
let hashes = self.build_hashes_inner(ks);
self.table
.get_many_mut(hashes, |i, (k, _)| ks[i].eq(k.borrow()))
}

unsafe fn get_many_unchecked_mut_inner<Q: ?Sized, const N: usize>(
&mut self,
ks: [&Q; N],
) -> Option<[&'_ mut (K, V); N]>
where
K: Borrow<Q>,
Q: Hash + Eq,
{
let hashes = self.build_hashes_inner(ks);
self.table
.get_many_unchecked_mut(hashes, |i, (k, _)| ks[i].eq(k.borrow()))
}

fn build_hashes_inner<Q: ?Sized, const N: usize>(&self, ks: [&Q; N]) -> [u64; N]
where
K: Borrow<Q>,
Q: Hash + Eq,
Expand All @@ -1283,8 +1423,7 @@ where
for i in 0..N {
hashes[i] = make_hash::<K, Q, S>(&self.hash_builder, ks[i]);
}
self.table
.get_each_mut(hashes, |i, (k, _)| ks[i].eq(k.borrow()))
hashes
}

/// Inserts a key-value pair into the map.
Expand Down Expand Up @@ -5120,31 +5259,32 @@ mod test_map {
}

#[test]
#[cfg(feature = "nightly")]
fn test_get_each_mut() {
use crate::UnavailableMutError::*;

let mut map = HashMap::new();
map.insert("foo".to_owned(), 0);
map.insert("bar".to_owned(), 10);
map.insert("baz".to_owned(), 20);
map.insert("qux".to_owned(), 30);

let xs = map.get_each_mut(["foo", "dud", "foo", "qux"]);
assert_eq!(
xs,
[Ok(&mut 0), Err(Absent), Err(Duplicate(0)), Ok(&mut 30)]
);
let xs = map.get_many_mut(["foo", "qux"]);
assert_eq!(xs, Some([&mut 0, &mut 30]));

let xs = map.get_many_mut(["foo", "dud"]);
assert_eq!(xs, None);

let ys = map.get_each_key_value_mut(["bar", "baz", "baz", "dip"]);
let xs = map.get_many_mut(["foo", "foo"]);
assert_eq!(xs, None);

let ys = map.get_many_key_value_mut(["bar", "baz"]);
assert_eq!(
ys,
[
Ok((&"bar".to_owned(), &mut 10)),
Ok((&"baz".to_owned(), &mut 20)),
Err(Duplicate(1)),
Err(Absent),
]
Some([(&"bar".to_owned(), &mut 10), (&"baz".to_owned(), &mut 20),]),
);

let ys = map.get_many_key_value_mut(["bar", "dip"]);
assert_eq!(ys, None);

let ys = map.get_many_key_value_mut(["baz", "baz"]);
assert_eq!(ys, None);
}
}
Loading

0 comments on commit c9c361a

Please sign in to comment.