Skip to content

Commit dfeb3dd

Browse files
authored
Merge pull request #577 from mkj/dev/linearentry
Add LinearMap entry() and retain()
2 parents 5ca8839 + ca12838 commit dfeb3dd

File tree

2 files changed

+348
-1
lines changed

2 files changed

+348
-1
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
2929
- Minor fixes to `pool::boxed` docs.
3030
- Add missing `Debug` derive to `vec::IntoIter`.
3131
- Removed generic from `spsc::Consumer`, `spsc::Producer` and `spsc::Iter`.
32+
- Added `LinearMap::entry()` API.
33+
- Added `LinearMap::retain()`.
3234

3335
### Fixed
3436

src/linear_map.rs

Lines changed: 346 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,50 @@ where
475475
pub fn values_mut(&mut self) -> impl Iterator<Item = &mut V> {
476476
self.iter_mut().map(|(_, v)| v)
477477
}
478+
479+
/// Returns an entry for the corresponding key
480+
/// ```
481+
/// use heapless::linear_map;
482+
/// use heapless::LinearMap;
483+
/// let mut map = LinearMap::<_, _, 16>::new();
484+
/// if let linear_map::Entry::Vacant(v) = map.entry("a") {
485+
/// v.insert(1).unwrap();
486+
/// }
487+
/// if let linear_map::Entry::Occupied(mut o) = map.entry("a") {
488+
/// println!("found {}", *o.get()); // Prints 1
489+
/// o.insert(2);
490+
/// }
491+
/// // Prints 2
492+
/// println!("val: {}", *map.get("a").unwrap());
493+
/// ```
494+
pub fn entry(&mut self, key: K) -> Entry<'_, K, V> {
495+
let idx = self
496+
.keys()
497+
.enumerate()
498+
.find(|&(_, k)| *k.borrow() == key)
499+
.map(|(idx, _)| idx);
500+
501+
match idx {
502+
Some(idx) => Entry::Occupied(OccupiedEntry {
503+
idx,
504+
map: self.as_mut_view(),
505+
}),
506+
None => Entry::Vacant(VacantEntry {
507+
key,
508+
map: self.as_mut_view(),
509+
}),
510+
}
511+
}
512+
513+
/// Retains only the elements specified by the predicate.
514+
///
515+
/// In other words, remove all pairs `(k, v)` for which `f(&k, &mut v)` returns `false`.
516+
pub fn retain<F>(&mut self, mut f: F)
517+
where
518+
F: FnMut(&K, &mut V) -> bool,
519+
{
520+
self.buffer.retain_mut(|(k, v)| f(k, v));
521+
}
478522
}
479523

480524
impl<K, V, Q, S: LinearMapStorage<K, V> + ?Sized> ops::Index<&'_ Q> for LinearMapInner<K, V, S>
@@ -643,11 +687,111 @@ where
643687
{
644688
}
645689

690+
/// A view into an entry in the map
691+
pub enum Entry<'a, K, V> {
692+
/// The entry corresponding to the key `K` exists in the map
693+
Occupied(OccupiedEntry<'a, K, V>),
694+
/// The entry corresponding to the key `K` does not exist in the map
695+
Vacant(VacantEntry<'a, K, V>),
696+
}
697+
698+
/// An occupied entry which can be manipulated
699+
pub struct OccupiedEntry<'a, K, V> {
700+
// SAFETY: `idx` must not be modified after construction, and
701+
// the size of `map` must not be changed.
702+
idx: usize,
703+
map: &'a mut LinearMapView<K, V>,
704+
}
705+
706+
impl<'a, K, V> OccupiedEntry<'a, K, V>
707+
where
708+
K: Eq,
709+
{
710+
/// Gets a reference to the key that this entity corresponds to
711+
pub fn key(&self) -> &K {
712+
// SAFETY: Valid idx from OccupiedEntry construction
713+
let (k, _v) = unsafe { self.map.buffer.get_unchecked(self.idx) };
714+
k
715+
}
716+
717+
/// Removes this entry from the map and yields its corresponding key and value
718+
pub fn remove_entry(self) -> (K, V) {
719+
// SAFETY: Valid idx from OccupiedEntry construction
720+
unsafe { self.map.buffer.swap_remove_unchecked(self.idx) }
721+
}
722+
723+
/// Removes this entry from the map and yields its corresponding key and value
724+
pub fn remove(self) -> V {
725+
self.remove_entry().1
726+
}
727+
728+
/// Gets a reference to the value associated with this entry
729+
pub fn get(&self) -> &V {
730+
// SAFETY: Valid idx from OccupiedEntry construction
731+
let (_k, v) = unsafe { self.map.buffer.get_unchecked(self.idx) };
732+
v
733+
}
734+
735+
/// Gets a mutable reference to the value associated with this entry
736+
pub fn get_mut(&mut self) -> &mut V {
737+
// SAFETY: Valid idx from OccupiedEntry construction
738+
let (_k, v) = unsafe { self.map.buffer.get_unchecked_mut(self.idx) };
739+
v
740+
}
741+
742+
/// Consumes this entry and yields a reference to the underlying value
743+
pub fn into_mut(self) -> &'a mut V {
744+
// SAFETY: Valid idx from OccupiedEntry construction
745+
let (_k, v) = unsafe { self.map.buffer.get_unchecked_mut(self.idx) };
746+
v
747+
}
748+
749+
/// Overwrites the underlying map's value with this entry's value
750+
pub fn insert(self, value: V) -> V {
751+
// SAFETY: Valid idx from OccupiedEntry construction
752+
let (_k, v) = unsafe { self.map.buffer.get_unchecked_mut(self.idx) };
753+
mem::replace(v, value)
754+
}
755+
}
756+
757+
/// A view into an empty slot in the underlying map
758+
pub struct VacantEntry<'a, K, V> {
759+
key: K,
760+
map: &'a mut LinearMapView<K, V>,
761+
}
762+
763+
impl<'a, K, V> VacantEntry<'a, K, V>
764+
where
765+
K: Eq,
766+
{
767+
/// Get the key associated with this entry
768+
pub fn key(&self) -> &K {
769+
&self.key
770+
}
771+
772+
/// Consumes this entry to yield to key associated with it
773+
pub fn into_key(self) -> K {
774+
self.key
775+
}
776+
777+
/// Inserts this entry into to underlying map, yields a mutable reference to the inserted value.
778+
/// If the map is at capacity the value is returned instead.
779+
pub fn insert(self, value: V) -> Result<&'a mut V, V> {
780+
self.map
781+
.buffer
782+
.push((self.key, value))
783+
.map_err(|(_k, v)| v)?;
784+
let idx = self.map.buffer.len() - 1;
785+
let r = &mut self.map.buffer[idx];
786+
Ok(&mut r.1)
787+
}
788+
}
789+
646790
#[cfg(test)]
647791
mod test {
648792
use static_assertions::assert_not_impl_any;
649793

650-
use super::{LinearMap, LinearMapView};
794+
use super::{Entry, LinearMap, LinearMapView};
651795

652796
// Ensure a `LinearMap` containing `!Send` keys stays `!Send` itself.
653797
assert_not_impl_any!(LinearMap<*const (), (), 4>: Send);
@@ -780,4 +924,205 @@ mod test {
780924
assert_eq!(map.len(), 0);
781925
assert!(map.is_empty());
782926
}
927+
928+
// tests that use this constant take too long to run under miri, specially on CI, with a map of
929+
// this size so make the map smaller when using miri
930+
#[cfg(not(miri))]
931+
const MAP_SLOTS: usize = 4096;
932+
#[cfg(miri)]
933+
const MAP_SLOTS: usize = 64;
934+
fn almost_filled_map() -> LinearMap<usize, usize, MAP_SLOTS> {
935+
let mut almost_filled = LinearMap::new();
936+
for i in 1..MAP_SLOTS {
937+
almost_filled.insert(i, i).unwrap();
938+
}
939+
almost_filled
940+
}
941+
942+
#[test]
943+
fn remove() {
944+
let mut src = almost_filled_map();
945+
// key doesn't exist
946+
let k = 0;
947+
let r = src.remove(&k);
948+
assert!(r.is_none());
949+
950+
let k = 5;
951+
let v = 5;
952+
let r = src.remove(&k);
953+
assert_eq!(r, Some(v));
954+
let r = src.remove(&k);
955+
assert!(r.is_none());
956+
assert_eq!(src.len(), MAP_SLOTS - 2);
957+
}
958+
959+
#[test]
960+
fn replace() {
961+
let mut src = almost_filled_map();
962+
src.insert(10, 1000).unwrap();
963+
let v = src.get(&10).unwrap();
964+
assert_eq!(*v, 1000);
965+
966+
let mut src = almost_filled_map();
967+
let v = src.get_mut(&10).unwrap();
968+
*v = 500;
969+
let v = src.get(&10).unwrap();
970+
assert_eq!(*v, 500);
971+
}
972+
973+
#[test]
974+
fn retain() {
975+
let mut src = almost_filled_map();
976+
src.retain(|k, _v| k % 2 == 0);
977+
src.retain(|k, _v| k % 3 == 0);
978+
979+
for (k, v) in src.iter() {
980+
assert_eq!(k, v);
981+
assert_eq!(k % 2, 0);
982+
assert_eq!(k % 3, 0);
983+
}
984+
985+
let mut src = almost_filled_map();
986+
src.retain(|_k, _v| false);
987+
assert!(src.is_empty());
988+
989+
let mut src = almost_filled_map();
990+
src.retain(|_k, _v| true);
991+
assert_eq!(src.len(), MAP_SLOTS - 1);
992+
src.insert(0, 0).unwrap();
993+
src.retain(|_k, _v| true);
994+
assert_eq!(src.len(), MAP_SLOTS);
995+
}
996+
997+
#[test]
998+
fn entry_find() {
999+
let key = 0;
1000+
let value = 0;
1001+
let mut src = almost_filled_map();
1002+
let entry = src.entry(key);
1003+
match entry {
1004+
Entry::Occupied(_) => {
1005+
panic!("Found entry without inserting");
1006+
}
1007+
Entry::Vacant(v) => {
1008+
assert_eq!(&key, v.key());
1009+
assert_eq!(key, v.into_key());
1010+
}
1011+
}
1012+
src.insert(key, value).unwrap();
1013+
let entry = src.entry(key);
1014+
match entry {
1015+
Entry::Occupied(mut o) => {
1016+
assert_eq!(&key, o.key());
1017+
assert_eq!(&value, o.get());
1018+
assert_eq!(&value, o.get_mut());
1019+
assert_eq!(&value, o.into_mut());
1020+
}
1021+
Entry::Vacant(_) => {
1022+
panic!("Entry not found");
1023+
}
1024+
}
1025+
}
1026+
1027+
#[test]
1028+
fn entry_vacant_insert() {
1029+
let key = 0;
1030+
let value = 0;
1031+
let mut src = almost_filled_map();
1032+
assert_eq!(MAP_SLOTS - 1, src.len());
1033+
let entry = src.entry(key);
1034+
match entry {
1035+
Entry::Occupied(_) => {
1036+
panic!("Entry found when empty");
1037+
}
1038+
Entry::Vacant(v) => {
1039+
assert_eq!(value, *v.insert(value).unwrap());
1040+
}
1041+
};
1042+
assert_eq!(value, *src.get(&key).unwrap());
1043+
}
1044+
1045+
#[test]
1046+
fn entry_vacant_full_insert() {
1047+
let mut src = almost_filled_map();
1048+
1049+
// fill the map
1050+
let key = MAP_SLOTS * 2;
1051+
let value = key;
1052+
src.insert(key, value).unwrap();
1053+
assert_eq!(MAP_SLOTS, src.len());
1054+
1055+
let key = 0;
1056+
let value = 0;
1057+
let entry = src.entry(key);
1058+
match entry {
1059+
Entry::Occupied(_) => {
1060+
panic!("Entry found when missing");
1061+
}
1062+
Entry::Vacant(v) => {
1063+
// Value is returned since the map is full
1064+
assert_eq!(value, v.insert(value).unwrap_err());
1065+
}
1066+
};
1067+
assert!(src.get(&key).is_none());
1068+
}
1069+
1070+
#[test]
1071+
fn entry_occupied_insert() {
1072+
let key = 0;
1073+
let value = 0;
1074+
let value2 = 5;
1075+
let mut src = almost_filled_map();
1076+
assert_eq!(MAP_SLOTS - 1, src.len());
1077+
src.insert(key, value).unwrap();
1078+
let entry = src.entry(key);
1079+
match entry {
1080+
Entry::Occupied(o) => {
1081+
assert_eq!(value, o.insert(value2));
1082+
}
1083+
Entry::Vacant(_) => {
1084+
panic!("Entry not found");
1085+
}
1086+
};
1087+
assert_eq!(value2, *src.get(&key).unwrap());
1088+
}
1089+
1090+
#[test]
1091+
fn entry_remove_entry() {
1092+
let key = 0;
1093+
let value = 0;
1094+
let mut src = almost_filled_map();
1095+
src.insert(key, value).unwrap();
1096+
assert_eq!(MAP_SLOTS, src.len());
1097+
let entry = src.entry(key);
1098+
match entry {
1099+
Entry::Occupied(o) => {
1100+
assert_eq!((key, value), o.remove_entry());
1101+
}
1102+
Entry::Vacant(_) => {
1103+
panic!("Entry not found")
1104+
}
1105+
};
1106+
assert_eq!(MAP_SLOTS - 1, src.len());
1107+
assert!(!src.contains_key(&key));
1108+
}
1109+
1110+
#[test]
1111+
fn entry_remove() {
1112+
let key = 0;
1113+
let value = 0;
1114+
let mut src = almost_filled_map();
1115+
src.insert(key, value).unwrap();
1116+
assert_eq!(MAP_SLOTS, src.len());
1117+
let entry = src.entry(key);
1118+
match entry {
1119+
Entry::Occupied(o) => {
1120+
assert_eq!(value, o.remove());
1121+
}
1122+
Entry::Vacant(_) => {
1123+
panic!("Entry not found");
1124+
}
1125+
};
1126+
assert_eq!(MAP_SLOTS - 1, src.len());
1127+
}
7831128
}

0 commit comments

Comments
 (0)