#![no_std]
extern crate alloc;
use core::{ops::{IndexMut}, mem::{replace, forget}};
use alloc::{vec::Vec, collections::VecDeque};
pub enum Entry<'a, C: ?Sized> {
Occupied(OccupiedEntry<'a, C>),
Vacant(VacantEntry<'a, C>),
}
pub struct OccupiedEntry<'a, C: ?Sized>{
index: usize,
container: &'a mut C,
}
pub struct VacantEntry<'a, C: ?Sized>{
index: usize,
container: &'a mut C,
}
impl<'a, C: 'a+Entriable> OccupiedEntry<'a, C> {
pub fn key(&self) -> &usize {
&self.index
}
pub fn remove_entry(self) -> (usize, C::T) {
debug_assert!(self.index < self.container.len());
(self.index, self.remove())
}
pub fn get(&self) -> &C::T {
debug_assert!(self.index < self.container.len());
&self.container[self.index]
}
pub fn get_mut(&mut self) -> &mut C::T {
debug_assert!(self.index < self.container.len());
&mut self.container[self.index]
}
pub fn into_mut(self) -> &'a mut C::T {
debug_assert!(self.index < self.container.len());
&mut self.container[self.index]
}
pub fn insert(&mut self, value: C::T) -> C::T {
replace(self.get_mut(), value)
}
pub fn remove(self) -> C::T {
debug_assert!(self.index < self.container.len());
self.container.remove(self.index)
}
pub fn replace_entry(mut self, value: C::T) -> (usize, C::T) {
(self.index, self.insert(value))
}
pub fn replace_entry_with<F, R>(mut self, f: F) -> (Entry<'a, C>, R)
where F: FnOnce(&usize, C::T) -> (Option<C::T>, R) {
struct RemoveDropHandler<'b, 'a, C: 'a+Entriable>(&'b mut OccupiedEntry<'a, C>);
impl<'b, 'a, C: 'a+Entriable> Drop for RemoveDropHandler<'b, 'a, C> {
fn drop(&mut self) {
forget(self.0.container.remove(self.0.index));
}
}
let index = self.index;
let ptr: *mut _ = self.get_mut();
let handler = RemoveDropHandler(&mut self);
let v = unsafe { core::ptr::read(ptr) };
match f(&index, v) {
(None, r) => {
drop(handler);
let entry = if self.container.len() <= self.index {
Entry::Vacant(VacantEntry{index: self.index, container: self.container})
} else {
Entry::Occupied(self)
};
(entry, r)
}
(Some(v), val) => {
forget(handler);
unsafe { core::ptr::write(ptr, v) };
(Entry::Occupied(self), val)
}
}
}
}
impl<'a, C : 'a+Entriable> VacantEntry<'a, C> {
pub fn key(&self) -> &usize {
&self.index
}
pub fn into_key(self) -> usize {
self.index
}
pub fn insert(self, v: C::T) -> &'a mut C::T {
self.container.insert(self.index, v);
&mut self.container[self.index]
}
}
pub trait EntryExt {
fn entry<'a>(&'a mut self, index: usize) -> Entry<'a, Self>;
}
pub trait Entriable: IndexMut<usize, Output = Self::T> {
type T;
fn len(&self) -> usize;
fn insert(&mut self, index: usize, value: Self::T);
fn remove(&mut self, index: usize) -> Self::T;
}
impl<C: Entriable> EntryExt for C {
fn entry<'a>(&'a mut self, index: usize) -> Entry<'a, Self> {
if index >= self.len() {
Entry::Vacant(VacantEntry{index, container: self})
} else {
Entry::Occupied(OccupiedEntry{index, container: self})
}
}
}
impl<T> Entriable for Vec<T> {
type T = T;
fn len(&self) -> usize {
Vec::len(self)
}
fn insert(&mut self, index: usize, value: T) {
Vec::insert(self, index, value)
}
fn remove(&mut self, index: usize) -> T {
Vec::remove(self, index)
}
}
impl<T> Entriable for VecDeque<T> {
type T = T;
fn len(&self) -> usize {
VecDeque::len(self)
}
fn insert(&mut self, index: usize, value: T) {
VecDeque::insert(self, index, value)
}
fn remove(&mut self, index: usize) -> T {
VecDeque::remove(self, index).unwrap()
}
}
#[cfg(test)]
mod tests {
use core::cell::Cell;
use alloc::vec;
use super::*;
#[test]
fn it_works() {
let mut v = vec![69u64, 420, 0xDEADBEEF];
match v.entry(1) {
Entry::Vacant(_) => unreachable!(),
Entry::Occupied(mut o) => {
assert_eq!(*o.get(), 420);
assert_eq!(*o.get_mut(), 420);
o.replace_entry_with(|k, v|{
assert_eq!(*k, 1);
assert_eq!(v, 420);
(None, ())
});
},
}
assert_eq!(v, [69, 0xDEADBEEF])
}
#[test]
fn drop_count() {
struct DropCounter<'a>(&'a Cell<usize>);
impl<'a> Drop for DropCounter<'a> {
fn drop(&mut self) {
self.0.set(self.0.get() + 1)
}
}
let c = Cell::new(0);
let mut v = vec![DropCounter(&c), DropCounter(&c), DropCounter(&c), DropCounter(&c)];
let entry = match v.entry(1) {
Entry::Vacant(_) => unreachable!(),
Entry::Occupied(o) => {
assert_eq!(c.get(), 0);
let (entry, ()) = o.replace_entry_with(|k, v|{
assert_eq!(*k, 1);
assert_eq!(c.get(), 0);
drop(v);
assert_eq!(c.get(), 1);
(None, ())
});
assert_eq!(c.get(), 1);
entry
},
};
match entry {
Entry::Vacant(_) => unreachable!(),
Entry::Occupied(o) => {
assert_eq!(c.get(), 1);
o.replace_entry_with(|k, v|{
assert_eq!(*k, 1);
assert_eq!(c.get(), 1);
drop(v);
assert_eq!(c.get(), 2);
(Some(DropCounter(&c)), ())
});
assert_eq!(c.get(), 2);
}
}
drop(v);
assert_eq!(c.get(), 5);
}
}