Skip to content

Commit 7602e52

Browse files
carllerchereem
authored andcommitted
Provide ::entry(K) API
Mirror the equivalent HashMap API. Unfortunately, this implementation is not able to provide the same benefits (reducing hashing) but provides utility in having API parity as well as simplifies in-place entry manipulation.
1 parent 3cefbd6 commit 7602e52

File tree

2 files changed

+259
-29
lines changed

2 files changed

+259
-29
lines changed

src/lib.rs

Lines changed: 227 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,24 @@ impl<K: Hash + Eq, V> LinkedHashMap<K, V> {
117117
}
118118

119119
impl<K, V, S> LinkedHashMap<K, V, S> {
120+
#[inline]
121+
fn detach(&mut self, node: *mut LinkedHashMapEntry<K, V>) {
122+
unsafe {
123+
(*(*node).prev).next = (*node).next;
124+
(*(*node).next).prev = (*node).prev;
125+
}
126+
}
127+
128+
#[inline]
129+
fn attach(&mut self, node: *mut LinkedHashMapEntry<K, V>) {
130+
unsafe {
131+
(*node).next = (*self.head).next;
132+
(*node).prev = self.head;
133+
(*self.head).next = node;
134+
(*(*node).next).prev = node;
135+
}
136+
}
137+
120138
fn clear_free_list(&mut self) {
121139
unsafe {
122140
let mut free = self.free;
@@ -128,6 +146,17 @@ impl<K, V, S> LinkedHashMap<K, V, S> {
128146
self.free = ptr::null_mut();
129147
}
130148
}
149+
150+
fn ensure_guard_node(&mut self) {
151+
if self.head.is_null() {
152+
// allocate the guard node if not present
153+
unsafe {
154+
self.head = Box::into_raw(Box::new(mem::uninitialized()));
155+
(*self.head).next = self.head;
156+
(*self.head).prev = self.head;
157+
}
158+
}
159+
}
131160
}
132161

133162
impl<K: Hash + Eq, V, S: BuildHasher> LinkedHashMap<K, V, S> {
@@ -165,6 +194,42 @@ impl<K: Hash + Eq, V, S: BuildHasher> LinkedHashMap<K, V, S> {
165194
self.clear_free_list();
166195
}
167196

197+
/// Gets the given key's corresponding entry in the map for in-place manipulation.
198+
///
199+
/// # Examples
200+
///
201+
/// ```
202+
/// use linked_hash_map::LinkedHashMap;
203+
///
204+
/// let mut letters = LinkedHashMap::new();
205+
///
206+
/// for ch in "a short treatise on fungi".chars() {
207+
/// let counter = letters.entry(ch).or_insert(0);
208+
/// *counter += 1;
209+
/// }
210+
///
211+
/// assert_eq!(letters[&'s'], 2);
212+
/// assert_eq!(letters[&'t'], 3);
213+
/// assert_eq!(letters[&'u'], 1);
214+
/// assert_eq!(letters.get(&'y'), None);
215+
/// ```
216+
pub fn entry(&mut self, k: K) -> Entry<K, V, S> {
217+
let self_ptr: *mut Self = self;
218+
219+
if let Some(entry) = self.map.get_mut(&KeyRef{k: &k}) {
220+
return Entry::Occupied(OccupiedEntry {
221+
entry: &mut **entry,
222+
map: self_ptr,
223+
marker: marker::PhantomData,
224+
});
225+
}
226+
227+
Entry::Vacant(VacantEntry {
228+
key: k,
229+
map: self,
230+
})
231+
}
232+
168233
/// Inserts a key-value pair into the map. If the key already existed, the old value is
169234
/// returned.
170235
///
@@ -180,14 +245,8 @@ impl<K: Hash + Eq, V, S: BuildHasher> LinkedHashMap<K, V, S> {
180245
/// assert_eq!(map[&2], "b");
181246
/// ```
182247
pub fn insert(&mut self, k: K, v: V) -> Option<V> {
183-
if self.head.is_null() {
184-
// allocate the guard node if not present
185-
unsafe {
186-
self.head = Box::into_raw(Box::new(mem::uninitialized()));
187-
(*self.head).next = self.head;
188-
(*self.head).prev = self.head;
189-
}
190-
}
248+
self.ensure_guard_node();
249+
191250
let (node_ptr, node_opt, old_val) = match self.map.get_mut(&KeyRef{k: &k}) {
192251
Some(node) => {
193252
let old_val = mem::replace(&mut node.value, v);
@@ -592,27 +651,10 @@ impl<'a, K, V, S, Q: ?Sized> IndexMut<&'a Q> for LinkedHashMap<K, V, S>
592651
}
593652
}
594653

595-
impl<K: Hash + Eq, V, S: BuildHasher> LinkedHashMap<K, V, S> {
596-
#[inline]
597-
fn detach(&mut self, node: *mut LinkedHashMapEntry<K, V>) {
598-
unsafe {
599-
(*(*node).prev).next = (*node).next;
600-
(*(*node).next).prev = (*node).prev;
601-
}
602-
}
603-
604-
#[inline]
605-
fn attach(&mut self, node: *mut LinkedHashMapEntry<K, V>) {
606-
unsafe {
607-
(*node).next = (*self.head).next;
608-
(*node).prev = self.head;
609-
(*self.head).next = node;
610-
(*(*node).next).prev = node;
611-
}
612-
}
613-
}
614-
615654
#[cfg(not(feature = "nightly"))]
655+
// FIXME: `HashMap` doesn't expose its hash state, so we cannot clone fully parameterized
656+
// `LinkedHashMap`s without cloning the original map and clearing it. For now, only
657+
// `LinkedHashMap<K, V>`s implement `Clone`.
616658
impl<K: Hash + Eq + Clone, V: Clone> Clone for LinkedHashMap<K, V> {
617659
fn clone(&self) -> Self {
618660
self.iter().map(|(k, v)| (k.clone(), v.clone())).collect()
@@ -895,6 +937,163 @@ impl<'a, K: Hash + Eq, V, S: BuildHasher> IntoIterator for &'a mut LinkedHashMap
895937
fn into_iter(self) -> IterMut<'a, K, V> { self.iter_mut() }
896938
}
897939

940+
/// A view into a single location in a map, which may be vacant or occupied.
941+
pub enum Entry<'a, K: 'a, V: 'a, S: 'a + HashState> {
942+
/// An occupied Entry.
943+
Occupied(OccupiedEntry<'a, K, V, S>),
944+
/// A vacant Entry.
945+
Vacant(VacantEntry<'a, K, V, S>),
946+
}
947+
948+
/// A view into a single occupied location in a LinkedHashMap.
949+
pub struct OccupiedEntry<'a, K: 'a, V: 'a, S: 'a + HashState> {
950+
entry: *mut LinkedHashMapEntry<K, V>,
951+
map: *mut LinkedHashMap<K, V, S>,
952+
marker: marker::PhantomData<&'a K>,
953+
}
954+
955+
/// A view into a single empty location in a HashMap.
956+
pub struct VacantEntry<'a, K: 'a, V: 'a, S: 'a + HashState> {
957+
key: K,
958+
map: &'a mut LinkedHashMap<K, V, S>,
959+
}
960+
961+
impl<'a, K: Hash + Eq, V, S: HashState> Entry<'a, K, V, S> {
962+
/// Returns the entry key
963+
///
964+
/// # Examples
965+
///
966+
/// ```
967+
/// use linked_hash_map::LinkedHashMap;
968+
///
969+
/// let mut map = LinkedHashMap::<String, u32>::new();
970+
///
971+
/// assert_eq!("hello", map.entry("hello".to_string()).key());
972+
/// ```
973+
pub fn key(&self) -> &K {
974+
match *self {
975+
Entry::Occupied(ref e) => e.key(),
976+
Entry::Vacant(ref e) => e.key(),
977+
}
978+
}
979+
980+
/// Ensures a value is in the entry by inserting the default if empty, and returns
981+
/// a mutable reference to the value in the entry.
982+
pub fn or_insert(self, default: V) -> &'a mut V {
983+
match self {
984+
Entry::Occupied(entry) => entry.into_mut(),
985+
Entry::Vacant(entry) => entry.insert(default),
986+
}
987+
}
988+
989+
/// Ensures a value is in the entry by inserting the result of the default function if empty,
990+
/// and returns a mutable reference to the value in the entry.
991+
pub fn or_insert_with<F: FnOnce() -> V>(self, default: F) -> &'a mut V {
992+
match self {
993+
Entry::Occupied(entry) => entry.into_mut(),
994+
Entry::Vacant(entry) => entry.insert(default()),
995+
}
996+
}
997+
}
998+
999+
impl<'a, K: Hash + Eq, V, S: HashState> OccupiedEntry<'a, K, V, S> {
1000+
/// Gets a reference to the entry key
1001+
///
1002+
/// # Examples
1003+
///
1004+
/// ```
1005+
/// use linked_hash_map::LinkedHashMap;
1006+
///
1007+
/// let mut map = LinkedHashMap::new();
1008+
///
1009+
/// map.insert("foo".to_string(), 1);
1010+
/// assert_eq!("foo", map.entry("foo".to_string()).key());
1011+
/// ```
1012+
pub fn key(&self) -> &K {
1013+
unsafe { &(*self.entry).key }
1014+
}
1015+
1016+
/// Gets a reference to the value in the entry.
1017+
pub fn get(&self) -> &V {
1018+
unsafe { &(*self.entry).value }
1019+
}
1020+
1021+
/// Gets a mutable reference to the value in the entry.
1022+
pub fn get_mut(&mut self) -> &mut V {
1023+
unsafe { &mut (*self.entry).value }
1024+
}
1025+
1026+
/// Converts the OccupiedEntry into a mutable reference to the value in the entry
1027+
/// with a lifetime bound to the map itself
1028+
pub fn into_mut(self) -> &'a mut V {
1029+
unsafe { &mut (*self.entry).value }
1030+
}
1031+
1032+
/// Sets the value of the entry, and returns the entry's old value
1033+
pub fn insert(&mut self, value: V) -> V {
1034+
unsafe {
1035+
(*self.map).ensure_guard_node();
1036+
1037+
let old_val = mem::replace(&mut (*self.entry).value, value);
1038+
let node_ptr: *mut LinkedHashMapEntry<K, V> = self.entry;
1039+
1040+
// Existing node, just update LRU position
1041+
(*self.map).detach(node_ptr);
1042+
(*self.map).attach(node_ptr);
1043+
1044+
old_val
1045+
}
1046+
}
1047+
1048+
/// Takes the value out of the entry, and returns it
1049+
pub fn remove(self) -> V {
1050+
unsafe { (*self.map).remove(&(*self.entry).key) }.unwrap()
1051+
}
1052+
}
1053+
1054+
impl<'a, K: 'a + Hash + Eq, V: 'a, S: HashState> VacantEntry<'a, K, V, S> {
1055+
/// Gets a reference to the entry key
1056+
///
1057+
/// # Examples
1058+
///
1059+
/// ```
1060+
/// use linked_hash_map::LinkedHashMap;
1061+
///
1062+
/// let mut map = LinkedHashMap::<String, u32>::new();
1063+
///
1064+
/// assert_eq!("foo", map.entry("foo".to_string()).key());
1065+
/// ```
1066+
pub fn key(&self) -> &K {
1067+
&self.key
1068+
}
1069+
1070+
/// Sets the value of the entry with the VacantEntry's key,
1071+
/// and returns a mutable reference to it
1072+
pub fn insert(self, value: V) -> &'a mut V {
1073+
self.map.ensure_guard_node();
1074+
1075+
let mut node = if self.map.free.is_null() {
1076+
Box::new(LinkedHashMapEntry::new(self.key, value))
1077+
} else {
1078+
// use a recycled box
1079+
unsafe {
1080+
let free = self.map.free;
1081+
self.map.free = (*free).next;
1082+
ptr::write(free, LinkedHashMapEntry::new(self.key, value));
1083+
Box::from_raw(free)
1084+
}
1085+
};
1086+
1087+
let node_ptr: *mut LinkedHashMapEntry<K, V> = &mut *node;
1088+
let keyref = unsafe { &(*node_ptr).key };
1089+
1090+
self.map.attach(node_ptr);
1091+
1092+
&mut self.map.map.entry(KeyRef{k: keyref})
1093+
.or_insert(node).value
1094+
}
1095+
}
1096+
8981097
#[cfg(all(feature = "nightly", test))]
8991098
mod bench {
9001099
extern crate test;

tests/test.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
extern crate linked_hash_map;
22

3-
use linked_hash_map::LinkedHashMap;
3+
use linked_hash_map::{LinkedHashMap, Entry};
44

55
fn assert_opt_eq<V: PartialEq>(opt: Option<&V>, v: V) {
66
assert!(opt.is_some());
@@ -36,6 +36,37 @@ fn test_insert_update() {
3636
assert_eq!(map.len(), 1);
3737
}
3838

39+
#[test]
40+
fn test_entry_insert_vacant() {
41+
let mut map = LinkedHashMap::new();
42+
match map.entry("1".to_string()) {
43+
Entry::Vacant(e) => {
44+
assert_eq!(*e.insert(vec![10, 10]), vec![10, 10]);
45+
}
46+
_ => panic!("fail"),
47+
}
48+
assert!(map.contains_key("1"));
49+
assert_eq!(map["1"], vec![10, 10]);
50+
51+
match map.entry("1".to_string()) {
52+
Entry::Occupied(mut e) => {
53+
assert_eq!(*e.get(), vec![10, 10]);
54+
assert_eq!(e.insert(vec![10, 16]), vec![10, 10]);
55+
}
56+
_ => panic!("fail"),
57+
}
58+
59+
assert!(map.contains_key("1"));
60+
assert_eq!(map["1"], vec![10, 16]);
61+
62+
match map.entry("1".to_string()) {
63+
Entry::Occupied(e) => {
64+
assert_eq!(e.remove(), vec![10, 16]);
65+
}
66+
_ => panic!("fail"),
67+
}
68+
}
69+
3970
#[test]
4071
fn test_debug() {
4172
let mut map = LinkedHashMap::new();

0 commit comments

Comments
 (0)