use crate::hash_map::{equivalent, make_hash, make_hasher};
use crate::raw::{Allocator, Bucket, Global, RawTable};
use crate::{Equivalent, HashMap};
use core::fmt::{self, Debug};
use core::hash::{BuildHasher, Hash};
use core::mem;
impl<K, V, S, A: Allocator> HashMap<K, V, S, A> {
#[cfg_attr(feature = "inline-more", inline)]
pub fn raw_entry_mut(&mut self) -> RawEntryBuilderMut<'_, K, V, S, A> {
RawEntryBuilderMut { map: self }
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn raw_entry(&self) -> RawEntryBuilder<'_, K, V, S, A> {
RawEntryBuilder { map: self }
}
}
pub struct RawEntryBuilderMut<'a, K, V, S, A: Allocator = Global> {
map: &'a mut HashMap<K, V, S, A>,
}
pub enum RawEntryMut<'a, K, V, S, A: Allocator = Global> {
Occupied(RawOccupiedEntryMut<'a, K, V, S, A>),
Vacant(RawVacantEntryMut<'a, K, V, S, A>),
}
pub struct RawOccupiedEntryMut<'a, K, V, S, A: Allocator = Global> {
elem: Bucket<(K, V)>,
table: &'a mut RawTable<(K, V), A>,
hash_builder: &'a S,
}
unsafe impl<K, V, S, A> Send for RawOccupiedEntryMut<'_, K, V, S, A>
where
K: Send,
V: Send,
S: Send,
A: Send + Allocator,
{
}
unsafe impl<K, V, S, A> Sync for RawOccupiedEntryMut<'_, K, V, S, A>
where
K: Sync,
V: Sync,
S: Sync,
A: Sync + Allocator,
{
}
pub struct RawVacantEntryMut<'a, K, V, S, A: Allocator = Global> {
table: &'a mut RawTable<(K, V), A>,
hash_builder: &'a S,
}
pub struct RawEntryBuilder<'a, K, V, S, A: Allocator = Global> {
map: &'a HashMap<K, V, S, A>,
}
impl<'a, K, V, S, A: Allocator> RawEntryBuilderMut<'a, K, V, S, A> {
#[cfg_attr(feature = "inline-more", inline)]
#[allow(clippy::wrong_self_convention)]
pub fn from_key<Q>(self, k: &Q) -> RawEntryMut<'a, K, V, S, A>
where
S: BuildHasher,
Q: Hash + Equivalent<K> + ?Sized,
{
let hash = make_hash::<Q, S>(&self.map.hash_builder, k);
self.from_key_hashed_nocheck(hash, k)
}
#[inline]
#[allow(clippy::wrong_self_convention)]
pub fn from_key_hashed_nocheck<Q>(self, hash: u64, k: &Q) -> RawEntryMut<'a, K, V, S, A>
where
Q: Equivalent<K> + ?Sized,
{
self.from_hash(hash, equivalent(k))
}
}
impl<'a, K, V, S, A: Allocator> RawEntryBuilderMut<'a, K, V, S, A> {
#[cfg_attr(feature = "inline-more", inline)]
#[allow(clippy::wrong_self_convention)]
pub fn from_hash<F>(self, hash: u64, is_match: F) -> RawEntryMut<'a, K, V, S, A>
where
for<'b> F: FnMut(&'b K) -> bool,
{
self.search(hash, is_match)
}
#[cfg_attr(feature = "inline-more", inline)]
fn search<F>(self, hash: u64, mut is_match: F) -> RawEntryMut<'a, K, V, S, A>
where
for<'b> F: FnMut(&'b K) -> bool,
{
match self.map.table.find(hash, |(k, _)| is_match(k)) {
Some(elem) => RawEntryMut::Occupied(RawOccupiedEntryMut {
elem,
table: &mut self.map.table,
hash_builder: &self.map.hash_builder,
}),
None => RawEntryMut::Vacant(RawVacantEntryMut {
table: &mut self.map.table,
hash_builder: &self.map.hash_builder,
}),
}
}
}
impl<'a, K, V, S, A: Allocator> RawEntryBuilder<'a, K, V, S, A> {
#[cfg_attr(feature = "inline-more", inline)]
#[allow(clippy::wrong_self_convention)]
pub fn from_key<Q>(self, k: &Q) -> Option<(&'a K, &'a V)>
where
S: BuildHasher,
Q: Hash + Equivalent<K> + ?Sized,
{
let hash = make_hash::<Q, S>(&self.map.hash_builder, k);
self.from_key_hashed_nocheck(hash, k)
}
#[cfg_attr(feature = "inline-more", inline)]
#[allow(clippy::wrong_self_convention)]
pub fn from_key_hashed_nocheck<Q>(self, hash: u64, k: &Q) -> Option<(&'a K, &'a V)>
where
Q: Equivalent<K> + ?Sized,
{
self.from_hash(hash, equivalent(k))
}
#[cfg_attr(feature = "inline-more", inline)]
fn search<F>(self, hash: u64, mut is_match: F) -> Option<(&'a K, &'a V)>
where
F: FnMut(&K) -> bool,
{
match self.map.table.get(hash, |(k, _)| is_match(k)) {
Some((key, value)) => Some((key, value)),
None => None,
}
}
#[cfg_attr(feature = "inline-more", inline)]
#[allow(clippy::wrong_self_convention)]
pub fn from_hash<F>(self, hash: u64, is_match: F) -> Option<(&'a K, &'a V)>
where
F: FnMut(&K) -> bool,
{
self.search(hash, is_match)
}
}
impl<'a, K, V, S, A: Allocator> RawEntryMut<'a, K, V, S, A> {
#[cfg_attr(feature = "inline-more", inline)]
pub fn insert(self, key: K, value: V) -> RawOccupiedEntryMut<'a, K, V, S, A>
where
K: Hash,
S: BuildHasher,
{
match self {
RawEntryMut::Occupied(mut entry) => {
entry.insert(value);
entry
}
RawEntryMut::Vacant(entry) => entry.insert_entry(key, value),
}
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn or_insert(self, default_key: K, default_val: V) -> (&'a mut K, &'a mut V)
where
K: Hash,
S: BuildHasher,
{
match self {
RawEntryMut::Occupied(entry) => entry.into_key_value(),
RawEntryMut::Vacant(entry) => entry.insert(default_key, default_val),
}
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn or_insert_with<F>(self, default: F) -> (&'a mut K, &'a mut V)
where
F: FnOnce() -> (K, V),
K: Hash,
S: BuildHasher,
{
match self {
RawEntryMut::Occupied(entry) => entry.into_key_value(),
RawEntryMut::Vacant(entry) => {
let (k, v) = default();
entry.insert(k, v)
}
}
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn and_modify<F>(self, f: F) -> Self
where
F: FnOnce(&mut K, &mut V),
{
match self {
RawEntryMut::Occupied(mut entry) => {
{
let (k, v) = entry.get_key_value_mut();
f(k, v);
}
RawEntryMut::Occupied(entry)
}
RawEntryMut::Vacant(entry) => RawEntryMut::Vacant(entry),
}
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn and_replace_entry_with<F>(self, f: F) -> Self
where
F: FnOnce(&K, V) -> Option<V>,
{
match self {
RawEntryMut::Occupied(entry) => entry.replace_entry_with(f),
RawEntryMut::Vacant(_) => self,
}
}
}
impl<'a, K, V, S, A: Allocator> RawOccupiedEntryMut<'a, K, V, S, A> {
#[cfg_attr(feature = "inline-more", inline)]
pub fn key(&self) -> &K {
unsafe { &self.elem.as_ref().0 }
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn key_mut(&mut self) -> &mut K {
unsafe { &mut self.elem.as_mut().0 }
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn into_key(self) -> &'a mut K {
unsafe { &mut self.elem.as_mut().0 }
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn get(&self) -> &V {
unsafe { &self.elem.as_ref().1 }
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn into_mut(self) -> &'a mut V {
unsafe { &mut self.elem.as_mut().1 }
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn get_mut(&mut self) -> &mut V {
unsafe { &mut self.elem.as_mut().1 }
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn get_key_value(&self) -> (&K, &V) {
unsafe {
let (key, value) = self.elem.as_ref();
(key, value)
}
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn get_key_value_mut(&mut self) -> (&mut K, &mut V) {
unsafe {
let &mut (ref mut key, ref mut value) = self.elem.as_mut();
(key, value)
}
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn into_key_value(self) -> (&'a mut K, &'a mut V) {
unsafe {
let &mut (ref mut key, ref mut value) = self.elem.as_mut();
(key, value)
}
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn insert(&mut self, value: V) -> V {
mem::replace(self.get_mut(), value)
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn insert_key(&mut self, key: K) -> K {
mem::replace(self.key_mut(), key)
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn remove(self) -> V {
self.remove_entry().1
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn remove_entry(self) -> (K, V) {
unsafe { self.table.remove(self.elem).0 }
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn replace_entry_with<F>(self, f: F) -> RawEntryMut<'a, K, V, S, A>
where
F: FnOnce(&K, V) -> Option<V>,
{
unsafe {
let still_occupied = self
.table
.replace_bucket_with(self.elem.clone(), |(key, value)| {
f(&key, value).map(|new_value| (key, new_value))
});
if still_occupied {
RawEntryMut::Occupied(self)
} else {
RawEntryMut::Vacant(RawVacantEntryMut {
table: self.table,
hash_builder: self.hash_builder,
})
}
}
}
}
impl<'a, K, V, S, A: Allocator> RawVacantEntryMut<'a, K, V, S, A> {
#[cfg_attr(feature = "inline-more", inline)]
pub fn insert(self, key: K, value: V) -> (&'a mut K, &'a mut V)
where
K: Hash,
S: BuildHasher,
{
let hash = make_hash::<K, S>(self.hash_builder, &key);
self.insert_hashed_nocheck(hash, key, value)
}
#[cfg_attr(feature = "inline-more", inline)]
#[allow(clippy::shadow_unrelated)]
pub fn insert_hashed_nocheck(self, hash: u64, key: K, value: V) -> (&'a mut K, &'a mut V)
where
K: Hash,
S: BuildHasher,
{
let &mut (ref mut k, ref mut v) = self.table.insert_entry(
hash,
(key, value),
make_hasher::<_, V, S>(self.hash_builder),
);
(k, v)
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn insert_with_hasher<H>(
self,
hash: u64,
key: K,
value: V,
hasher: H,
) -> (&'a mut K, &'a mut V)
where
H: Fn(&K) -> u64,
{
let &mut (ref mut k, ref mut v) = self
.table
.insert_entry(hash, (key, value), |x| hasher(&x.0));
(k, v)
}
#[cfg_attr(feature = "inline-more", inline)]
fn insert_entry(self, key: K, value: V) -> RawOccupiedEntryMut<'a, K, V, S, A>
where
K: Hash,
S: BuildHasher,
{
let hash = make_hash::<K, S>(self.hash_builder, &key);
let elem = self.table.insert(
hash,
(key, value),
make_hasher::<_, V, S>(self.hash_builder),
);
RawOccupiedEntryMut {
elem,
table: self.table,
hash_builder: self.hash_builder,
}
}
}
impl<K, V, S, A: Allocator> Debug for RawEntryBuilderMut<'_, K, V, S, A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RawEntryBuilder").finish()
}
}
impl<K: Debug, V: Debug, S, A: Allocator> Debug for RawEntryMut<'_, K, V, S, A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
RawEntryMut::Vacant(ref v) => f.debug_tuple("RawEntry").field(v).finish(),
RawEntryMut::Occupied(ref o) => f.debug_tuple("RawEntry").field(o).finish(),
}
}
}
impl<K: Debug, V: Debug, S, A: Allocator> Debug for RawOccupiedEntryMut<'_, K, V, S, A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RawOccupiedEntryMut")
.field("key", self.key())
.field("value", self.get())
.finish()
}
}
impl<K, V, S, A: Allocator> Debug for RawVacantEntryMut<'_, K, V, S, A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RawVacantEntryMut").finish()
}
}
impl<K, V, S, A: Allocator> Debug for RawEntryBuilder<'_, K, V, S, A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RawEntryBuilder").finish()
}
}
#[cfg(test)]
mod test_map {
use super::HashMap;
use super::RawEntryMut;
#[test]
fn test_raw_occupied_entry_replace_entry_with() {
let mut a = HashMap::new();
let key = "a key";
let value = "an initial value";
let new_value = "a new value";
let entry = a
.raw_entry_mut()
.from_key(&key)
.insert(key, value)
.replace_entry_with(|k, v| {
assert_eq!(k, &key);
assert_eq!(v, value);
Some(new_value)
});
match entry {
RawEntryMut::Occupied(e) => {
assert_eq!(e.key(), &key);
assert_eq!(e.get(), &new_value);
}
RawEntryMut::Vacant(_) => panic!(),
}
assert_eq!(a[key], new_value);
assert_eq!(a.len(), 1);
let entry = match a.raw_entry_mut().from_key(&key) {
RawEntryMut::Occupied(e) => e.replace_entry_with(|k, v| {
assert_eq!(k, &key);
assert_eq!(v, new_value);
None
}),
RawEntryMut::Vacant(_) => panic!(),
};
match entry {
RawEntryMut::Vacant(_) => {}
RawEntryMut::Occupied(_) => panic!(),
}
assert!(!a.contains_key(key));
assert_eq!(a.len(), 0);
}
#[test]
fn test_raw_entry_and_replace_entry_with() {
let mut a = HashMap::new();
let key = "a key";
let value = "an initial value";
let new_value = "a new value";
let entry = a
.raw_entry_mut()
.from_key(&key)
.and_replace_entry_with(|_, _| panic!());
match entry {
RawEntryMut::Vacant(_) => {}
RawEntryMut::Occupied(_) => panic!(),
}
a.insert(key, value);
let entry = a
.raw_entry_mut()
.from_key(&key)
.and_replace_entry_with(|k, v| {
assert_eq!(k, &key);
assert_eq!(v, value);
Some(new_value)
});
match entry {
RawEntryMut::Occupied(e) => {
assert_eq!(e.key(), &key);
assert_eq!(e.get(), &new_value);
}
RawEntryMut::Vacant(_) => panic!(),
}
assert_eq!(a[key], new_value);
assert_eq!(a.len(), 1);
let entry = a
.raw_entry_mut()
.from_key(&key)
.and_replace_entry_with(|k, v| {
assert_eq!(k, &key);
assert_eq!(v, new_value);
None
});
match entry {
RawEntryMut::Vacant(_) => {}
RawEntryMut::Occupied(_) => panic!(),
}
assert!(!a.contains_key(key));
assert_eq!(a.len(), 0);
}
#[test]
fn test_raw_entry() {
use super::RawEntryMut::{Occupied, Vacant};
let xs = [(1_i32, 10_i32), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)];
let mut map: HashMap<_, _> = xs.iter().copied().collect();
let compute_hash = |map: &HashMap<i32, i32>, k: i32| -> u64 {
super::make_hash::<i32, _>(map.hasher(), &k)
};
match map.raw_entry_mut().from_key(&1) {
Vacant(_) => unreachable!(),
Occupied(mut view) => {
assert_eq!(view.get(), &10);
assert_eq!(view.insert(100), 10);
}
}
let hash1 = compute_hash(&map, 1);
assert_eq!(map.raw_entry().from_key(&1).unwrap(), (&1, &100));
assert_eq!(
map.raw_entry().from_hash(hash1, |k| *k == 1).unwrap(),
(&1, &100)
);
assert_eq!(
map.raw_entry().from_key_hashed_nocheck(hash1, &1).unwrap(),
(&1, &100)
);
assert_eq!(map.len(), 6);
match map.raw_entry_mut().from_key(&2) {
Vacant(_) => unreachable!(),
Occupied(mut view) => {
let v = view.get_mut();
let new_v = (*v) * 10;
*v = new_v;
}
}
let hash2 = compute_hash(&map, 2);
assert_eq!(map.raw_entry().from_key(&2).unwrap(), (&2, &200));
assert_eq!(
map.raw_entry().from_hash(hash2, |k| *k == 2).unwrap(),
(&2, &200)
);
assert_eq!(
map.raw_entry().from_key_hashed_nocheck(hash2, &2).unwrap(),
(&2, &200)
);
assert_eq!(map.len(), 6);
let hash3 = compute_hash(&map, 3);
match map.raw_entry_mut().from_key_hashed_nocheck(hash3, &3) {
Vacant(_) => unreachable!(),
Occupied(view) => {
assert_eq!(view.remove_entry(), (3, 30));
}
}
assert_eq!(map.raw_entry().from_key(&3), None);
assert_eq!(map.raw_entry().from_hash(hash3, |k| *k == 3), None);
assert_eq!(map.raw_entry().from_key_hashed_nocheck(hash3, &3), None);
assert_eq!(map.len(), 5);
match map.raw_entry_mut().from_key(&10) {
Occupied(_) => unreachable!(),
Vacant(view) => {
assert_eq!(view.insert(10, 1000), (&mut 10, &mut 1000));
}
}
assert_eq!(map.raw_entry().from_key(&10).unwrap(), (&10, &1000));
assert_eq!(map.len(), 6);
for k in 0..12 {
let hash = compute_hash(&map, k);
let v = map.get(&k).copied();
let kv = v.as_ref().map(|v| (&k, v));
assert_eq!(map.raw_entry().from_key(&k), kv);
assert_eq!(map.raw_entry().from_hash(hash, |q| *q == k), kv);
assert_eq!(map.raw_entry().from_key_hashed_nocheck(hash, &k), kv);
match map.raw_entry_mut().from_key(&k) {
Occupied(o) => assert_eq!(Some(o.get_key_value()), kv),
Vacant(_) => assert_eq!(v, None),
}
match map.raw_entry_mut().from_key_hashed_nocheck(hash, &k) {
Occupied(o) => assert_eq!(Some(o.get_key_value()), kv),
Vacant(_) => assert_eq!(v, None),
}
match map.raw_entry_mut().from_hash(hash, |q| *q == k) {
Occupied(o) => assert_eq!(Some(o.get_key_value()), kv),
Vacant(_) => assert_eq!(v, None),
}
}
}
#[test]
fn test_key_without_hash_impl() {
#[derive(Debug)]
struct IntWrapper(u64);
let mut m: HashMap<IntWrapper, (), ()> = HashMap::default();
{
assert!(m.raw_entry().from_hash(0, |k| k.0 == 0).is_none());
}
{
let vacant_entry = match m.raw_entry_mut().from_hash(0, |k| k.0 == 0) {
RawEntryMut::Occupied(..) => panic!("Found entry for key 0"),
RawEntryMut::Vacant(e) => e,
};
vacant_entry.insert_with_hasher(0, IntWrapper(0), (), |k| k.0);
}
{
assert!(m.raw_entry().from_hash(0, |k| k.0 == 0).is_some());
assert!(m.raw_entry().from_hash(1, |k| k.0 == 1).is_none());
assert!(m.raw_entry().from_hash(2, |k| k.0 == 2).is_none());
}
{
let vacant_entry = match m.raw_entry_mut().from_hash(1, |k| k.0 == 1) {
RawEntryMut::Occupied(..) => panic!("Found entry for key 1"),
RawEntryMut::Vacant(e) => e,
};
vacant_entry.insert_with_hasher(1, IntWrapper(1), (), |k| k.0);
}
{
assert!(m.raw_entry().from_hash(0, |k| k.0 == 0).is_some());
assert!(m.raw_entry().from_hash(1, |k| k.0 == 1).is_some());
assert!(m.raw_entry().from_hash(2, |k| k.0 == 2).is_none());
}
{
let occupied_entry = match m.raw_entry_mut().from_hash(0, |k| k.0 == 0) {
RawEntryMut::Occupied(e) => e,
RawEntryMut::Vacant(..) => panic!("Couldn't find entry for key 0"),
};
occupied_entry.remove();
}
assert!(m.raw_entry().from_hash(0, |k| k.0 == 0).is_none());
assert!(m.raw_entry().from_hash(1, |k| k.0 == 1).is_some());
assert!(m.raw_entry().from_hash(2, |k| k.0 == 2).is_none());
}
}