#![deny(unsafe_code)]
#[derive(Debug, PartialEq)]
pub struct IndexList<T> {
contents: Vec<Entry<T>>,
generation: usize,
next_free: Option<usize>,
head: Option<usize>,
tail: Option<usize>,
}
#[derive(Debug, PartialEq)]
enum Entry<T> {
Free { next_free: Option<usize> },
Occupied(OccupiedEntry<T>),
}
#[derive(Debug, PartialEq)]
struct OccupiedEntry<T> {
item: T,
generation: usize,
next: Option<usize>,
prev: Option<usize>,
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Index {
index: usize,
generation: usize,
}
impl<T> IndexList<T>
where
T: PartialEq,
T: std::fmt::Debug,
{
pub fn new() -> IndexList<T> {
IndexList {
contents: Vec::new(),
generation: 0,
next_free: None,
head: None,
tail: None,
}
}
pub fn with_capacity(size: usize) -> IndexList<T> {
IndexList {
contents: Vec::with_capacity(size),
generation: 0,
next_free: None,
head: None,
tail: None,
}
}
pub fn head(&self) -> Option<&T> {
let index = self.head?;
self.contents.get(index).and_then(|e| match e {
Entry::Free { .. } => None,
Entry::Occupied(e) => Some(&e.item),
})
}
pub fn push_back(&mut self, item: T) -> Index {
if self.head.is_none() {
let generation = self.generation;
let index = if let Some(index) = self.next_free {
match self.contents[index] {
Entry::Occupied { .. } => panic!("Corrupted list"),
Entry::Free { next_free } => self.next_free = next_free,
}
self.contents[index] = Entry::Occupied(OccupiedEntry {
item,
generation,
next: None,
prev: None,
});
index
} else {
let index = self.contents.len();
self.contents.push(Entry::Occupied(OccupiedEntry {
item,
generation,
next: None,
prev: None,
}));
index
};
self.tail = Some(index);
self.head = Some(index);
return Index { index, generation };
}
let tail_index = self.tail.unwrap();
let position = if let Some(position) = self.next_free {
match self.contents[position] {
Entry::Occupied { .. } => panic!("Corrupted list"),
Entry::Free { next_free } => self.next_free = next_free,
}
self.contents[position] = Entry::Occupied(OccupiedEntry {
item,
generation: self.generation,
next: None,
prev: Some(tail_index),
});
position
} else {
let position = self.contents.len();
self.contents.push(Entry::Occupied(OccupiedEntry {
item,
generation: self.generation,
next: None,
prev: Some(tail_index),
}));
position
};
let new_index = Index {
index: position,
generation: self.generation,
};
match &mut self.contents[tail_index] {
Entry::Free { .. } => panic!("Corrupted list"),
Entry::Occupied(e) => e.next = Some(new_index.index),
}
self.tail = Some(position);
new_index
}
pub fn push_front(&mut self, item: T) -> Index {
if self.head.is_none() {
self.contents.push(Entry::Occupied(OccupiedEntry {
item,
generation: self.generation,
next: None,
prev: None,
}));
self.tail = Some(0);
self.head = Some(0);
return Index {
index: 0,
generation: self.generation,
};
}
let head_index = self.head.unwrap();
let position = if let Some(position) = self.next_free {
self.contents[position] = Entry::Occupied(OccupiedEntry {
item,
generation: self.generation,
next: Some(head_index),
prev: None,
});
position
} else {
let position = self.contents.len();
self.contents.push(Entry::Occupied(OccupiedEntry {
item,
generation: self.generation,
next: Some(head_index),
prev: None,
}));
position
};
let new_index = Index {
index: position,
generation: self.generation,
};
match &mut self.contents[head_index] {
Entry::Free { .. } => panic!("Corrupted list"),
Entry::Occupied(e) => e.prev = Some(new_index.index),
}
self.head = Some(position);
new_index
}
pub fn contains(&self, value: &T) -> bool {
self.iter().any(|e| e == value)
}
pub fn get(&self, index: Index) -> Option<&T> {
match self.contents.get(index.index)? {
Entry::Occupied(e) if e.generation == index.generation => Some(&e.item),
_ => None,
}
}
pub fn remove(&mut self, index: Index) -> Option<T> {
let head_index = self.head?;
let tail_index = self.tail?;
let (prev_index, index, next_index) = match self.contents.get(index.index)? {
Entry::Free { .. } => return None,
Entry::Occupied(e) => {
if index.generation != e.generation {
return None;
}
(e.prev, index.index, e.next)
},
};
let removed = std::mem::replace(
&mut self.contents[index],
Entry::Free {
next_free: self.next_free,
},
);
self.next_free = Some(index);
self.generation += 1;
if (index == head_index) && (index == tail_index) {
self.head = None;
self.tail = None;
} else if index == head_index {
let next = match &mut self.contents[next_index.unwrap()] {
Entry::Free { .. } => panic!("Corrupted list"),
Entry::Occupied(e) => e,
};
next.prev = None;
self.head = next_index;
} else if index == tail_index {
let prev = match &mut self.contents[prev_index.unwrap()] {
Entry::Free { .. } => panic!("Corrupted list"),
Entry::Occupied(e) => e,
};
prev.next = None;
self.tail = prev_index;
} else if index != head_index && index != tail_index {
{
let next = match &mut self.contents[next_index.unwrap()] {
Entry::Free { .. } => panic!("Corrupted list"),
Entry::Occupied(e) => e,
};
next.prev = prev_index;
}
{
let prev = match &mut self.contents[prev_index.unwrap()] {
Entry::Free { .. } => panic!("Corrupted list"),
Entry::Occupied(e) => e,
};
prev.next = next_index;
}
}
match removed {
Entry::Free { .. } => panic!("Corrupted list"),
Entry::Occupied(e) => Some(e.item),
}
}
pub fn iter(&self) -> impl Iterator<Item = &T> {
Iter {
list: self,
next_index: self.head,
}
}
pub fn index_of(&self, item: &T) -> Option<Index> {
let generation = self.generation;
for (index, e) in self.contents.iter().enumerate() {
if let Entry::Occupied(e) = e {
if &e.item == item {
return Some(Index { index, generation });
}
}
}
None
}
pub fn pop_front(&mut self) -> Option<T> {
let head_index = self.head?;
let (head_index, next_index) = match self.contents.get(head_index)? {
Entry::Free { .. } => return None,
Entry::Occupied(e) => (head_index, e.next),
};
let removed = std::mem::replace(
&mut self.contents[head_index],
Entry::Free {
next_free: self.next_free,
},
);
self.next_free = Some(head_index);
self.generation += 1;
if Some(head_index) == self.tail {
self.head = None;
self.tail = None;
} else {
let next = match &mut self.contents[next_index.unwrap()] {
Entry::Free { .. } => panic!("Corrupted list"),
Entry::Occupied(e) => e,
};
next.prev = None;
self.head = next_index;
}
match removed {
Entry::Free { .. } => panic!("Corrupted list"),
Entry::Occupied(e) => Some(e.item),
}
}
}
struct Iter<'a, T>
where
T: 'a,
{
list: &'a IndexList<T>,
next_index: Option<usize>,
}
impl<'a, T> Iterator for Iter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
let next_index = self.next_index?;
match &self.list.contents[next_index] {
Entry::Free { .. } => panic!("Corrupted list"),
Entry::Occupied(e) => {
self.next_index = e.next;
Some(&e.item)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn create_list() {
let _list: IndexList<i32> = IndexList::new();
}
#[test]
fn insert() {
let mut list = IndexList::new();
list.push_back(5);
assert_eq!(
list.contents[0],
Entry::Occupied(OccupiedEntry {
item: 5,
next: None,
prev: None,
generation: 0,
})
);
}
#[test]
fn contains() {
let mut list = IndexList::new();
list.push_back(5);
assert!(list.contains(&5));
}
#[test]
fn get() {
let mut list = IndexList::new();
let five = list.push_back(5);
let entry = list.get(five);
assert!(entry.is_some());
assert_eq!(entry.unwrap(), &5);
}
#[test]
fn insert_thrice() {
let mut list = IndexList::new();
list.push_back(5);
list.push_back(10);
list.push_back(15);
assert_eq!(
list.contents[0],
Entry::Occupied(OccupiedEntry {
item: 5,
next: Some(1),
prev: None,
generation: 0,
})
);
assert_eq!(
list.contents[1],
Entry::Occupied(OccupiedEntry {
item: 10,
next: Some(2),
prev: Some(0),
generation: 0,
})
);
assert_eq!(
list.contents[2],
Entry::Occupied(OccupiedEntry {
item: 15,
next: None,
prev: Some(1),
generation: 0,
})
);
}
#[test]
fn remove_middle() {
let mut list = IndexList::new();
list.push_back(5);
let ten = list.push_back(10);
list.push_back(15);
let removed = list.remove(ten).unwrap();
assert_eq!(removed, 10);
assert_eq!(
list,
IndexList {
contents: vec![
Entry::Occupied(OccupiedEntry {
item: 5,
next: Some(2),
prev: None,
generation: 0,
}),
Entry::Free { next_free: None },
Entry::Occupied(OccupiedEntry {
item: 15,
next: None,
prev: Some(0),
generation: 0,
}),
],
generation: 1,
next_free: Some(1),
head: Some(0),
tail: Some(2),
}
);
}
#[test]
fn remove_head() {
let mut list = IndexList::new();
let five = list.push_back(5);
list.push_back(10);
list.push_back(15);
let removed = list.remove(five).unwrap();
assert_eq!(removed, 5);
assert_eq!(
list,
IndexList {
contents: vec![
Entry::Free { next_free: None },
Entry::Occupied(OccupiedEntry {
item: 10,
next: Some(2),
prev: None,
generation: 0,
}),
Entry::Occupied(OccupiedEntry {
item: 15,
next: None,
prev: Some(1),
generation: 0,
}),
],
generation: 1,
next_free: Some(0),
head: Some(1),
tail: Some(2),
}
);
}
#[test]
fn remove_tail() {
let mut list = IndexList::new();
list.push_back(5);
list.push_back(10);
let fifteen = list.push_back(15);
let removed = list.remove(fifteen).unwrap();
assert_eq!(removed, 15);
assert_eq!(
list,
IndexList {
contents: vec![
Entry::Occupied(OccupiedEntry {
item: 5,
next: Some(1),
prev: None,
generation: 0,
}),
Entry::Occupied(OccupiedEntry {
item: 10,
next: None,
prev: Some(0),
generation: 0,
}),
Entry::Free { next_free: None },
],
generation: 1,
next_free: Some(2),
head: Some(0),
tail: Some(1),
}
);
}
#[test]
fn remove_only() {
let mut list = IndexList::new();
let five = list.push_back(5);
let removed = list.remove(five).unwrap();
assert_eq!(removed, 5);
assert_eq!(
list,
IndexList {
contents: vec![Entry::Free { next_free: None },],
generation: 1,
next_free: Some(0),
head: None,
tail: None,
}
);
}
#[test]
fn remove_returns_none_when_not_there() {
let mut list = IndexList::new();
let five_index = list.push_back(5);
let five_entry = list.remove(five_index).unwrap();
assert_eq!(list.contents[0], Entry::Free { next_free: None });
assert_eq!(five_entry, 5);
assert!(list.remove(five_index).is_none());
}
#[test]
fn iter() {
let mut list = IndexList::new();
list.push_back(5);
let ten = list.push_back(10);
list.push_back(15);
list.remove(ten);
let mut iter = list.iter();
assert_eq!(iter.next().unwrap(), &5);
assert_eq!(iter.next().unwrap(), &15);
assert!(iter.next().is_none());
}
#[test]
fn reallocation() {
let mut list = IndexList::new();
list.push_back(5);
let ten = list.push_back(10);
list.push_back(15);
let ten = list.remove(ten).unwrap();
assert_eq!(ten, 10);
list.push_back(20);
assert_eq!(
list.contents[0],
Entry::Occupied(OccupiedEntry {
item: 5,
next: Some(2),
prev: None,
generation: 0,
})
);
assert_eq!(
list.contents[1],
Entry::Occupied(OccupiedEntry {
item: 20,
next: None,
prev: Some(2),
generation: 1,
})
);
assert_eq!(
list.contents[2],
Entry::Occupied(OccupiedEntry {
item: 15,
next: Some(1),
prev: Some(0),
generation: 0,
})
);
}
#[test]
fn generations() {
let mut list = IndexList::new();
let five = list.push_back(5);
let ten = list.push_back(10);
list.push_back(15);
list.remove(ten);
let twenty = list.push_back(20);
assert!(list.get(ten).is_none());
assert!(list.get(five).is_some());
assert!(list.get(twenty).is_some());
}
#[test]
fn head() {
let mut list = IndexList::new();
assert!(list.head().is_none());
let five = list.push_back(5);
assert_eq!(list.head().unwrap(), &5);
list.push_back(10);
list.remove(five);
assert_eq!(list.head().unwrap(), &10);
assert_eq!(list.contents[0], Entry::Free { next_free: None });
assert_eq!(list.head, Some(1));
assert_eq!(
list.contents[1],
Entry::Occupied(OccupiedEntry {
item: 10,
next: None,
prev: None,
generation: 0,
})
);
}
#[test]
fn push_front() {
let mut list = IndexList::new();
list.push_front(5);
list.push_front(10);
list.push_front(15);
assert_eq!(
list.contents[0],
Entry::Occupied(OccupiedEntry {
item: 5,
next: None,
prev: Some(1),
generation: 0,
})
);
assert_eq!(
list.contents[1],
Entry::Occupied(OccupiedEntry {
item: 10,
next: Some(0),
prev: Some(2),
generation: 0,
})
);
assert_eq!(
list.contents[2],
Entry::Occupied(OccupiedEntry {
item: 15,
next: Some(1),
prev: None,
generation: 0,
})
);
}
#[test]
fn index_of() {
let mut list = IndexList::new();
list.push_back(5);
list.push_back(10);
list.push_back(15);
assert_eq!(
list.index_of(&10).unwrap(),
Index {
index: 1,
generation: 0,
}
);
assert!(list.index_of(&20).is_none());
}
#[test]
fn pop_front() {
let mut list = IndexList::new();
list.push_back(5);
list.push_back(10);
list.push_back(15);
assert_eq!(list.pop_front().unwrap(), 5);
assert_eq!(list.pop_front().unwrap(), 10);
assert_eq!(list.pop_front().unwrap(), 15);
assert_eq!(
list,
IndexList {
contents: vec![
Entry::Free { next_free: None },
Entry::Free { next_free: Some(0) },
Entry::Free { next_free: Some(1) },
],
generation: 3,
next_free: Some(2),
head: None,
tail: None,
}
);
}
#[test]
fn push_and_pop() {
let mut list = IndexList::new();
list.push_back(5);
list.push_back(10);
list.push_back(15);
assert_eq!(list.pop_front().unwrap(), 5);
assert_eq!(list.pop_front().unwrap(), 10);
assert_eq!(list.pop_front().unwrap(), 15);
list.push_back(5);
list.push_back(10);
list.push_back(15);
assert_eq!(list.pop_front().unwrap(), 5);
assert_eq!(list.pop_front().unwrap(), 10);
assert_eq!(list.pop_front().unwrap(), 15);
assert_eq!(
list,
IndexList {
contents: vec![
Entry::Free { next_free: Some(1) },
Entry::Free { next_free: Some(2) },
Entry::Free { next_free: None },
],
generation: 6,
next_free: Some(0),
head: None,
tail: None,
}
);
}
}