Skip to content

Commit df6f699

Browse files
committed
linear_map: Implement Entry API
entry tests and docs are copied verbatim from IndexMap
1 parent 6b17767 commit df6f699

File tree

2 files changed

+250
-1
lines changed

2 files changed

+250
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1212
- `bytes::BufMut` is now implemented on `VecInner`.
1313
- Removed generic from `history_buf::OldestOrdered`.
1414
- Made `LenType` opt-in.
15+
- Added `LinearMap::entry()` API.
1516

1617
### Fixed
1718

src/linear_map.rs

Lines changed: 249 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,34 @@ where
467467
pub fn values_mut(&mut self) -> impl Iterator<Item = &mut V> {
468468
self.iter_mut().map(|(_, v)| v)
469469
}
470+
471+
/// Returns an entry for the corresponding key
472+
/// ```
473+
/// use heapless::linear_map;
474+
/// use heapless::LinearMap;
475+
/// let mut map = LinearMap::<_, _, 16>::new();
476+
/// if let linear_map::Entry::Vacant(v) = map.entry("a") {
477+
/// v.insert(1).unwrap();
478+
/// }
479+
/// if let linear_map::Entry::Occupied(mut o) = map.entry("a") {
480+
/// println!("found {}", *o.get()); // Prints 1
481+
/// o.insert(2);
482+
/// }
483+
/// // Prints 2
484+
/// println!("val: {}", *map.get("a").unwrap());
485+
/// ```
486+
pub fn entry(&mut self, key: K) -> Entry<'_, K, V, S> {
487+
let idx = self
488+
.keys()
489+
.enumerate()
490+
.find(|&(_, k)| *k.borrow() == key)
491+
.map(|(idx, _)| idx);
492+
493+
match idx {
494+
Some(idx) => Entry::Occupied(OccupiedEntry { idx, map: self }),
495+
None => Entry::Vacant(VacantEntry { key, map: self }),
496+
}
497+
}
470498
}
471499

472500
impl<K, V, Q, S: LinearMapStorage<K, V> + ?Sized> ops::Index<&'_ Q> for LinearMapInner<K, V, S>
@@ -635,11 +663,111 @@ where
635663
{
636664
}
637665

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

642-
use super::{LinearMap, LinearMapView};
770+
use super::{Entry, LinearMap, LinearMapView};
643771

644772
// Ensure a `LinearMap` containing `!Send` keys stays `!Send` itself.
645773
assert_not_impl_any!(LinearMap<*const (), (), 4>: Send);
@@ -752,4 +880,124 @@ mod test {
752880
let map: LinearMap<usize, f32, 4> = Default::default();
753881
assert_eq!(map, map);
754882
}
883+
884+
// tests that use this constant take too long to run under miri, specially on CI, with a map of
885+
// this size so make the map smaller when using miri
886+
#[cfg(not(miri))]
887+
const MAP_SLOTS: usize = 4096;
888+
#[cfg(miri)]
889+
const MAP_SLOTS: usize = 64;
890+
fn almost_filled_map() -> LinearMap<usize, usize, MAP_SLOTS> {
891+
let mut almost_filled = LinearMap::new();
892+
for i in 1..MAP_SLOTS {
893+
almost_filled.insert(i, i).unwrap();
894+
}
895+
almost_filled
896+
}
897+
898+
#[test]
899+
fn entry_find() {
900+
let key = 0;
901+
let value = 0;
902+
let mut src = almost_filled_map();
903+
let entry = src.entry(key);
904+
match entry {
905+
Entry::Occupied(_) => {
906+
panic!("Found entry without inserting");
907+
}
908+
Entry::Vacant(v) => {
909+
assert_eq!(&key, v.key());
910+
assert_eq!(key, v.into_key());
911+
}
912+
}
913+
src.insert(key, value).unwrap();
914+
let entry = src.entry(key);
915+
match entry {
916+
Entry::Occupied(mut o) => {
917+
assert_eq!(&key, o.key());
918+
assert_eq!(&value, o.get());
919+
assert_eq!(&value, o.get_mut());
920+
assert_eq!(&value, o.into_mut());
921+
}
922+
Entry::Vacant(_) => {
923+
panic!("Entry not found");
924+
}
925+
}
926+
}
927+
928+
#[test]
929+
fn entry_vacant_insert() {
930+
let key = 0;
931+
let value = 0;
932+
let mut src = almost_filled_map();
933+
assert_eq!(MAP_SLOTS - 1, src.len());
934+
let entry = src.entry(key);
935+
match entry {
936+
Entry::Occupied(_) => {
937+
panic!("Entry found when empty");
938+
}
939+
Entry::Vacant(v) => {
940+
assert_eq!(value, *v.insert(value).unwrap());
941+
}
942+
};
943+
assert_eq!(value, *src.get(&key).unwrap());
944+
}
945+
946+
#[test]
947+
fn entry_occupied_insert() {
948+
let key = 0;
949+
let value = 0;
950+
let value2 = 5;
951+
let mut src = almost_filled_map();
952+
assert_eq!(MAP_SLOTS - 1, src.len());
953+
src.insert(key, value).unwrap();
954+
let entry = src.entry(key);
955+
match entry {
956+
Entry::Occupied(o) => {
957+
assert_eq!(value, o.insert(value2));
958+
}
959+
Entry::Vacant(_) => {
960+
panic!("Entry not found");
961+
}
962+
};
963+
assert_eq!(value2, *src.get(&key).unwrap());
964+
}
965+
966+
#[test]
967+
fn entry_remove_entry() {
968+
let key = 0;
969+
let value = 0;
970+
let mut src = almost_filled_map();
971+
src.insert(key, value).unwrap();
972+
assert_eq!(MAP_SLOTS, src.len());
973+
let entry = src.entry(key);
974+
match entry {
975+
Entry::Occupied(o) => {
976+
assert_eq!((key, value), o.remove_entry());
977+
}
978+
Entry::Vacant(_) => {
979+
panic!("Entry not found")
980+
}
981+
};
982+
assert_eq!(MAP_SLOTS - 1, src.len());
983+
}
984+
985+
#[test]
986+
fn entry_remove() {
987+
let key = 0;
988+
let value = 0;
989+
let mut src = almost_filled_map();
990+
src.insert(key, value).unwrap();
991+
assert_eq!(MAP_SLOTS, src.len());
992+
let entry = src.entry(key);
993+
match entry {
994+
Entry::Occupied(o) => {
995+
assert_eq!(value, o.remove());
996+
}
997+
Entry::Vacant(_) => {
998+
panic!("Entry not found");
999+
}
1000+
};
1001+
assert_eq!(MAP_SLOTS - 1, src.len());
1002+
}
7551003
}

0 commit comments

Comments
 (0)