use std::cmp::Ordering;
use std::ptr::NonNull;
#[derive(Debug)]
struct FixedSizeListNode<T> {
prev: usize,
next: usize,
data: T,
}
#[derive(Debug)]
pub(crate) struct FixedSizeList<T> {
capacity: usize,
nodes: Vec<Option<FixedSizeListNode<T>>>,
free: Vec<usize>,
front: usize,
back: usize,
}
impl<T> FixedSizeList<T> {
#[inline]
pub(crate) fn new(capacity: usize) -> Self {
Self {
capacity,
nodes: Vec::new(),
free: Vec::new(),
front: usize::MAX,
back: usize::MAX,
}
}
#[inline]
pub(crate) fn with_memory(capacity: usize, mut reserve: usize) -> Self {
if reserve > capacity {
reserve = capacity;
}
Self {
capacity,
nodes: Vec::with_capacity(reserve),
free: Vec::new(),
front: usize::MAX,
back: usize::MAX,
}
}
#[inline]
pub(crate) fn capacity(&self) -> usize {
self.capacity
}
#[inline]
pub(crate) fn len(&self) -> usize {
self.nodes.len() - self.free.len()
}
#[inline]
pub(crate) fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub(crate) fn is_full(&self) -> bool {
self.len() == self.capacity()
}
pub(crate) fn clear(&mut self) {
self.nodes.clear();
self.free.clear();
self.front = usize::MAX;
self.back = usize::MAX;
}
fn next(&mut self) -> Option<usize> {
if self.is_full() {
None
} else if self.free.is_empty() {
let len = self.len();
self.nodes.push(None);
Some(len)
} else {
self.free.pop()
}
}
#[inline]
fn node_ref(&self, idx: usize) -> Option<&FixedSizeListNode<T>> {
self.nodes.get(idx).and_then(|node| node.as_ref())
}
#[inline]
pub(crate) fn get(&self, idx: usize) -> Option<&T> {
self.node_ref(idx).map(|node| &node.data)
}
#[inline]
fn node_mut(&mut self, idx: usize) -> Option<&mut FixedSizeListNode<T>> {
self.nodes.get_mut(idx).and_then(|node| node.as_mut())
}
#[inline]
pub(crate) fn get_mut(&mut self, idx: usize) -> Option<&mut T> {
self.node_mut(idx).map(|node| &mut node.data)
}
#[inline]
pub(crate) fn front(&self) -> Option<&T> {
self.node_ref(self.front).map(|node| &node.data)
}
#[inline]
pub(crate) fn front_mut(&mut self) -> Option<&mut T> {
self.node_mut(self.front).map(|node| &mut node.data)
}
#[inline]
pub(crate) fn back_idx(&self) -> usize {
self.back
}
#[inline]
pub(crate) fn back(&self) -> Option<&T> {
self.node_ref(self.back).map(|node| &node.data)
}
#[inline]
pub(crate) fn back_mut(&mut self) -> Option<&mut T> {
self.node_mut(self.back).map(|node| &mut node.data)
}
pub(crate) fn push_front(&mut self, data: T) -> Option<(usize, &mut T)> {
let idx = self.next()?;
if let Some(front) = self.node_mut(self.front) {
front.prev = idx;
}
if self.node_ref(self.back).is_none() {
self.back = idx;
}
let node = self.nodes.get_mut(idx).unwrap().insert(FixedSizeListNode {
prev: usize::MAX,
next: self.front,
data,
});
self.front = idx;
Some((idx, &mut node.data))
}
#[cfg(test)]
fn push_back(&mut self, data: T) -> Option<(usize, &mut T)> {
let idx = self.next()?;
if let Some(back) = self.node_mut(self.back) {
back.next = idx;
}
if self.node_ref(self.front).is_none() {
self.front = idx;
}
let node = self.nodes.get_mut(idx).unwrap().insert(FixedSizeListNode {
prev: self.back,
next: usize::MAX,
data,
});
self.back = idx;
Some((idx, &mut node.data))
}
#[inline]
pub(crate) fn pop_front(&mut self) -> Option<T> {
self.remove(self.front)
}
#[inline]
pub(crate) fn pop_back(&mut self) -> Option<T> {
self.remove(self.back)
}
pub(crate) fn remove(&mut self, idx: usize) -> Option<T> {
let node = self.nodes.get_mut(idx)?.take()?;
if let Some(prev) = self.node_mut(node.prev) {
prev.next = node.next;
} else {
self.front = node.next;
}
if let Some(next) = self.node_mut(node.next) {
next.prev = node.prev;
} else {
self.back = node.prev;
}
self.free.push(idx);
Some(node.data)
}
#[inline]
pub(crate) fn iter(&self) -> FixedSizeListIter<'_, T> {
FixedSizeListIter {
list: self,
front: self.front,
back: self.back,
len: self.len(),
}
}
#[inline]
pub(crate) fn iter_mut(&mut self) -> FixedSizeListIterMut<'_, T> {
let front = self.front;
let back = self.back;
let len = self.len();
FixedSizeListIterMut::new(&mut self.nodes, front, back, len)
}
fn reorder(&mut self) {
if self.is_empty() {
return;
}
let len = self.len();
let mut current = 0;
while current < len {
let front = self.front;
let front_data = self.pop_front().unwrap();
if front != current {
debug_assert!(current < front, "{} < {}", current, front);
if let Some(current_node) = self.nodes[current].take() {
if let Some(node) = self.node_mut(current_node.prev) {
node.next = front;
} else {
self.front = front;
}
if let Some(node) = self.node_mut(current_node.next) {
node.prev = front;
} else {
self.back = front;
}
self.nodes[front] = Some(current_node);
}
}
self.nodes[current] = Some(FixedSizeListNode {
prev: current.wrapping_sub(1),
next: current + 1,
data: front_data,
});
current += 1;
}
self.front = 0;
self.nodes[len - 1].as_mut().unwrap().next = usize::MAX;
self.back = len - 1;
self.free.clear();
self.free.extend((len..self.nodes.len()).rev());
}
pub(crate) fn resize(&mut self, capacity: usize) {
let len = self.len();
let cap = self.capacity();
match capacity.cmp(&cap) {
Ordering::Less => {
self.reorder();
self.nodes.truncate(capacity);
self.free.clear();
self.free.extend(len..self.nodes.len());
self.capacity = capacity;
}
Ordering::Equal => {}
Ordering::Greater => {
self.capacity = capacity;
}
};
debug_assert_eq!(self.len(), len);
debug_assert_eq!(self.capacity(), capacity);
}
pub(crate) fn retain<F>(&mut self, mut f: F)
where
F: FnMut(&T) -> bool,
{
let mut front = self.front;
while front != usize::MAX {
let node = self.node_ref(front).unwrap();
let next = node.next;
if !f(&node.data) {
self.remove(front);
}
front = next;
}
}
pub(crate) fn retain_mut<F>(&mut self, mut f: F)
where
F: FnMut(&mut T) -> bool,
{
let mut front = self.front;
while front != usize::MAX {
let node = self.node_mut(front).unwrap();
let next = node.next;
if !f(&mut node.data) {
self.remove(front);
}
front = next;
}
}
#[inline]
pub(crate) fn move_front(&mut self, idx: usize) -> Option<&mut T> {
let node = self.nodes.get_mut(idx)?.take()?;
if let Some(prev) = self.node_mut(node.prev) {
prev.next = node.next;
} else {
self.front = node.next;
}
if let Some(next) = self.node_mut(node.next) {
next.prev = node.prev;
} else {
self.back = node.prev;
}
if let Some(front) = self.node_mut(self.front) {
front.prev = idx;
}
if self.node_ref(self.back).is_none() {
self.back = idx;
}
let node = self.nodes.get_mut(idx).unwrap().insert(FixedSizeListNode {
prev: usize::MAX,
next: self.front,
data: node.data,
});
self.front = idx;
Some(&mut node.data)
}
}
#[derive(Debug)]
pub(crate) struct FixedSizeListIter<'a, T> {
list: &'a FixedSizeList<T>,
front: usize,
back: usize,
len: usize,
}
impl<'a, T> Clone for FixedSizeListIter<'a, T> {
fn clone(&self) -> Self {
Self {
list: self.list,
front: self.front,
back: self.back,
len: self.len,
}
}
}
impl<'a, T> Iterator for FixedSizeListIter<'a, T> {
type Item = (usize, &'a T);
fn next(&mut self) -> Option<Self::Item> {
if self.len > 0 {
let front = self.front;
let node = self.list.node_ref(front).unwrap();
self.front = node.next;
self.len -= 1;
Some((front, &node.data))
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len, Some(self.len))
}
}
impl<'a, T> DoubleEndedIterator for FixedSizeListIter<'a, T> {
fn next_back(&mut self) -> Option<Self::Item> {
if self.len > 0 {
let back = self.back;
let node = self.list.node_ref(back).unwrap();
self.back = node.prev;
self.len -= 1;
Some((back, &node.data))
} else {
None
}
}
}
impl<'a, T> ExactSizeIterator for FixedSizeListIter<'a, T> {
fn len(&self) -> usize {
self.size_hint().0
}
}
pub(crate) struct FixedSizeListIterMut<'a, T> {
ptr: NonNull<Option<FixedSizeListNode<T>>>,
front: usize,
back: usize,
len: usize,
_marker: std::marker::PhantomData<&'a mut T>,
}
impl<'a, T> FixedSizeListIterMut<'a, T> {
#[allow(unsafe_code)]
fn new(
slice: &'a mut [Option<FixedSizeListNode<T>>],
front: usize,
back: usize,
len: usize,
) -> Self {
let ptr = slice.as_mut_ptr();
Self {
ptr: unsafe { NonNull::new_unchecked(ptr) },
front,
back,
len,
_marker: std::marker::PhantomData,
}
}
}
impl<'a, T> Iterator for FixedSizeListIterMut<'a, T> {
type Item = (usize, &'a mut T);
#[allow(unsafe_code)]
fn next(&mut self) -> Option<Self::Item> {
if self.len > 0 {
let front = self.front;
let node_ref = unsafe {
let ptr = NonNull::new_unchecked(self.ptr.as_ptr().add(front)).as_ptr();
&mut *ptr
};
let node = node_ref.as_mut().unwrap();
self.front = node.next;
self.len -= 1;
Some((front, &mut node.data))
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len, Some(self.len))
}
}
impl<'a, T> DoubleEndedIterator for FixedSizeListIterMut<'a, T> {
#[allow(unsafe_code)]
fn next_back(&mut self) -> Option<Self::Item> {
if self.len > 0 {
let back = self.back;
let node_ref = unsafe {
let ptr = NonNull::new_unchecked(self.ptr.as_ptr().add(back)).as_ptr();
&mut *ptr
};
let node = node_ref.as_mut().unwrap();
self.back = node.prev;
self.len -= 1;
Some((back, &mut node.data))
} else {
None
}
}
}
impl<'a, T> ExactSizeIterator for FixedSizeListIterMut<'a, T> {
fn len(&self) -> usize {
self.size_hint().0
}
}
#[cfg(test)]
#[allow(clippy::bool_assert_comparison)]
mod tests {
use super::*;
#[test]
fn test_fixed_size_list() {
let mut list = FixedSizeList::new(4);
assert!(list.is_empty());
assert_eq!(list.len(), 0);
assert_eq!(list.front(), None);
assert_eq!(list.front_mut(), None);
assert_eq!(list.back(), None);
assert_eq!(list.back_mut(), None);
assert_eq!(list.iter().count(), 0);
assert_eq!(list.iter().rev().count(), 0);
assert_eq!(list.push_front(7), Some((0, &mut 7)));
assert!(!list.is_empty());
assert_eq!(list.len(), 1);
assert_eq!(list.front(), Some(&7));
assert_eq!(list.front_mut(), Some(&mut 7));
assert_eq!(list.back(), Some(&7));
assert_eq!(list.back_mut(), Some(&mut 7));
assert_eq!(list.iter().collect::<Vec<_>>(), vec![(0, &7)]);
assert_eq!(list.iter().rev().collect::<Vec<_>>(), vec![(0, &7)]);
assert_eq!(list.push_front(5), Some((1, &mut 5)));
assert!(!list.is_empty());
assert_eq!(list.len(), 2);
assert_eq!(list.front(), Some(&5));
assert_eq!(list.front_mut(), Some(&mut 5));
assert_eq!(list.back(), Some(&7));
assert_eq!(list.back_mut(), Some(&mut 7));
assert_eq!(list.iter().collect::<Vec<_>>(), vec![(1, &5), (0, &7)]);
assert_eq!(
list.iter().rev().collect::<Vec<_>>(),
vec![(0, &7), (1, &5)]
);
assert_eq!(list.push_front(3), Some((2, &mut 3)));
assert!(!list.is_empty());
assert_eq!(list.len(), 3);
assert_eq!(list.front(), Some(&3));
assert_eq!(list.front_mut(), Some(&mut 3));
assert_eq!(list.back(), Some(&7));
assert_eq!(list.back_mut(), Some(&mut 7));
assert_eq!(
list.iter().collect::<Vec<_>>(),
vec![(2, &3), (1, &5), (0, &7)]
);
assert_eq!(
list.iter().rev().collect::<Vec<_>>(),
vec![(0, &7), (1, &5), (2, &3)]
);
list.remove(1);
assert!(!list.is_empty());
assert_eq!(list.len(), 2);
assert_eq!(list.front(), Some(&3));
assert_eq!(list.front_mut(), Some(&mut 3));
assert_eq!(list.back(), Some(&7));
assert_eq!(list.back_mut(), Some(&mut 7));
assert_eq!(list.iter().collect::<Vec<_>>(), vec![(2, &3), (0, &7)]);
assert_eq!(
list.iter().rev().collect::<Vec<_>>(),
vec![(0, &7), (2, &3)]
);
list.remove(0);
assert!(!list.is_empty());
assert_eq!(list.len(), 1);
assert_eq!(list.front(), Some(&3));
assert_eq!(list.front_mut(), Some(&mut 3));
assert_eq!(list.back(), Some(&3));
assert_eq!(list.back_mut(), Some(&mut 3));
assert_eq!(list.iter().collect::<Vec<_>>(), vec![(2, &3)]);
assert_eq!(list.iter().rev().collect::<Vec<_>>(), vec![(2, &3)]);
list.remove(2);
assert!(list.is_empty());
assert_eq!(list.len(), 0);
assert_eq!(list.front(), None);
assert_eq!(list.front_mut(), None);
assert_eq!(list.back(), None);
assert_eq!(list.back_mut(), None);
assert_eq!(list.iter().count(), 0);
assert_eq!(list.iter().rev().count(), 0);
}
#[test]
fn test_fixed_size_list_reorder() {
let mut list = FixedSizeList::new(4);
list.push_back('a');
list.push_front('b');
list.push_back('c');
list.push_front('d');
assert_eq!(
list.iter().collect::<Vec<_>>(),
vec![(3, &'d'), (1, &'b'), (0, &'a'), (2, &'c')]
);
list.reorder();
assert_eq!(
list.iter().collect::<Vec<_>>(),
vec![(0, &'d'), (1, &'b'), (2, &'a'), (3, &'c')]
);
}
}