Skip to content

Commit e6c1fe0

Browse files
mkjsgued
authored andcommitted
linear_map: Implement Entry API
entry tests and docs are copied verbatim from IndexMap
1 parent 5ca8839 commit e6c1fe0

File tree

2 files changed

+256
-1
lines changed

2 files changed

+256
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ 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.
3233

3334
### Fixed
3435

src/linear_map.rs

Lines changed: 255 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,40 @@ 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+
}
478512
}
479513

480514
impl<K, V, Q, S: LinearMapStorage<K, V> + ?Sized> ops::Index<&'_ Q> for LinearMapInner<K, V, S>
@@ -643,11 +677,111 @@ where
643677
{
644678
}
645679

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

650-
use super::{LinearMap, LinearMapView};
784+
use super::{Entry, LinearMap, LinearMapView};
651785

652786
// Ensure a `LinearMap` containing `!Send` keys stays `!Send` itself.
653787
assert_not_impl_any!(LinearMap<*const (), (), 4>: Send);
@@ -780,4 +914,124 @@ mod test {
780914
assert_eq!(map.len(), 0);
781915
assert!(map.is_empty());
782916
}
917+
918+
// tests that use this constant take too long to run under miri, specially on CI, with a map of
919+
// this size so make the map smaller when using miri
920+
#[cfg(not(miri))]
921+
const MAP_SLOTS: usize = 4096;
922+
#[cfg(miri)]
923+
const MAP_SLOTS: usize = 64;
924+
fn almost_filled_map() -> LinearMap<usize, usize, MAP_SLOTS> {
925+
let mut almost_filled = LinearMap::new();
926+
for i in 1..MAP_SLOTS {
927+
almost_filled.insert(i, i).unwrap();
928+
}
929+
almost_filled
930+
}
931+
932+
#[test]
933+
fn entry_find() {
934+
let key = 0;
935+
let value = 0;
936+
let mut src = almost_filled_map();
937+
let entry = src.entry(key);
938+
match entry {
939+
Entry::Occupied(_) => {
940+
panic!("Found entry without inserting");
941+
}
942+
Entry::Vacant(v) => {
943+
assert_eq!(&key, v.key());
944+
assert_eq!(key, v.into_key());
945+
}
946+
}
947+
src.insert(key, value).unwrap();
948+
let entry = src.entry(key);
949+
match entry {
950+
Entry::Occupied(mut o) => {
951+
assert_eq!(&key, o.key());
952+
assert_eq!(&value, o.get());
953+
assert_eq!(&value, o.get_mut());
954+
assert_eq!(&value, o.into_mut());
955+
}
956+
Entry::Vacant(_) => {
957+
panic!("Entry not found");
958+
}
959+
}
960+
}
961+
962+
#[test]
963+
fn entry_vacant_insert() {
964+
let key = 0;
965+
let value = 0;
966+
let mut src = almost_filled_map();
967+
assert_eq!(MAP_SLOTS - 1, src.len());
968+
let entry = src.entry(key);
969+
match entry {
970+
Entry::Occupied(_) => {
971+
panic!("Entry found when empty");
972+
}
973+
Entry::Vacant(v) => {
974+
assert_eq!(value, *v.insert(value).unwrap());
975+
}
976+
};
977+
assert_eq!(value, *src.get(&key).unwrap());
978+
}
979+
980+
#[test]
981+
fn entry_occupied_insert() {
982+
let key = 0;
983+
let value = 0;
984+
let value2 = 5;
985+
let mut src = almost_filled_map();
986+
assert_eq!(MAP_SLOTS - 1, src.len());
987+
src.insert(key, value).unwrap();
988+
let entry = src.entry(key);
989+
match entry {
990+
Entry::Occupied(o) => {
991+
assert_eq!(value, o.insert(value2));
992+
}
993+
Entry::Vacant(_) => {
994+
panic!("Entry not found");
995+
}
996+
};
997+
assert_eq!(value2, *src.get(&key).unwrap());
998+
}
999+
1000+
#[test]
1001+
fn entry_remove_entry() {
1002+
let key = 0;
1003+
let value = 0;
1004+
let mut src = almost_filled_map();
1005+
src.insert(key, value).unwrap();
1006+
assert_eq!(MAP_SLOTS, src.len());
1007+
let entry = src.entry(key);
1008+
match entry {
1009+
Entry::Occupied(o) => {
1010+
assert_eq!((key, value), o.remove_entry());
1011+
}
1012+
Entry::Vacant(_) => {
1013+
panic!("Entry not found")
1014+
}
1015+
};
1016+
assert_eq!(MAP_SLOTS - 1, src.len());
1017+
}
1018+
1019+
#[test]
1020+
fn entry_remove() {
1021+
let key = 0;
1022+
let value = 0;
1023+
let mut src = almost_filled_map();
1024+
src.insert(key, value).unwrap();
1025+
assert_eq!(MAP_SLOTS, src.len());
1026+
let entry = src.entry(key);
1027+
match entry {
1028+
Entry::Occupied(o) => {
1029+
assert_eq!(value, o.remove());
1030+
}
1031+
Entry::Vacant(_) => {
1032+
panic!("Entry not found");
1033+
}
1034+
};
1035+
assert_eq!(MAP_SLOTS - 1, src.len());
1036+
}
7831037
}

0 commit comments

Comments
 (0)