use core::borrow::Borrow;
use core::cmp::Ordering;
use heapless::Vec as HeaplessVec;
#[derive(Debug, Clone)]
pub struct Entry<K, V>(pub K, pub V);
impl<K: PartialEq, V> PartialEq for Entry<K, V> {
fn eq(&self, other: &Self) -> bool {
self.0.eq(&other.0)
}
}
impl<K: Eq, V> Eq for Entry<K, V> {}
impl<K: PartialOrd, V> PartialOrd for Entry<K, V> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.0.partial_cmp(&other.0)
}
}
impl<K: Ord, V> Ord for Entry<K, V> {
fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(&other.0)
}
}
#[derive(Debug, Clone)]
pub struct HeaplessBTreeMap<K, V, const N: usize> {
buf: HeaplessVec<Entry<K, V>, N>,
}
impl<K: Ord, V, const N: usize> HeaplessBTreeMap<K, V, N> {
pub fn new() -> Self {
const {
assert!(
std::mem::size_of::<Self>() <= 16 * 1024,
"HeaplessBTreeMap is too large! The struct size exceeds the 16KB limit. Reduce N."
);
}
Self {
buf: HeaplessVec::new(),
}
}
pub fn len(&self) -> usize {
self.buf.len()
}
pub fn is_empty(&self) -> bool {
self.buf.is_empty()
}
pub fn is_full(&self) -> bool {
self.buf.is_full()
}
pub fn clear(&mut self) {
self.buf.clear();
}
pub fn insert(&mut self, key: K, value: V) -> Result<Option<V>, (K, V)> {
match self.buf.binary_search_by(|e| e.0.cmp(&key)) {
Ok(idx) => Ok(Some(core::mem::replace(&mut self.buf[idx].1, value))),
Err(idx) => {
if self.buf.is_full() {
Err((key, value))
} else {
self.buf.insert(idx, Entry(key, value)).ok().unwrap();
Ok(None)
}
}
}
}
pub fn get<Q>(&self, key: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: Ord + ?Sized,
{
match self.buf.binary_search_by(|e| e.0.borrow().cmp(key)) {
Ok(idx) => Some(&self.buf[idx].1),
Err(_) => None,
}
}
pub fn get_mut<Q>(&mut self, key: &Q) -> Option<&mut V>
where
K: Borrow<Q>,
Q: Ord + ?Sized,
{
match self.buf.binary_search_by(|e| e.0.borrow().cmp(key)) {
Ok(idx) => Some(&mut self.buf[idx].1),
Err(_) => None,
}
}
pub fn remove<Q>(&mut self, key: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: Ord + ?Sized,
{
match self.buf.binary_search_by(|e| e.0.borrow().cmp(key)) {
Ok(idx) => Some(self.buf.remove(idx).1),
Err(_) => None,
}
}
pub fn iter(&self) -> core::slice::Iter<'_, Entry<K, V>> {
self.buf.iter()
}
pub fn into_vec(self) -> HeaplessVec<Entry<K, V>, N> {
self.buf
}
}
impl<K: Ord, V, const N: usize> Default for HeaplessBTreeMap<K, V, N> {
fn default() -> Self {
Self::new()
}
}
impl<K: Ord, V, const N: usize> IntoIterator for HeaplessBTreeMap<K, V, N> {
type Item = Entry<K, V>;
type IntoIter = heapless::vec::IntoIter<Entry<K, V>, N, usize>;
fn into_iter(self) -> Self::IntoIter {
self.buf.into_iter()
}
}
impl<K: Ord, V: PartialEq, const N: usize> PartialEq for HeaplessBTreeMap<K, V, N> {
fn eq(&self, other: &Self) -> bool {
if self.len() != other.len() {
return false;
}
self.iter()
.zip(other.iter())
.all(|(a, b)| a.0 == b.0 && a.1 == b.1)
}
}
impl<K: Ord, V: Eq, const N: usize> Eq for HeaplessBTreeMap<K, V, N> {}
impl<K: Ord, V: PartialOrd, const N: usize> PartialOrd for HeaplessBTreeMap<K, V, N> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.iter()
.map(|e| (&e.0, &e.1))
.partial_cmp(other.iter().map(|e| (&e.0, &e.1)))
}
}
impl<K: Ord, V: Ord, const N: usize> Ord for HeaplessBTreeMap<K, V, N> {
fn cmp(&self, other: &Self) -> Ordering {
self.iter()
.map(|e| (&e.0, &e.1))
.cmp(other.iter().map(|e| (&e.0, &e.1)))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_heapless_btree_map_stack_ops_sorted_order() {
let mut map: HeaplessBTreeMap<i32, i32, 4> = HeaplessBTreeMap::new();
map.insert(3, 30).unwrap();
map.insert(1, 10).unwrap();
map.insert(2, 20).unwrap();
let keys: Vec<_> = map.iter().map(|e| e.0).collect();
assert_eq!(keys, vec![1, 2, 3]);
}
#[test]
fn test_heapless_btree_map_stack_ops_update() {
let mut map: HeaplessBTreeMap<i32, i32, 4> = HeaplessBTreeMap::new();
assert_eq!(map.insert(1, 10), Ok(None));
assert_eq!(map.insert(1, 20), Ok(Some(10)));
assert_eq!(map.get(&1), Some(&20));
}
#[test]
fn test_heapless_btree_map_stack_ops_remove() {
let mut map: HeaplessBTreeMap<i32, i32, 4> = HeaplessBTreeMap::new();
map.insert(1, 10).unwrap();
map.insert(2, 20).unwrap();
assert_eq!(map.remove(&1), Some(10));
assert_eq!(map.len(), 1);
assert_eq!(map.get(&1), None);
}
#[test]
fn test_heapless_btree_map_stack_ops_full_returns_err() {
let mut map: HeaplessBTreeMap<i32, i32, 2> = HeaplessBTreeMap::new();
map.insert(1, 10).unwrap();
map.insert(2, 20).unwrap();
assert!(map.is_full());
assert_eq!(map.insert(3, 30), Err((3, 30)));
}
#[test]
fn test_heapless_btree_map_stack_ops_get_mut() {
let mut map: HeaplessBTreeMap<i32, i32, 4> = HeaplessBTreeMap::new();
map.insert(1, 10).unwrap();
if let Some(v) = map.get_mut(&1) {
*v = 99;
}
assert_eq!(map.get(&1), Some(&99));
}
#[test]
fn test_heapless_btree_map_traits_clone_default() {
let map1: HeaplessBTreeMap<i32, i32, 4> = HeaplessBTreeMap::default();
let mut map2 = map1.clone();
map2.insert(1, 1).unwrap();
assert_eq!(map1.len(), 0);
assert_eq!(map2.len(), 1);
}
#[test]
fn test_heapless_btree_map_traits_entry_ord() {
let e1 = Entry(1i32, 10i32);
let e2 = Entry(1i32, 20i32);
let e3 = Entry(2i32, 10i32);
assert_eq!(e1, e2);
assert!(e1 < e3);
assert_eq!(e1.cmp(&e2), Ordering::Equal);
assert_eq!(e1.partial_cmp(&e3), Some(Ordering::Less));
}
}
#[cfg(test)]
mod heapless_btree_map_coverage_tests {
use super::*;
#[test]
fn test_is_empty_false() {
let mut map: HeaplessBTreeMap<i32, i32, 2> = HeaplessBTreeMap::new();
map.insert(1, 10).unwrap();
assert!(!map.is_empty());
}
#[test]
fn test_get_mut_missing() {
let mut map: HeaplessBTreeMap<i32, i32, 2> = HeaplessBTreeMap::new();
map.insert(1, 10).unwrap();
assert_eq!(map.get_mut(&2), None);
}
#[test]
fn test_into_vec() {
let mut map: HeaplessBTreeMap<i32, i32, 4> = HeaplessBTreeMap::new();
map.insert(2, 20).unwrap();
map.insert(1, 10).unwrap();
let vec = map.into_vec();
assert_eq!(vec.len(), 2);
assert_eq!(vec[0].0, 1);
assert_eq!(vec[1].0, 2);
}
#[test]
fn test_traits_partial_eq() {
let mut m1: HeaplessBTreeMap<i32, i32, 4> = HeaplessBTreeMap::new();
m1.insert(1, 10).unwrap();
let mut m2: HeaplessBTreeMap<i32, i32, 4> = HeaplessBTreeMap::new();
m2.insert(1, 10).unwrap();
let mut m3: HeaplessBTreeMap<i32, i32, 4> = HeaplessBTreeMap::new();
m3.insert(1, 10).unwrap();
m3.insert(2, 20).unwrap();
let mut m4: HeaplessBTreeMap<i32, i32, 4> = HeaplessBTreeMap::new();
m4.insert(1, 99).unwrap();
assert_eq!(m1, m2);
assert_ne!(m1, m3); assert_ne!(m1, m4); }
#[test]
fn test_traits_ord_partial_ord() {
let mut m1: HeaplessBTreeMap<i32, i32, 4> = HeaplessBTreeMap::new();
m1.insert(1, 10).unwrap();
m1.insert(2, 20).unwrap();
let mut m2: HeaplessBTreeMap<i32, i32, 4> = HeaplessBTreeMap::new();
m2.insert(1, 10).unwrap();
m2.insert(3, 30).unwrap();
assert!(m1 < m2);
assert_eq!(m1.cmp(&m2), Ordering::Less);
assert_eq!(m1.partial_cmp(&m2), Some(Ordering::Less));
assert_eq!(m1.cmp(&m1), Ordering::Equal);
}
}