Skip to content

Commit 4e436d1

Browse files
committed
implement Entry API for SmallIntMap
1 parent a2e7c4d commit 4e436d1

File tree

1 file changed

+144
-2
lines changed

1 file changed

+144
-2
lines changed

src/libcollections/smallintmap.rs

Lines changed: 144 additions & 2 deletions
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};
@@ -332,6 +333,19 @@ impl<V> SmallIntMap<V> {
332333
v.map(|v| (i, v))
333334
})
334335
}
336+
337+
/// Gets the given key's corresponding entry in the map for in-place manipulation
338+
pub fn entry<'a>(&'a mut self, key: uint) -> Entry<'a, V> {
339+
let self_ptr = self as *mut _;
340+
match self.find_mut(&key) {
341+
// FIXME: So, this is absolutely awful. We absolutely *do not* need a raw ptr for
342+
// VacantEntry, but borrowck thinks that self is borrowed in both this None branch,
343+
// and after the match if we return in the Some branch. If anyone knows how to work
344+
// around this, *please do*.
345+
None => Vacant(VacantEntry{ map: self_ptr, key: key, marker: ContravariantLifetime }),
346+
Some(val) => Occupied(OccupiedEntry{ map: self_ptr, key: key, val: val }),
347+
}
348+
}
335349
}
336350

337351
impl<V:Clone> SmallIntMap<V> {
@@ -532,14 +546,90 @@ pub type Keys<'a, T> =
532546
pub type Values<'a, T> =
533547
iter::Map<'static, (uint, &'a T), &'a T, Entries<'a, T>>;
534548

549+
/// A view into a single occupied location in a SmallIntMap
550+
pub struct OccupiedEntry<'a, V:'a> {
551+
key: uint,
552+
val: &'a mut V,
553+
// We only need this for `take`. Should never be null, and we'll only use it when
554+
// we we'll never touch `val` again. Totally safe, just lame that we need this.
555+
// The alternative would be doing the indexing on every op, which would make this API
556+
// *worse* than useless. This way it's *only* useless.
557+
map: *mut SmallIntMap<V>,
558+
}
559+
560+
/// A view into a single empty location in a SmallIntMap
561+
pub struct VacantEntry<'a, V:'a> {
562+
// See the FIXME in `entry` for why this mess happened
563+
map: *mut SmallIntMap<V>,
564+
key: uint,
565+
marker: ContravariantLifetime<'a>,
566+
}
567+
568+
/// A view into a single location in a map, which may be vacant or occupied
569+
pub enum Entry<'a, V:'a> {
570+
/// An occupied Entry
571+
Occupied(OccupiedEntry<'a, V>),
572+
/// A vacant Entry
573+
Vacant(VacantEntry<'a, V>),
574+
}
575+
576+
impl<'a, V> OccupiedEntry<'a, V> {
577+
/// Gets a reference to the value in the entry
578+
pub fn get(&self) -> &V {
579+
&*self.val
580+
}
581+
582+
/// Gets a mutable reference to the value in the entry
583+
pub fn get_mut(&mut self) -> &mut V {
584+
self.val
585+
}
586+
587+
/// Converts the OccupiedEntry into a mutable reference to the value in the entry
588+
/// with a lifetime bound to the map itself
589+
pub fn into_mut(self) -> &'a mut V {
590+
self.val
591+
}
592+
593+
/// Sets the value of the entry, and returns the entry's old value
594+
pub fn set(&mut self, mut value: V) -> V {
595+
swap(&mut value, self.val);
596+
value
597+
}
598+
599+
/// Takes the value out of the entry, and returns it
600+
pub fn take(self) -> V {
601+
// This is pretty much as effecient as this can be, short of storing *another* raw ptr
602+
// to the option, so that we can `None` it out directly, and then decrement the map's
603+
// length directly. That would be... awful.
604+
unsafe {
605+
(*self.map).pop(&self.key).unwrap()
606+
}
607+
}
608+
}
609+
610+
impl<'a, V> VacantEntry<'a, V> {
611+
/// Sets the value of the entry with the VacantEntry's key,
612+
/// and returns a mutable reference to it
613+
pub fn set(self, value: V) -> &'a mut V {
614+
// This is moderately inefficient because we do two indexing operations, where we could
615+
// only do one. However insert handles all the growing and length logic for us,
616+
// and this API is already pretty silly on SmallIntMap. Not worth a big refactor over.
617+
//
618+
// There isn't any clear way to avoid an `unwrap` of the underlying option, regardless.
619+
let map = unsafe { &mut *self.map };
620+
map.insert(self.key, value);
621+
map.find_mut(&self.key).unwrap()
622+
}
623+
}
624+
535625
#[cfg(test)]
536626
mod test_map {
537627
use std::prelude::*;
538628
use vec::Vec;
539629
use hash;
540630

541631
use {Map, MutableMap, Mutable, MutableSeq};
542-
use super::SmallIntMap;
632+
use super::{SmallIntMap, OccupiedEntry, VacantEntry};
543633

544634
#[test]
545635
fn test_find_mut() {
@@ -901,6 +991,58 @@ mod test_map {
901991

902992
map[4];
903993
}
994+
995+
#[test]
996+
fn test_entry(){
997+
let xs = [(1i, 10i), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)];
998+
999+
let mut map: SmallIntMap<int> = xs.iter().map(|&x| x).collect();
1000+
1001+
// Existing key (insert)
1002+
match map.entry(1) {
1003+
Vacant(_) => unreachable!(),
1004+
Occupied(mut view) => {
1005+
assert_eq!(view.get(), &10);
1006+
assert_eq!(view.set(100), 10);
1007+
}
1008+
}
1009+
assert_eq!(map.find(&1).unwrap(), &100);
1010+
assert_eq!(map.len(), 6);
1011+
1012+
1013+
// Existing key (update)
1014+
match map.entry(2) {
1015+
Vacant(_) => unreachable!(),
1016+
Occupied(mut view) => {
1017+
let v = view.get_mut();
1018+
let new_v = (*v) * 10;
1019+
*v = new_v;
1020+
}
1021+
}
1022+
assert_eq!(map.find(&2).unwrap(), &200);
1023+
assert_eq!(map.len(), 6);
1024+
1025+
// Existing key (take)
1026+
match map.entry(3) {
1027+
Vacant(_) => unreachable!(),
1028+
Occupied(view) => {
1029+
assert_eq!(view.take(), 30);
1030+
}
1031+
}
1032+
assert_eq!(map.find(&3), None);
1033+
assert_eq!(map.len(), 5);
1034+
1035+
1036+
// Inexistent key (insert)
1037+
match map.entry(10) {
1038+
Occupied(_) => unreachable!(),
1039+
Vacant(view) => {
1040+
assert_eq!(*view.set(1000), 1000);
1041+
}
1042+
}
1043+
assert_eq!(map.find(&10).unwrap(), &1000);
1044+
assert_eq!(map.len(), 6);
1045+
}
9041046
}
9051047

9061048
#[cfg(test)]

0 commit comments

Comments
 (0)