#![no_std]
#![warn(missing_docs)]
#![warn(rust_2018_idioms)]
extern crate alloc;
use alloc::vec::Vec;
mod iter;
mod option_index;
pub use iter::{Drain, IntoIter, Iter, IterMut, Keys, Values, ValuesMut};
use option_index::OptionIndex;
#[derive(PartialEq, PartialOrd, Eq, Ord)]
pub struct IndexMap<T> {
data: Vec<OptionIndex<T>>,
head: Option<usize>,
len: usize,
}
impl<T> IndexMap<T> {
pub fn new() -> Self {
Self {
data: Vec::new(),
head: None,
len: 0,
}
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
data: Vec::with_capacity(capacity),
head: None,
len: 0,
}
}
pub fn capacity(&self) -> usize {
self.data.capacity()
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn clear(&mut self) {
self.len = 0;
self.data.clear()
}
pub fn reserve(&mut self, additional: usize) {
self.data.reserve(additional)
}
pub fn shrink_to_fit(&mut self) {
if self.head.is_none() || self.data.last().unwrap().is_inner() {
self.data.shrink_to_fit();
return;
}
if self.is_empty() {
self.head = None;
self.data.clear();
self.data.shrink_to_fit();
return;
}
let mut last = usize::MAX;
for (i, v) in self.data.iter().enumerate().rev() {
if v.is_inner() {
last = i;
break;
}
}
assert_ne!(last, usize::MAX);
let mut head = self.head.unwrap();
let mut should_set_head = head > last;
while let OptionIndex::Index(next) = self.data[head] {
if next > last {
self.data[head] = match self.data[next] {
OptionIndex::Index(i) => OptionIndex::Index(i),
OptionIndex::NoIndex => OptionIndex::NoIndex,
OptionIndex::Some(_) => {
unreachable!("encountered value while walking index list")
}
};
}
if should_set_head && head < last {
self.head = Some(head);
should_set_head = false;
}
head = next;
}
if should_set_head {
self.head = if head < last { Some(head) } else { None };
}
self.data[head] = OptionIndex::NoIndex;
self.data.truncate(last + 1);
self.data.shrink_to_fit()
}
pub fn contains_key(&self, index: usize) -> bool {
if index >= self.data.len() {
return false;
}
self.data[index].is_inner()
}
pub fn insert(&mut self, value: T) -> usize {
self.len += 1;
if let Some(head) = self.head {
self.head = self.data[head].take().into_index();
self.data[head] = OptionIndex::Some(value);
head
} else {
self.data.push(OptionIndex::Some(value));
self.data.len() - 1
}
}
pub fn remove(&mut self, index: usize) -> Option<T> {
if !self.data.get(index)?.is_inner() {
return None;
}
let val = self.data.get_mut(index)?.take().into_inner()?;
if let Some(head) = self.head {
self.data[index] = OptionIndex::Index(head);
} else {
self.data[index] = OptionIndex::NoIndex;
}
self.head = Some(index);
self.len -= 1;
Some(val)
}
pub fn remove_entry(&mut self, index: usize) -> Option<(usize, T)> {
Some((index, self.remove(index)?))
}
pub fn get(&self, index: usize) -> Option<&T> {
self.data.get(index)?.as_ref().into_inner()
}
pub fn get_key_value(&self, index: usize) -> Option<(usize, &T)> {
Some((index, self.get(index)?))
}
pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
self.data.get_mut(index)?.as_mut().into_inner()
}
pub fn retain<P>(&mut self, mut predicate: P)
where
P: FnMut(usize, &mut T) -> bool,
{
for (i, v) in self.data.iter_mut().enumerate() {
if let OptionIndex::Some(val) = v {
if !predicate(i, val) {
*v = if let Some(head) = self.head {
OptionIndex::Index(head)
} else {
OptionIndex::NoIndex
};
self.len -= 1;
self.head = Some(i)
}
}
}
}
}
impl<T: Clone> Clone for IndexMap<T> {
fn clone(&self) -> Self {
Self {
data: self.data.clone(),
head: self.head,
len: self.len,
}
}
}
impl<T> Default for IndexMap<T> {
fn default() -> Self {
Self::new()
}
}
use core::fmt;
impl<T: fmt::Debug> fmt::Debug for IndexMap<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_map().entries(self.iter()).finish()
}
}
use core::ops::{Index, IndexMut};
impl<T> Index<usize> for IndexMap<T> {
type Output = T;
fn index(&self, key: usize) -> &T {
self.get(key).unwrap()
}
}
impl<T> IndexMut<usize> for IndexMap<T> {
fn index_mut(&mut self, key: usize) -> &mut T {
self.get_mut(key).unwrap()
}
}
#[cfg(test)]
mod tests {
use super::{IndexMap, OptionIndex as OI};
fn assert_state<T: Eq + core::fmt::Debug>(
map: &IndexMap<T>,
data: &[OI<T>],
head: Option<usize>,
) {
assert_eq!(map.data[..], data[..]);
assert_eq!(map.head, head);
}
#[test]
fn test_map() {
let mut map = IndexMap::new();
let _ = map.insert('a');
let b = map.insert('b');
let c = map.insert('c');
let _ = map.insert('d');
let e = map.insert('e');
assert_state(
&map,
&[
OI::Some('a'),
OI::Some('b'),
OI::Some('c'),
OI::Some('d'),
OI::Some('e'),
],
None,
);
assert_eq!(map.remove(b), Some('b'));
assert_state(
&map,
&[
OI::Some('a'),
OI::NoIndex,
OI::Some('c'),
OI::Some('d'),
OI::Some('e'),
],
Some(1),
);
assert_eq!(map.remove(e), Some('e'));
assert_state(
&map,
&[
OI::Some('a'),
OI::NoIndex,
OI::Some('c'),
OI::Some('d'),
OI::Index(1),
],
Some(4),
);
assert_eq!(map.remove(c), Some('c'));
assert_state(
&map,
&[
OI::Some('a'),
OI::NoIndex,
OI::Index(4),
OI::Some('d'),
OI::Index(1),
],
Some(2),
);
map.shrink_to_fit();
assert_state(
&map,
&[OI::Some('a'), OI::NoIndex, OI::Index(1), OI::Some('d')],
Some(2),
);
}
#[test]
fn test_shrink_to_fit() {
let mut map = IndexMap::new();
let a = map.insert('a');
let b = map.insert('b');
let c = map.insert('c');
let d = map.insert('d');
let e = map.insert('e');
map.remove(e);
map.remove(b);
map.remove(d);
assert_state(
&map,
&[
OI::Some('a'),
OI::Index(4),
OI::Some('c'),
OI::Index(1),
OI::NoIndex,
],
Some(3),
);
map.shrink_to_fit();
assert_state(&map, &[OI::Some('a'), OI::NoIndex, OI::Some('c')], Some(1));
map.remove(c);
map.shrink_to_fit();
assert_state(&map, &[OI::Some('a')], None);
map.remove(a);
assert_state(&map, &[OI::NoIndex], Some(0));
map.shrink_to_fit();
assert_state(&map, &[], None);
let mut map = IndexMap::new();
let _ = map.insert('a');
let b = map.insert('b');
let _ = map.insert('c');
let d = map.insert('d');
let e = map.insert('e');
map.remove(b);
map.remove(d);
map.remove(e);
assert_state(
&map,
&[
OI::Some('a'),
OI::NoIndex,
OI::Some('c'),
OI::Index(1),
OI::Index(3),
],
Some(4),
);
map.shrink_to_fit();
assert_state(&map, &[OI::Some('a'), OI::NoIndex, OI::Some('c')], Some(1));
}
}