blob: d9a7ece9c5826b964e13915e21a0c10840a663fc [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::constants::SENTINEL;
use slab::Slab;
struct Node<T> {
value: T,
next: usize,
prev: usize,
}
/// Maintain a list of objects referencable by an id.
/// Used for implementing the waiter list for cutex.
pub(crate) struct List<T> {
slab: Slab<Node<T>>,
first: usize,
last: usize,
}
impl<T: std::fmt::Debug> std::fmt::Debug for List<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_list().entries(self.iter()).finish()
}
}
impl<T> List<T> {
pub(crate) fn new() -> List<T> {
List { slab: Slab::new(), first: SENTINEL, last: SENTINEL }
}
pub(crate) fn get_mut(&mut self, id: usize) -> &mut T {
&mut self.slab[id].value
}
pub(crate) fn push(&mut self, value: T) -> (usize, bool) {
match (self.first, self.last) {
(SENTINEL, SENTINEL) => {
let id = self.slab.insert(Node { value, next: SENTINEL, prev: SENTINEL });
self.first = id;
self.last = id;
(id, true)
}
(_, SENTINEL) | (SENTINEL, _) => {
unreachable!("first/last should either be both SENTINEL or both node indices")
}
(_, prev) => {
let id = self.slab.insert(Node { value, next: SENTINEL, prev });
self.slab[prev].next = id;
self.last = id;
(id, false)
}
}
}
pub(crate) fn iter(&self) -> Iter<'_, T> {
Iter { slab: &self.slab, next: self.first }
}
// Run `f` on each list element.
// `f` is passed the id of the element and a mutable reference to its value.
// if `f` returns `false`, the iteration short circuits.
// Returns true if all elements were visited, false if the iteration was short circuited.
pub(crate) fn for_each_until_mut(&mut self, f: impl Fn(usize, &mut T) -> bool) -> bool {
let mut cur = self.first;
while cur != SENTINEL {
let n = &mut self.slab[cur];
if !f(cur, &mut n.value) {
return false;
}
cur = n.next;
}
return true;
}
pub(crate) fn remove(&mut self, id: usize) -> (T, bool) {
let n = self.slab.remove(id);
let is_last = if n.prev == SENTINEL {
if n.next == SENTINEL {
self.first = SENTINEL;
self.last = SENTINEL;
true
} else {
self.slab[n.next].prev = SENTINEL;
self.first = n.next;
false
}
} else if n.next == SENTINEL {
self.slab[n.prev].next = SENTINEL;
self.last = n.prev;
false
} else {
self.slab[n.next].prev = n.prev;
self.slab[n.prev].next = n.next;
false
};
(n.value, is_last)
}
#[cfg(test)]
fn to_vec(&self) -> Vec<T>
where
T: Clone,
{
self.iter().map(Clone::clone).collect()
}
}
pub(crate) struct Iter<'a, T> {
slab: &'a Slab<Node<T>>,
next: usize,
}
impl<'a, T> std::iter::Iterator for Iter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<&'a T> {
if self.next == SENTINEL {
None
} else {
let n = &self.slab[self.next];
self.next = n.next;
Some(&n.value)
}
}
}
#[cfg(test)]
mod tests {
use super::List;
#[test]
fn works() {
let mut l = List::new();
let (idx0, first) = l.push(0u8);
assert_eq!(first, true);
assert_eq!(l.to_vec(), vec![0u8]);
let (idx1, first) = l.push(1u8);
assert_eq!(first, false);
assert_eq!(l.to_vec(), vec![0u8, 1u8]);
assert_eq!(l.remove(idx0), (0u8, false));
assert_eq!(l.to_vec(), vec![1u8]);
let (idx2, first) = l.push(2u8);
assert_eq!(first, false);
assert_eq!(l.to_vec(), vec![1u8, 2u8]);
assert_eq!(l.remove(idx2), (2u8, false));
assert_eq!(l.to_vec(), vec![1u8]);
assert_eq!(l.remove(idx1), (1u8, true));
assert_eq!(l.to_vec(), vec![]);
}
}