Skip to content

implement RFC 1194 #28043

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 29, 2015
Merged
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
101 changes: 92 additions & 9 deletions src/libcollections/btree/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ impl<K: Ord, V> BTreeMap<K, V> {
}
});
match result {
Finished(ret) => return ret,
Finished(ret) => return ret.map(|(_, v)| v),
Continue(new_stack) => stack = new_stack
}
}
Expand Down Expand Up @@ -693,16 +693,16 @@ mod stack {
impl<'a, K, V> SearchStack<'a, K, V, handle::KV, handle::Leaf> {
/// Removes the key and value in the top element of the stack, then handles underflows as
/// described in BTree's pop function.
fn remove_leaf(mut self) -> V {
fn remove_leaf(mut self) -> (K, V) {
self.map.length -= 1;

// Remove the key-value pair from the leaf that this search stack points to.
// Then, note if the leaf is underfull, and promptly forget the leaf and its ptr
// to avoid ownership issues.
let (value, mut underflow) = unsafe {
let (_, value) = self.top.from_raw_mut().remove_as_leaf();
let (key_val, mut underflow) = unsafe {
let key_val = self.top.from_raw_mut().remove_as_leaf();
let underflow = self.top.from_raw().node().is_underfull();
(value, underflow)
(key_val, underflow)
};

loop {
Expand All @@ -717,7 +717,7 @@ mod stack {
self.map.depth -= 1;
self.map.root.hoist_lone_child();
}
return value;
return key_val;
}
Some(mut handle) => {
if underflow {
Expand All @@ -728,7 +728,7 @@ mod stack {
}
} else {
// All done!
return value;
return key_val;
}
}
}
Expand All @@ -739,7 +739,7 @@ mod stack {
impl<'a, K, V> SearchStack<'a, K, V, handle::KV, handle::LeafOrInternal> {
/// Removes the key and value in the top element of the stack, then handles underflows as
/// described in BTree's pop function.
pub fn remove(self) -> V {
pub fn remove(self) -> (K, V) {
// Ensure that the search stack goes to a leaf. This is necessary to perform deletion
// in a BTree. Note that this may put the tree in an inconsistent state (further
// described in into_leaf's comments), but this is immediately fixed by the
Expand Down Expand Up @@ -1208,7 +1208,7 @@ impl<'a, K: Ord, V> OccupiedEntry<'a, K, V> {
/// Takes the value of the entry out of the map, and returns it.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn remove(self) -> V {
self.stack.remove()
self.stack.remove().1
}
}

Expand Down Expand Up @@ -1609,3 +1609,86 @@ impl<K: Ord, V> BTreeMap<K, V> {
}
}
}

impl<K, Q: ?Sized> super::Recover<Q> for BTreeMap<K, ()> where K: Borrow<Q> + Ord, Q: Ord {
type Key = K;

fn get(&self, key: &Q) -> Option<&K> {
let mut cur_node = &self.root;
loop {
match Node::search(cur_node, key) {
Found(handle) => return Some(handle.into_kv().0),
GoDown(handle) => match handle.force() {
Leaf(_) => return None,
Internal(internal_handle) => {
cur_node = internal_handle.into_edge();
continue;
Copy link
Member

Choose a reason for hiding this comment

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

I think this continue can be omitted

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is actually copied from BTreeMap::get, so I opted to keep the code as similar as possible.

}
}
}
}
}

fn take(&mut self, key: &Q) -> Option<K> {
// See `remove` for an explanation of this.

let mut stack = stack::PartialSearchStack::new(self);
loop {
let result = stack.with(move |pusher, node| {
match Node::search(node, key) {
Found(handle) => {
// Perfect match. Terminate the stack here, and remove the entry
Finished(Some(pusher.seal(handle).remove()))
},
GoDown(handle) => {
// We need to keep searching, try to go down the next edge
match handle.force() {
// We're at a leaf; the key isn't in here
Leaf(_) => Finished(None),
Internal(internal_handle) => Continue(pusher.push(internal_handle))
}
}
}
});
match result {
Finished(ret) => return ret.map(|(k, _)| k),
Continue(new_stack) => stack = new_stack
}
}
}

fn replace(&mut self, mut key: K) -> Option<K> {
// See `insert` for an explanation of this.

let mut stack = stack::PartialSearchStack::new(self);

loop {
let result = stack.with(move |pusher, node| {
match Node::search::<K, _>(node, &key) {
Found(mut handle) => {
mem::swap(handle.key_mut(), &mut key);
Finished(Some(key))
},
GoDown(handle) => {
match handle.force() {
Leaf(leaf_handle) => {
pusher.seal(leaf_handle).insert(key, ());
Finished(None)
}
Internal(internal_handle) => {
Continue((pusher.push(internal_handle), key, ()))
}
}
}
}
});
match result {
Finished(ret) => return ret,
Continue((new_stack, renewed_key, _)) => {
stack = new_stack;
key = renewed_key;
}
}
}
}
}
8 changes: 8 additions & 0 deletions src/libcollections/btree/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,11 @@
mod node;
pub mod map;
pub mod set;

trait Recover<Q: ?Sized> {
type Key;

fn get(&self, key: &Q) -> Option<&Self::Key>;
fn take(&mut self, key: &Q) -> Option<Self::Key>;
fn replace(&mut self, key: Self::Key) -> Option<Self::Key>;
}
28 changes: 28 additions & 0 deletions src/libcollections/btree/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use core::ops::{BitOr, BitAnd, BitXor, Sub};

use borrow::Borrow;
use btree_map::{BTreeMap, Keys};
use super::Recover;
use Bound;

// FIXME(conventions): implement bounded iterators
Expand Down Expand Up @@ -329,6 +330,16 @@ impl<T: Ord> BTreeSet<T> {
self.map.contains_key(value)
}

/// Returns a reference to the value in the set, if any, that is equal to the given value.
///
/// The value may be any borrowed form of the set's value type,
/// but the ordering on the borrowed form *must* match the
/// ordering on the value type.
#[unstable(feature = "set_recovery", issue = "28050")]
pub fn get<Q: ?Sized>(&self, value: &Q) -> Option<&T> where T: Borrow<Q>, Q: Ord {
Recover::get(&self.map, value)
}

/// Returns `true` if the set has no elements in common with `other`.
/// This is equivalent to checking for an empty intersection.
///
Expand Down Expand Up @@ -436,6 +447,13 @@ impl<T: Ord> BTreeSet<T> {
self.map.insert(value, ()).is_none()
}

/// Adds a value to the set, replacing the existing value, if any, that is equal to the given
/// one. Returns the replaced value.
#[unstable(feature = "set_recovery", issue = "28050")]
pub fn replace(&mut self, value: T) -> Option<T> {
Recover::replace(&mut self.map, value)
}

/// Removes a value from the set. Returns `true` if the value was
/// present in the set.
///
Expand All @@ -458,6 +476,16 @@ impl<T: Ord> BTreeSet<T> {
pub fn remove<Q: ?Sized>(&mut self, value: &Q) -> bool where T: Borrow<Q>, Q: Ord {
self.map.remove(value).is_some()
}

/// Removes and returns the value in the set, if any, that is equal to the given one.
///
/// The value may be any borrowed form of the set's value type,
/// but the ordering on the borrowed form *must* match the
/// ordering on the value type.
#[unstable(feature = "set_recovery", issue = "28050")]
pub fn take<Q: ?Sized>(&mut self, value: &Q) -> Option<T> where T: Borrow<Q>, Q: Ord {
Recover::take(&mut self.map, value)
}
}

#[stable(feature = "rust1", since = "1.0.0")]
Expand Down
49 changes: 49 additions & 0 deletions src/libcollectionstest/btree/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,3 +211,52 @@ fn test_extend_ref() {
assert!(a.contains(&5));
assert!(a.contains(&6));
}

#[test]
fn test_recovery() {
use std::cmp::Ordering;

#[derive(Debug)]
struct Foo(&'static str, i32);

impl PartialEq for Foo {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}

impl Eq for Foo {}

impl PartialOrd for Foo {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.0.partial_cmp(&other.0)
}
}

impl Ord for Foo {
fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(&other.0)
}
}

let mut s = BTreeSet::new();
assert_eq!(s.replace(Foo("a", 1)), None);
assert_eq!(s.len(), 1);
assert_eq!(s.replace(Foo("a", 2)), Some(Foo("a", 1)));
assert_eq!(s.len(), 1);

{
let mut it = s.iter();
assert_eq!(it.next(), Some(&Foo("a", 2)));
assert_eq!(it.next(), None);
}

assert_eq!(s.get(&Foo("a", 1)), Some(&Foo("a", 2)));
assert_eq!(s.take(&Foo("a", 1)), Some(Foo("a", 2)));
assert_eq!(s.len(), 0);

assert_eq!(s.get(&Foo("a", 1)), None);
assert_eq!(s.take(&Foo("a", 1)), None);

assert_eq!(s.iter().next(), None);
}
1 change: 1 addition & 0 deletions src/libcollectionstest/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#![feature(rand)]
#![feature(range_inclusive)]
#![feature(rustc_private)]
#![feature(set_recovery)]
#![feature(slice_bytes)]
#![feature(slice_splits)]
#![feature(split_off)]
Expand Down
37 changes: 33 additions & 4 deletions src/libstd/collections/hash/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,7 @@ impl<K, V, S> HashMap<K, V, S>
/// If the key already exists, the hashtable will be returned untouched
/// and a reference to the existing element will be returned.
fn insert_hashed_nocheck(&mut self, hash: SafeHash, k: K, v: V) -> &mut V {
self.insert_or_replace_with(hash, k, v, |_, _, _| ())
self.insert_or_replace_with(hash, k, v, |_, _, _, _| ())
}

fn insert_or_replace_with<'a, F>(&'a mut self,
Expand All @@ -778,7 +778,7 @@ impl<K, V, S> HashMap<K, V, S>
v: V,
mut found_existing: F)
-> &'a mut V where
F: FnMut(&mut K, &mut V, V),
F: FnMut(&mut K, &mut V, K, V),
{
// Worst case, we'll find one empty bucket among `size + 1` buckets.
let size = self.table.size();
Expand All @@ -801,7 +801,7 @@ impl<K, V, S> HashMap<K, V, S>
let (bucket_k, bucket_v) = bucket.into_mut_refs();
debug_assert!(k == *bucket_k);
// Key already exists. Get its reference.
found_existing(bucket_k, bucket_v, v);
found_existing(bucket_k, bucket_v, k, v);
return bucket_v;
}
}
Expand Down Expand Up @@ -1123,7 +1123,7 @@ impl<K, V, S> HashMap<K, V, S>
self.reserve(1);

let mut retval = None;
self.insert_or_replace_with(hash, k, v, |_, val_ref, val| {
self.insert_or_replace_with(hash, k, v, |_, val_ref, _, val| {
retval = Some(replace(val_ref, val));
});
retval
Expand Down Expand Up @@ -1630,6 +1630,35 @@ impl Default for RandomState {
}
}

impl<K, S, Q: ?Sized> super::Recover<Q> for HashMap<K, (), S>
where K: Eq + Hash + Borrow<Q>, S: HashState, Q: Eq + Hash
{
type Key = K;

fn get(&self, key: &Q) -> Option<&K> {
self.search(key).map(|bucket| bucket.into_refs().0)
}

fn take(&mut self, key: &Q) -> Option<K> {
if self.table.size() == 0 {
return None
}

self.search_mut(key).map(|bucket| pop_internal(bucket).0)
}

fn replace(&mut self, key: K) -> Option<K> {
let hash = self.make_hash(&key);
self.reserve(1);

let mut retkey = None;
self.insert_or_replace_with(hash, key, (), |key_ref, _, key, _| {
retkey = Some(replace(key_ref, key));
});
retkey
}
}

#[cfg(test)]
mod test_map {
use prelude::v1::*;
Expand Down
8 changes: 8 additions & 0 deletions src/libstd/collections/hash/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,11 @@ mod table;
pub mod map;
pub mod set;
pub mod state;

trait Recover<Q: ?Sized> {
type Key;

fn get(&self, key: &Q) -> Option<&Self::Key>;
fn take(&mut self, key: &Q) -> Option<Self::Key>;
fn replace(&mut self, key: Self::Key) -> Option<Self::Key>;
}
Loading