Skip to content

Commit 584c798

Browse files
Charlie FanF001
Charlie Fan
authored andcommitted
Implement placement-in protocol for HashMap
1 parent 1727b23 commit 584c798

File tree

3 files changed

+183
-5
lines changed

3 files changed

+183
-5
lines changed

src/libstd/collections/hash/map.rs

+155-5
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ use fmt::{self, Debug};
1919
use hash::{Hash, Hasher, BuildHasher, SipHasher13};
2020
use iter::{FromIterator, FusedIterator};
2121
use mem::{self, replace};
22-
use ops::{Deref, Index};
22+
use ops::{Deref, Index, InPlace, Place, Placer};
2323
use rand::{self, Rng};
24+
use ptr;
2425

2526
use super::table::{self, Bucket, EmptyBucket, FullBucket, FullBucketMut, RawTable, SafeHash};
2627
use super::table::BucketState::{Empty, Full};
@@ -483,7 +484,7 @@ fn robin_hood<'a, K: 'a, V: 'a>(bucket: FullBucketMut<'a, K, V>,
483484
mut hash: SafeHash,
484485
mut key: K,
485486
mut val: V)
486-
-> &'a mut V {
487+
-> FullBucketMut<'a, K, V> {
487488
let start_index = bucket.index();
488489
let size = bucket.table().size();
489490
// Save the *starting point*.
@@ -515,7 +516,7 @@ fn robin_hood<'a, K: 'a, V: 'a>(bucket: FullBucketMut<'a, K, V>,
515516
// bucket, which is a FullBucket on top of a
516517
// FullBucketMut, into just one FullBucketMut. The "table"
517518
// refers to the inner FullBucketMut in this context.
518-
return bucket.into_table().into_mut_refs().1;
519+
return bucket.into_table();
519520
}
520521
Full(bucket) => bucket,
521522
};
@@ -1818,6 +1819,80 @@ impl<'a, K, V> fmt::Debug for Drain<'a, K, V>
18181819
}
18191820
}
18201821

1822+
/// A place for insertion to a `Entry`.
1823+
///
1824+
/// See [`HashMap::entry`](struct.HashMap.html#method.entry) for details.
1825+
#[must_use = "places do nothing unless written to with `<-` syntax"]
1826+
#[unstable(feature = "collection_placement",
1827+
reason = "struct name and placement protocol is subject to change",
1828+
issue = "30172")]
1829+
pub struct EntryPlace<'a, K: 'a, V: 'a> {
1830+
bucket: FullBucketMut<'a, K, V>,
1831+
}
1832+
1833+
#[unstable(feature = "collection_placement",
1834+
reason = "struct name and placement protocol is subject to change",
1835+
issue = "30172")]
1836+
impl<'a, K: 'a + Debug, V: 'a + Debug> Debug for EntryPlace<'a, K, V> {
1837+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1838+
f.debug_struct("EntryPlace")
1839+
.field("key", self.bucket.read().0)
1840+
.field("value", self.bucket.read().1)
1841+
.finish()
1842+
}
1843+
}
1844+
1845+
#[unstable(feature = "collection_placement",
1846+
reason = "struct name and placement protocol is subject to change",
1847+
issue = "30172")]
1848+
impl<'a, K, V> Drop for EntryPlace<'a, K, V> {
1849+
fn drop(&mut self) {
1850+
// Inplacement insertion failed. Only key need to drop.
1851+
// The value is failed to insert into map.
1852+
unsafe { self.bucket.remove_key() };
1853+
}
1854+
}
1855+
1856+
#[unstable(feature = "collection_placement",
1857+
reason = "placement protocol is subject to change",
1858+
issue = "30172")]
1859+
impl<'a, K, V> Placer<V> for Entry<'a, K, V> {
1860+
type Place = EntryPlace<'a, K, V>;
1861+
1862+
fn make_place(self) -> EntryPlace<'a, K, V> {
1863+
let b = match self {
1864+
Occupied(mut o) => {
1865+
unsafe { ptr::drop_in_place(o.elem.read_mut().1); }
1866+
o.elem
1867+
}
1868+
Vacant(v) => {
1869+
unsafe { v.insert_key() }
1870+
}
1871+
};
1872+
EntryPlace { bucket: b }
1873+
}
1874+
}
1875+
1876+
#[unstable(feature = "collection_placement",
1877+
reason = "placement protocol is subject to change",
1878+
issue = "30172")]
1879+
impl<'a, K, V> Place<V> for EntryPlace<'a, K, V> {
1880+
fn pointer(&mut self) -> *mut V {
1881+
self.bucket.read_mut().1
1882+
}
1883+
}
1884+
1885+
#[unstable(feature = "collection_placement",
1886+
reason = "placement protocol is subject to change",
1887+
issue = "30172")]
1888+
impl<'a, K, V> InPlace<V> for EntryPlace<'a, K, V> {
1889+
type Owner = ();
1890+
1891+
unsafe fn finalize(self) {
1892+
mem::forget(self);
1893+
}
1894+
}
1895+
18211896
impl<'a, K, V> Entry<'a, K, V> {
18221897
#[stable(feature = "rust1", since = "1.0.0")]
18231898
/// Ensures a value is in the entry by inserting the default if empty, and returns
@@ -2108,7 +2183,7 @@ impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> {
21082183
/// ```
21092184
#[stable(feature = "rust1", since = "1.0.0")]
21102185
pub fn insert(self, value: V) -> &'a mut V {
2111-
match self.elem {
2186+
let b = match self.elem {
21122187
NeqElem(mut bucket, disp) => {
21132188
if disp >= DISPLACEMENT_THRESHOLD {
21142189
bucket.table_mut().set_tag(true);
@@ -2119,7 +2194,28 @@ impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> {
21192194
if disp >= DISPLACEMENT_THRESHOLD {
21202195
bucket.table_mut().set_tag(true);
21212196
}
2122-
bucket.put(self.hash, self.key, value).into_mut_refs().1
2197+
bucket.put(self.hash, self.key, value)
2198+
},
2199+
};
2200+
b.into_mut_refs().1
2201+
}
2202+
2203+
// Only used for InPlacement insert. Avoid unnecessary value copy.
2204+
// The value remains uninitialized.
2205+
unsafe fn insert_key(self) -> FullBucketMut<'a, K, V> {
2206+
match self.elem {
2207+
NeqElem(mut bucket, disp) => {
2208+
if disp >= DISPLACEMENT_THRESHOLD {
2209+
bucket.table_mut().set_tag(true);
2210+
}
2211+
let uninit = mem::uninitialized();
2212+
robin_hood(bucket, disp, self.hash, self.key, uninit)
2213+
},
2214+
NoElem(mut bucket, disp) => {
2215+
if disp >= DISPLACEMENT_THRESHOLD {
2216+
bucket.table_mut().set_tag(true);
2217+
}
2218+
bucket.put_key(self.hash, self.key)
21232219
},
21242220
}
21252221
}
@@ -2392,6 +2488,7 @@ mod test_map {
23922488
use super::RandomState;
23932489
use cell::RefCell;
23942490
use rand::{thread_rng, Rng};
2491+
use panic;
23952492

23962493
#[test]
23972494
fn test_zero_capacities() {
@@ -3265,4 +3362,57 @@ mod test_map {
32653362
}
32663363
panic!("Adaptive early resize failed");
32673364
}
3365+
3366+
#[test]
3367+
fn test_placement_in() {
3368+
let mut map = HashMap::new();
3369+
map.extend((0..10).map(|i| (i, i)));
3370+
3371+
map.entry(100) <- 100;
3372+
assert_eq!(map[&100], 100);
3373+
3374+
map.entry(0) <- 10;
3375+
assert_eq!(map[&0], 10);
3376+
3377+
assert_eq!(map.len(), 11);
3378+
}
3379+
3380+
#[test]
3381+
fn test_placement_panic() {
3382+
let mut map = HashMap::new();
3383+
map.extend((0..10).map(|i| (i, i)));
3384+
3385+
fn mkpanic() -> usize { panic!() }
3386+
3387+
// modify existing key
3388+
// when panic happens, previous key is removed.
3389+
let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| { map.entry(0) <- mkpanic(); }));
3390+
assert_eq!(map.len(), 9);
3391+
assert!(!map.contains_key(&0));
3392+
3393+
// add new key
3394+
let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| { map.entry(100) <- mkpanic(); }));
3395+
assert_eq!(map.len(), 9);
3396+
assert!(!map.contains_key(&100));
3397+
}
3398+
3399+
#[test]
3400+
fn test_placement_drop() {
3401+
// correctly drop
3402+
struct TestV<'a>(&'a mut bool);
3403+
impl<'a> Drop for TestV<'a> {
3404+
fn drop(&mut self) {
3405+
if !*self.0 { panic!("value double drop!"); } // no double drop
3406+
*self.0 = false;
3407+
}
3408+
}
3409+
3410+
fn makepanic<'a>() -> TestV<'a> { panic!() }
3411+
3412+
let mut can_drop = true;
3413+
let mut hm = HashMap::new();
3414+
hm.insert(0, TestV(&mut can_drop));
3415+
let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| { hm.entry(0) <- makepanic(); }));
3416+
assert_eq!(hm.len(), 0);
3417+
}
32683418
}

src/libstd/collections/hash/table.rs

+27
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,22 @@ impl<K, V, M> EmptyBucket<K, V, M>
506506
table: self.table,
507507
}
508508
}
509+
510+
/// Puts given key, remain value uninitialized.
511+
/// It is only used for inplacement insertion.
512+
pub unsafe fn put_key(mut self, hash: SafeHash, key: K) -> FullBucket<K, V, M> {
513+
*self.raw.hash = hash.inspect();
514+
let pair_mut = self.raw.pair as *mut (K, V);
515+
ptr::write(&mut (*pair_mut).0, key);
516+
517+
self.table.borrow_table_mut().size += 1;
518+
519+
FullBucket {
520+
raw: self.raw,
521+
idx: self.idx,
522+
table: self.table,
523+
}
524+
}
509525
}
510526

511527
impl<K, V, M: Deref<Target = RawTable<K, V>>> FullBucket<K, V, M> {
@@ -581,6 +597,17 @@ impl<'t, K, V> FullBucket<K, V, &'t mut RawTable<K, V>> {
581597
v)
582598
}
583599
}
600+
601+
/// Remove this bucket's `key` from the hashtable.
602+
/// Only used for inplacement insertion.
603+
/// NOTE: `Value` is uninitialized when this function is called, don't try to drop the `Value`.
604+
pub unsafe fn remove_key(&mut self) {
605+
self.table.size -= 1;
606+
607+
*self.raw.hash = EMPTY_BUCKET;
608+
let pair_mut = self.raw.pair as *mut (K, V);
609+
ptr::drop_in_place(&mut (*pair_mut).0); // only drop key
610+
}
584611
}
585612

586613
// This use of `Put` is misleading and restrictive, but safe and sufficient for our use cases

src/libstd/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@
281281
#![feature(panic_unwind)]
282282
#![feature(peek)]
283283
#![feature(placement_in_syntax)]
284+
#![feature(placement_new_protocol)]
284285
#![feature(prelude_import)]
285286
#![feature(pub_restricted)]
286287
#![feature(rand)]

0 commit comments

Comments
 (0)