Remove TinyList.

It is optimized for lists with a single element, avoiding the need for
an allocation in that case. But `SmallVec<[T; 1]>` also avoids the
allocation, and is better in general: more standard, log2 number of
allocations if the list exceeds one item, and a much more capable API.

This commit removes `TinyList` and converts the two uses to
`SmallVec<[T; 1]>`. It also reorders the `use` items in the relevant
file so they are in just two sections (`pub` and non-`pub`), ordered
alphabetically, instead of many sections. (This is a relevant part of
the change because I had to decide where to add a `use` item for
`SmallVec`.)
This commit is contained in:
Nicholas Nethercote 2024-05-07 08:05:02 +10:00
parent d7814e72eb
commit f5d7d346a4
4 changed files with 8 additions and 245 deletions

View file

@ -82,7 +82,6 @@ pub mod svh;
pub mod sync;
pub mod tagged_ptr;
pub mod temp_dir;
pub mod tiny_list;
pub mod transitive_relation;
pub mod unhash;
pub mod unord;

View file

@ -1,80 +0,0 @@
//! A singly-linked list.
//!
//! Using this data structure only makes sense under very specific
//! circumstances:
//!
//! - If you have a list that rarely stores more than one element, then this
//! data-structure can store the element without allocating and only uses as
//! much space as an `Option<(T, usize)>`. If T can double as the `Option`
//! discriminant, it will even only be as large as `T, usize`.
//!
//! If you expect to store more than 1 element in the common case, steer clear
//! and use a `Vec<T>`, `Box<[T]>`, or a `SmallVec<T>`.
#[cfg(test)]
mod tests;
#[derive(Clone)]
pub struct TinyList<T> {
head: Option<Element<T>>,
}
impl<T: PartialEq> TinyList<T> {
#[inline]
pub fn new() -> TinyList<T> {
TinyList { head: None }
}
#[inline]
pub fn new_single(data: T) -> TinyList<T> {
TinyList { head: Some(Element { data, next: None }) }
}
#[inline]
pub fn insert(&mut self, data: T) {
self.head = Some(Element { data, next: self.head.take().map(Box::new) });
}
#[inline]
pub fn remove(&mut self, data: &T) -> bool {
self.head = match &mut self.head {
Some(head) if head.data == *data => head.next.take().map(|x| *x),
Some(head) => return head.remove_next(data),
None => return false,
};
true
}
#[inline]
pub fn contains(&self, data: &T) -> bool {
let mut elem = self.head.as_ref();
while let Some(e) = elem {
if &e.data == data {
return true;
}
elem = e.next.as_deref();
}
false
}
}
#[derive(Clone)]
struct Element<T> {
data: T,
next: Option<Box<Element<T>>>,
}
impl<T: PartialEq> Element<T> {
fn remove_next(mut self: &mut Self, data: &T) -> bool {
loop {
match self.next {
Some(ref mut next) if next.data == *data => {
self.next = next.next.take();
return true;
}
Some(ref mut next) => self = next,
None => return false,
}
}
}
}

View file

@ -1,155 +0,0 @@
use super::*;
extern crate test;
use test::{black_box, Bencher};
impl<T> TinyList<T> {
fn len(&self) -> usize {
let (mut elem, mut count) = (self.head.as_ref(), 0);
while let Some(e) = elem {
count += 1;
elem = e.next.as_deref();
}
count
}
}
#[test]
fn test_contains_and_insert() {
fn do_insert(i: u32) -> bool {
i % 2 == 0
}
let mut list = TinyList::new();
for i in 0..10 {
for j in 0..i {
if do_insert(j) {
assert!(list.contains(&j));
} else {
assert!(!list.contains(&j));
}
}
assert!(!list.contains(&i));
if do_insert(i) {
list.insert(i);
assert!(list.contains(&i));
}
}
}
#[test]
fn test_remove_first() {
let mut list = TinyList::new();
list.insert(1);
list.insert(2);
list.insert(3);
list.insert(4);
assert_eq!(list.len(), 4);
assert!(list.remove(&4));
assert!(!list.contains(&4));
assert_eq!(list.len(), 3);
assert!(list.contains(&1));
assert!(list.contains(&2));
assert!(list.contains(&3));
}
#[test]
fn test_remove_last() {
let mut list = TinyList::new();
list.insert(1);
list.insert(2);
list.insert(3);
list.insert(4);
assert_eq!(list.len(), 4);
assert!(list.remove(&1));
assert!(!list.contains(&1));
assert_eq!(list.len(), 3);
assert!(list.contains(&2));
assert!(list.contains(&3));
assert!(list.contains(&4));
}
#[test]
fn test_remove_middle() {
let mut list = TinyList::new();
list.insert(1);
list.insert(2);
list.insert(3);
list.insert(4);
assert_eq!(list.len(), 4);
assert!(list.remove(&2));
assert!(!list.contains(&2));
assert_eq!(list.len(), 3);
assert!(list.contains(&1));
assert!(list.contains(&3));
assert!(list.contains(&4));
}
#[test]
fn test_remove_single() {
let mut list = TinyList::new();
list.insert(1);
assert_eq!(list.len(), 1);
assert!(list.remove(&1));
assert!(!list.contains(&1));
assert_eq!(list.len(), 0);
}
#[bench]
fn bench_insert_empty(b: &mut Bencher) {
b.iter(|| {
let mut list = black_box(TinyList::new());
list.insert(1);
list
})
}
#[bench]
fn bench_insert_one(b: &mut Bencher) {
b.iter(|| {
let mut list = black_box(TinyList::new_single(0));
list.insert(1);
list
})
}
#[bench]
fn bench_contains_empty(b: &mut Bencher) {
b.iter(|| black_box(TinyList::new()).contains(&1));
}
#[bench]
fn bench_contains_unknown(b: &mut Bencher) {
b.iter(|| black_box(TinyList::new_single(0)).contains(&1));
}
#[bench]
fn bench_contains_one(b: &mut Bencher) {
b.iter(|| black_box(TinyList::new_single(1)).contains(&1));
}
#[bench]
fn bench_remove_empty(b: &mut Bencher) {
b.iter(|| black_box(TinyList::new()).remove(&1));
}
#[bench]
fn bench_remove_unknown(b: &mut Bencher) {
b.iter(|| black_box(TinyList::new_single(0)).remove(&1));
}
#[bench]
fn bench_remove_one(b: &mut Bencher) {
b.iter(|| black_box(TinyList::new_single(1)).remove(&1));
}