Skip to content

Commit

Permalink
Add TinyList data structure.
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelwoerister committed Jun 1, 2018
1 parent 63cd4a3 commit f9f90ed
Show file tree
Hide file tree
Showing 2 changed files with 252 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/librustc_data_structures/lib.rs
Expand Up @@ -74,6 +74,7 @@ pub mod control_flow_graph;
pub mod flock;
pub mod sync;
pub mod owning_ref;
pub mod tiny_list;
pub mod sorted_map;

pub struct OnDrop<F: Fn()>(pub F);
Expand Down
251 changes: 251 additions & 0 deletions src/librustc_data_structures/tiny_list.rs
@@ -0,0 +1,251 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.


//! 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 a `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>`.

use std::mem;

#[derive(Clone, Hash, Debug, PartialEq)]
pub struct TinyList<T: PartialEq> {
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) {
let current_head = mem::replace(&mut self.head, None);

if let Some(current_head) = current_head {
let current_head = Box::new(current_head);
self.head = Some(Element {
data,
next: Some(current_head)
});
} else {
self.head = Some(Element {
data,
next: None,
})
}
}

#[inline]
pub fn remove(&mut self, data: &T) -> bool {
let remove_head = if let Some(ref mut head) = self.head {
if head.data == *data {
Some(mem::replace(&mut head.next, None))
} else {
None
}
} else {
return false
};

if let Some(remove_head) = remove_head {
if let Some(next) = remove_head {
self.head = Some(*next);
} else {
self.head = None;
}
return true
}

self.head.as_mut().unwrap().remove_next(data)
}

#[inline]
pub fn contains(&self, data: &T) -> bool {
if let Some(ref head) = self.head {
head.contains(data)
} else {
false
}
}

#[inline]
pub fn len(&self) -> usize {
if let Some(ref head) = self.head {
head.len()
} else {
0
}
}
}

#[derive(Clone, Hash, Debug, PartialEq)]
struct Element<T: PartialEq> {
data: T,
next: Option<Box<Element<T>>>,
}

impl<T: PartialEq> Element<T> {

fn remove_next(&mut self, data: &T) -> bool {
let new_next = if let Some(ref mut next) = self.next {
if next.data != *data {
return next.remove_next(data)
} else {
mem::replace(&mut next.next, None)
}
} else {
return false
};

self.next = new_next;
return true
}

fn len(&self) -> usize {
if let Some(ref next) = self.next {
1 + next.len()
} else {
1
}
}

fn contains(&self, data: &T) -> bool {
if self.data == *data {
return true
}

if let Some(ref next) = self.next {
next.contains(data)
} else {
false
}
}
}

#[cfg(test)]
mod test {
use super::*;

#[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);
}
}

0 comments on commit f9f90ed

Please sign in to comment.