Skip to content

Commit 361ec43

Browse files
committed
implement Entry API for SmallIntMap
1 parent dd71136 commit 361ec43

File tree

1 file changed

+144
-2
lines changed

1 file changed

+144
-2
lines changed

src/libcollections/smallintmap.rs

+144-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ use core::default::Default;
1919
use core::fmt;
2020
use core::iter;
2121
use core::iter::{Enumerate, FilterMap};
22-
use core::mem::replace;
22+
use core::mem::{replace, swap};
23+
use core::kinds::marker::ContravariantLifetime;
2324

2425
use {Mutable, Map, MutableMap, MutableSeq};
2526
use {vec, slice};
@@ -295,6 +296,19 @@ impl<V> SmallIntMap<V> {
295296
v.map(|v| (i, v))
296297
})
297298
}
299+
300+
/// Gets the given key's corresponding entry in the map for in-place manipulation
301+
pub fn entry<'a>(&'a mut self, key: uint) -> Entry<'a, V> {
302+
let self_ptr = self as *mut _;
303+
match self.find_mut(&key) {
304+
// FIXME: So, this is absolutely awful. We absolutely *do not* need a raw ptr for
305+
// VacantEntry, but borrowck thinks that self is borrowed in both this None branch,
306+
// and after the match if we return in the Some branch. If anyone knows how to work
307+
// around this, *please do*.
308+
None => Vacant(VacantEntry{ map: self_ptr, key: key, marker: ContravariantLifetime }),
309+
Some(val) => Occupied(OccupiedEntry{ map: self_ptr, key: key, val: val }),
310+
}
311+
}
298312
}
299313

300314
impl<V:Clone> SmallIntMap<V> {
@@ -494,14 +508,90 @@ pub type Keys<'a, T> =
494508
pub type Values<'a, T> =
495509
iter::Map<'static, (uint, &'a T), &'a T, Entries<'a, T>>;
496510

511+
/// A view into a single occupied location in a SmallIntMap
512+
pub struct OccupiedEntry<'a, V:'a> {
513+
key: uint,
514+
val: &'a mut V,
515+
// We only need this for `take`. Should never be null, and we'll only use it when
516+
// we we'll never touch `val` again. Totally safe, just lame that we need this.
517+
// The alternative would be doing the indexing on every op, which would make this API
518+
// *worse* than useless. This way it's *only* useless.
519+
map: *mut SmallIntMap<V>,
520+
}
521+
522+
/// A view into a single empty location in a SmallIntMap
523+
pub struct VacantEntry<'a, V:'a> {
524+
// See the FIXME in `entry` for why this mess happened
525+
map: *mut SmallIntMap<V>,
526+
key: uint,
527+
marker: ContravariantLifetime<'a>,
528+
}
529+
530+
/// A view into a single location in a map, which may be vacant or occupied
531+
pub enum Entry<'a, V:'a> {
532+
/// An occupied Entry
533+
Occupied(OccupiedEntry<'a, V>),
534+
/// A vacant Entry
535+
Vacant(VacantEntry<'a, V>),
536+
}
537+
538+
impl<'a, V> OccupiedEntry<'a, V> {
539+
/// Gets a reference to the value in the entry
540+
pub fn get(&self) -> &V {
541+
&*self.val
542+
}
543+
544+
/// Gets a mutable reference to the value in the entry
545+
pub fn get_mut(&mut self) -> &mut V {
546+
self.val
547+
}
548+
549+
/// Converts the OccupiedEntry into a mutable reference to the value in the entry
550+
/// with a lifetime bound to the map itself
551+
pub fn into_mut(self) -> &'a mut V {
552+
self.val
553+
}
554+
555+
/// Sets the value of the entry, and returns the entry's old value
556+
pub fn set(&mut self, mut value: V) -> V {
557+
swap(&mut value, self.val);
558+
value
559+
}
560+
561+
/// Takes the value out of the entry, and returns it
562+
pub fn take(self) -> V {
563+
// This is pretty much as effecient as this can be, short of storing *another* raw ptr
564+
// to the option, so that we can `None` it out directly, and then decrement the map's
565+
// length directly. That would be... awful.
566+
unsafe {
567+
(*self.map).pop(&self.key).unwrap()
568+
}
569+
}
570+
}
571+
572+
impl<'a, V> VacantEntry<'a, V> {
573+
/// Sets the value of the entry with the VacantEntry's key,
574+
/// and returns a mutable reference to it
575+
pub fn set(self, value: V) -> &'a mut V {
576+
// This is moderately inefficient because we do two indexing operations, where we could
577+
// only do one. However insert handles all the growing and length logic for us,
578+
// and this API is already pretty silly on SmallIntMap. Not worth a big refactor over.
579+
//
580+
// There isn't any clear way to avoid an `unwrap` of the underlying option, regardless.
581+
let map = unsafe { &mut *self.map };
582+
map.insert(self.key, value);
583+
map.find_mut(&self.key).unwrap()
584+
}
585+
}
586+
497587
#[cfg(test)]
498588
mod test_map {
499589
use std::prelude::*;
500590
use vec::Vec;
501591
use hash;
502592

503593
use {Map, MutableMap, Mutable, MutableSeq};
504-
use super::SmallIntMap;
594+
use super::{SmallIntMap, Occupied, Vacant};
505595

506596
#[test]
507597
fn test_find_mut() {
@@ -863,6 +953,58 @@ mod test_map {
863953

864954
map[4];
865955
}
956+
957+
#[test]
958+
fn test_entry(){
959+
let xs = [(1i, 10i), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)];
960+
961+
let mut map: SmallIntMap<int> = xs.iter().map(|&x| x).collect();
962+
963+
// Existing key (insert)
964+
match map.entry(1) {
965+
Vacant(_) => unreachable!(),
966+
Occupied(mut view) => {
967+
assert_eq!(view.get(), &10);
968+
assert_eq!(view.set(100), 10);
969+
}
970+
}
971+
assert_eq!(map.find(&1).unwrap(), &100);
972+
assert_eq!(map.len(), 6);
973+
974+
975+
// Existing key (update)
976+
match map.entry(2) {
977+
Vacant(_) => unreachable!(),
978+
Occupied(mut view) => {
979+
let v = view.get_mut();
980+
let new_v = (*v) * 10;
981+
*v = new_v;
982+
}
983+
}
984+
assert_eq!(map.find(&2).unwrap(), &200);
985+
assert_eq!(map.len(), 6);
986+
987+
// Existing key (take)
988+
match map.entry(3) {
989+
Vacant(_) => unreachable!(),
990+
Occupied(view) => {
991+
assert_eq!(view.take(), 30);
992+
}
993+
}
994+
assert_eq!(map.find(&3), None);
995+
assert_eq!(map.len(), 5);
996+
997+
998+
// Inexistent key (insert)
999+
match map.entry(10) {
1000+
Occupied(_) => unreachable!(),
1001+
Vacant(view) => {
1002+
assert_eq!(*view.set(1000), 1000);
1003+
}
1004+
}
1005+
assert_eq!(map.find(&10).unwrap(), &1000);
1006+
assert_eq!(map.len(), 6);
1007+
}
8661008
}
8671009

8681010
#[cfg(test)]

0 commit comments

Comments
 (0)