From eb9e1e8f2b8c83fdaa2efbfba6047a6f6f31a142 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Mon, 5 Aug 2024 05:51:54 +0000 Subject: [PATCH] Use index_list crate instead of copied indexlist code --- Cargo.lock | 7 + bigtools/Cargo.toml | 3 +- bigtools/src/bbi/bigbedwrite.rs | 54 +- bigtools/src/bed/indexer.rs | 32 +- bigtools/src/utils/indexlist.rs | 1980 ------------------------------- bigtools/src/utils/mod.rs | 1 - 6 files changed, 58 insertions(+), 2019 deletions(-) delete mode 100644 bigtools/src/utils/indexlist.rs diff --git a/Cargo.lock b/Cargo.lock index b39bc1a..ece1036 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -127,6 +127,7 @@ dependencies = [ "crossbeam-channel", "crossbeam-utils", "futures", + "index_list", "itertools", "libdeflater", "rand", @@ -469,6 +470,12 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "index_list" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e6ba961c14e98151cd6416dd3685efe786a94c38bc1a535c06ceff0a1600813" + [[package]] name = "indoc" version = "1.0.9" diff --git a/bigtools/Cargo.toml b/bigtools/Cargo.toml index 2798f26..6e5a1f3 100644 --- a/bigtools/Cargo.toml +++ b/bigtools/Cargo.toml @@ -30,6 +30,7 @@ ufmt = { version = "0.2", features = ["std"], optional = true } bytes = { version = "1.4.0", optional = true } tokio = { version = "1.34.0", features = ["rt", "rt-multi-thread"] } smallvec = { version = "1.11.2", features = ["write"] } +index_list = { version = "0.2.13", optional = true } [dev-dependencies] rand = "0.8" @@ -83,4 +84,4 @@ default = ["remote", "read", "write", "cli"] remote = ["attohttpc", "tempfile"] cli = ["anyhow", "clap", "ryu", "ufmt", "read", "write"] read = ["bytes", "itertools"] -write = ["crossbeam-channel", "tempfile", "futures", "serde", "itertools", "bincode"] +write = ["crossbeam-channel", "tempfile", "futures", "serde", "itertools", "bincode", "index_list"] diff --git a/bigtools/src/bbi/bigbedwrite.rs b/bigtools/src/bbi/bigbedwrite.rs index 81fdf7e..05ba23c 100644 --- a/bigtools/src/bbi/bigbedwrite.rs +++ b/bigtools/src/bbi/bigbedwrite.rs @@ -10,7 +10,6 @@ use byteorder::{NativeEndian, WriteBytesExt}; use tokio::runtime::{Handle, Runtime}; use crate::bbiwrite::process_internal::BBIDataProcessorCreate; -use crate::utils::indexlist::IndexList; use crate::utils::tell::Tell; use crate::{ write_info, BBIDataProcessor, BBIDataProcessoredData, BBIDataProcessoringInputSectionChannel, @@ -18,6 +17,7 @@ use crate::{ NoZoomsInternalProcessedData, ProcessDataError, ZoomsInternalProcessData, ZoomsInternalProcessedData, }; +use index_list::IndexList; use crate::bbi::{BedEntry, Summary, Value, ZoomRecord, BIGBED_MAGIC}; use crate::bbiwrite::{ @@ -294,15 +294,15 @@ async fn process_val( // If any overlaps exists, it must be starting at the current start (else it would have to be after the current entry) // If the overlap starts before, the entry wasn't correctly cut last iteration debug_assert!(overlap - .head() + .get_first() .map(|f| f.start == item_start) .unwrap_or(true)); // For each item in `overlap` that overlaps the current // item, add `1` to the value. - let mut index = overlap.head_index(); - while let Some(i) = index { - match overlap.get_mut(i) { + let mut index = overlap.first_index(); + while index.is_some() { + match overlap.get_mut(index) { None => break, Some(o) => { o.value += 1.0; @@ -311,7 +311,7 @@ async fn process_val( let end = o.end; o.end = item_end; overlap.insert_after( - i, + index, Value { start: item_end, end, @@ -320,15 +320,18 @@ async fn process_val( ); break; } - index = overlap.next_index(i); + index = overlap.next_index(index); } } } - debug_assert!(overlap.tail().map(|o| o.end >= item_start).unwrap_or(true)); + debug_assert!(overlap + .get_last() + .map(|o| o.end >= item_start) + .unwrap_or(true)); - if overlap.tail().map(|o| o.end).unwrap_or(item_start) == item_start { - overlap.push_back(Value { + if overlap.get_last().map(|o| o.end).unwrap_or(item_start) == item_start { + overlap.insert_last(Value { start: item_start, end: item_end, value: 1.0, @@ -338,18 +341,18 @@ async fn process_val( let next_start = next_start_opt.unwrap_or(u32::max_value()); while overlap - .head() + .get_first() .map(|f| f.start < next_start) .unwrap_or(false) { - let mut removed = overlap.pop_front().unwrap(); + let mut removed = overlap.remove_first().unwrap(); let (len, val) = if removed.end <= next_start { (removed.end - removed.start, f64::from(removed.value)) } else { let len = next_start - removed.start; let val = f64::from(removed.value); removed.start = next_start; - overlap.push_front(removed); + overlap.insert_first(removed); (len, val) }; @@ -411,9 +414,9 @@ async fn process_val_zoom( // For each item in `overlap` that overlaps the current // item, add `1` to the value. - let mut index = overlap.head_index(); - while let Some(i) = index { - match overlap.get_mut(i) { + let mut index = overlap.first_index(); + while index.is_some() { + match overlap.get_mut(index) { None => break, Some(o) => { o.value += 1.0; @@ -422,7 +425,7 @@ async fn process_val_zoom( let end = o.end; o.end = item_end; overlap.insert_after( - i, + index, Value { start: item_end, end, @@ -431,15 +434,18 @@ async fn process_val_zoom( ); break; } - index = overlap.next_index(i); + index = overlap.next_index(index); } } } - debug_assert!(overlap.tail().map(|o| o.end >= item_start).unwrap_or(true)); + debug_assert!(overlap + .get_last() + .map(|o| o.end >= item_start) + .unwrap_or(true)); - if overlap.tail().map(|o| o.end).unwrap_or(item_start) == item_start { - overlap.push_back(Value { + if overlap.get_last().map(|o| o.end).unwrap_or(item_start) == item_start { + overlap.insert_last(Value { start: item_start, end: item_end, value: 1.0, @@ -449,18 +455,18 @@ async fn process_val_zoom( let next_start = next_val.map(|v| v.start).unwrap_or(u32::max_value()); while overlap - .head() + .get_first() .map(|f| f.start < next_start) .unwrap_or(false) { - let mut removed = overlap.pop_front().unwrap(); + let mut removed = overlap.remove_first().unwrap(); let val = f64::from(removed.value); let (removed_start, removed_end) = if removed.end <= next_start { (removed.start, removed.end) } else { let start = removed.start; removed.start = next_start; - overlap.push_front(removed); + overlap.insert_first(removed); (start, next_start) }; diff --git a/bigtools/src/bed/indexer.rs b/bigtools/src/bed/indexer.rs index 47f83d8..ce93652 100644 --- a/bigtools/src/bed/indexer.rs +++ b/bigtools/src/bed/indexer.rs @@ -4,7 +4,8 @@ Utilities for indexing a bed file that is start-sorted (chromosomes may be out o use std::fs::File; use std::io::{self, BufRead, BufReader, Seek, SeekFrom}; -use crate::utils::indexlist::{Index, IndexList}; +use index_list::{IndexList, ListIndex}; + use crate::utils::tell::Tell; /// Returns a Vec of offsets into a bed file, and the chromosome starting at each offset. @@ -49,7 +50,7 @@ pub fn index_chroms(file: File) -> io::Result>> { } let chrom = parse_line(&line)?.unwrap(); - let first = chroms.push_front((0, chrom)); + let first = chroms.insert_first((0, chrom)); let file_size = file.seek(SeekFrom::End(0))?; fn do_index( @@ -57,16 +58,18 @@ pub fn index_chroms(file: File) -> io::Result>> { file: &mut BufReader, chroms: &mut IndexList<(u64, String)>, line: &mut String, - prev: Index<(u64, String)>, - next: Option>, + prev: ListIndex, + next: Option, limit: usize, ) -> Result<(), io::Error> { if limit == 0 { panic!("Recursive depth limit reached"); } - let next_tell = next.map(|next| chroms[next].0).unwrap_or(file_size); - let mid = (next_tell + chroms[prev].0) / 2; + let next_tell = next + .map(|next| chroms.get(next).unwrap().0) + .unwrap_or(file_size); + let mid = (next_tell + chroms.get(prev).unwrap().0) / 2; file.seek(SeekFrom::Start(mid))?; file.read_line(line)?; line.clear(); @@ -97,11 +100,14 @@ pub fn index_chroms(file: File) -> io::Result>> { // to continue to index between the previous and current as well as // between the current and next. - let curr = chroms.insert_after(prev, (tell, chrom)).unwrap(); + let curr = chroms.insert_after(prev, (tell, chrom)); - let left = chroms[curr].1 != chroms[prev].1 && tell < next_tell; + let left = chroms.get(curr).unwrap().1 != chroms.get(prev).unwrap().1 && tell < next_tell; let right = next - .map(|next| chroms[curr].1 != chroms[next].1 && tell < chroms[next].0) + .map(|next| { + chroms.get(curr).unwrap().1 != chroms.get(next).unwrap().1 + && tell < chroms.get(next).unwrap().0 + }) .unwrap_or(true); if left { @@ -112,8 +118,8 @@ pub fn index_chroms(file: File) -> io::Result>> { do_index(file_size, file, chroms, line, curr, next, limit - 1)?; } - if chroms[curr].1 != chroms[prev].1 && tell == next_tell { - file.seek(SeekFrom::Start(chroms[prev].0))?; + if chroms.get(curr).unwrap().1 != chroms.get(prev).unwrap().1 && tell == next_tell { + file.seek(SeekFrom::Start(chroms.get(prev).unwrap().0))?; line.clear(); file.read_line(line)?; line.clear(); @@ -121,7 +127,7 @@ pub fn index_chroms(file: File) -> io::Result>> { file.read_line(line)?; let chrom = parse_line(&*line)? .expect("Bad logic. Must at least find last entry for chromosome."); - chroms.insert_after(prev, (tell, chrom)).unwrap(); + chroms.insert_after(prev, (tell, chrom)); } Ok(()) } @@ -136,7 +142,7 @@ pub fn index_chroms(file: File) -> io::Result>> { 100, )?; - let mut chroms: Vec<_> = chroms.into_iter().collect(); + let mut chroms: Vec<_> = chroms.drain_iter().collect(); chroms.dedup_by_key(|index| index.1.clone()); let mut deduped_chroms = chroms.clone(); deduped_chroms.sort(); diff --git a/bigtools/src/utils/indexlist.rs b/bigtools/src/utils/indexlist.rs deleted file mode 100644 index ae27f9e..0000000 --- a/bigtools/src/utils/indexlist.rs +++ /dev/null @@ -1,1980 +0,0 @@ -//! A doubly linked list, backed by a vector. -//! -//! This crate provides a struct, `IndexList`, which is a doubly-linked -//! list. However, unlike a traditional linked list, which heap allocates -//! each of its nodes individually, all nodes are stored in a vector. Rather -//! than provide pointers to nodes, an `Index` struct can be used to access -//! a particular element in the middle of the list. -//! -//! # Safety -//! -//! This crate uses `#![deny(unsafe_code)]` to ensure everything is implemented -//! in 100% Safe Rust. -//! -//! # Generational indexes -//! -//! `Index` uses a generations scheme, so that if you hold an `Index` to a node, -//! and it's removed, and a new node is allocated in its place, you do not access -//! the new node. -//! -//! # Performance -//! -//! In general, performance is quite good. Benchmarks against the standard library's -//! `LinkedList` are provided. But some other details: -//! -//! * The list keeps track of its head and tail for efficient insertion. -//! * The underlying vector only grows, never shrinks. When a node is removed, its -//! entry is marked as free for future insertions. -//! * Free entries are themselves kept as a singly-linked list, meaning that they -//! can be re-used efficiently. -//! -//! # Missing features -//! -//! Right now, I've only implemented a minimal number of features; there's `iter` -//! and `into_iter` but no `iter_mut`. This is on the to-do list. PRs welcome! -//! -//! # Examples -//! -//! Creating a list, appending nodes, and printing them out: -//! -//! ``` -//! use bigtools::utils::indexlist::IndexList; -//! -//! let mut list = IndexList::new(); -//! -//! list.push_back(5); -//! list.push_back(10); -//! list.push_back(15); -//! -//! // This prints 5, 10, and then 15, each on its own line -//! for element in list.iter() { -//! println!("{}", element); -//! } -//! ``` -//! -//! Removing an item from the list: -//! -//! ``` -//! use bigtools::utils::indexlist::IndexList; -//! -//! let mut list = IndexList::new(); -//! -//! let five = list.push_back(5); -//! list.push_back(10); -//! -//! list.remove(five); -//! -//! // 5 is no longer in the list -//! assert!(list.get(five).is_none()); -//! ``` -//! -//! Generational indexes: -//! -//! ``` -//! use bigtools::utils::indexlist::IndexList; -//! -//! let mut list = IndexList::new(); -//! -//! let five = list.push_back(5); -//! list.push_back(10); -//! -//! list.remove(five); -//! -//! // since we have a free spot, this will go where 5 was -//! list.push_back(15); -//! -//! // our index is out of date, and so will not return 15 here -//! assert!(list.get(five).is_none()); -//! ``` - -#![deny(unsafe_code)] -use std::marker::PhantomData; - -/// A doubly linked list, backed by a vector. -/// -/// See the crate documentation for more. -#[derive(Debug, PartialEq)] -pub struct IndexList { - contents: Vec>, - generation: usize, - next_free: Option, - head: Option, - tail: Option, -} - -#[derive(Debug, PartialEq)] -enum Entry { - Free { next_free: Option }, - Occupied(OccupiedEntry), -} - -#[derive(Debug, PartialEq)] -struct OccupiedEntry { - item: T, - generation: usize, - next: Option, - prev: Option, -} - -/// A reference to an element in the list. -/// -/// If you have an `Index`, you can get or remove the item at that position in -/// the list. -/// -/// # Generational indexes -/// -/// `Index` employs a "generational index" scheme. A "generation" is a counter, -/// saved by the `IndexList`. This counter increases whenever an item is -/// removed from the list. Each item in the list keeps track of the generation -/// it was inserted in. -/// -/// An Index also keeps track of a generation. When you attempt to manipulate an -/// item in the list via an `Index`, the generations are compared. If the -/// `Index`'s generation is older than the item at that position, it is stale, -/// and so that item will not be returned or removed. -/// -/// This scheme lets us re-use removed slots in the list, while ensuring that -/// you won't see bad data. -/// -/// # Examples -/// -/// You can get an `Index` by inserting something into the list: -/// -/// ``` -/// use bigtools::utils::indexlist::IndexList; -/// -/// let mut list = IndexList::new(); -/// -/// // this is an Index -/// let index = list.push_back(5); -/// ``` -/// -/// You can also get one with `index_of`: -/// -/// ``` -/// use bigtools::utils::indexlist::IndexList; -/// -/// let mut list = IndexList::new(); -/// -/// let five = list.push_back(5); -/// -/// let index = list.index_of(&5); -/// -/// assert_eq!(Some(five), index); -/// ``` -#[derive(Debug, PartialEq)] -pub struct Index { - index: usize, - generation: usize, - _marker: PhantomData, -} - -impl Clone for Index { - fn clone(&self) -> Self { - Self { - index: self.index, - generation: self.generation, - _marker: PhantomData, - } - } -} -impl Copy for Index {} - -impl Index { - fn new(index: usize, generation: usize) -> Index { - Index { - index, - generation, - _marker: PhantomData, - } - } -} - -impl Default for IndexList { - fn default() -> Self { - IndexList { - contents: Default::default(), - generation: Default::default(), - next_free: Default::default(), - head: Default::default(), - tail: Default::default(), - } - } -} - -impl IndexList -where - T: PartialEq, - T: std::fmt::Debug, -{ - /// Creates a new `IndexList`. - /// - /// # Examples - /// - /// Making a new list: - /// - /// ``` - /// use bigtools::utils::indexlist::IndexList; - /// - /// let list: IndexList = IndexList::new(); - /// ``` - pub fn new() -> IndexList { - Self::default() - } - - /// Creates a new `IndexList` with a given capacity. - /// - /// If you know roughly how many elements will be stored in the list, - /// creating one with that capacity can reduce allocations, increasing - /// performance. - /// - /// # Examples - /// - /// Making a new list: - /// - /// ``` - /// use bigtools::utils::indexlist::IndexList; - /// - /// let list: IndexList = IndexList::with_capacity(100); - /// ``` - pub fn with_capacity(size: usize) -> IndexList { - IndexList { - contents: Vec::with_capacity(size), - generation: 0, - next_free: None, - head: None, - tail: None, - } - } - - /// Returns a reference to the first item in the list. - /// - /// Will return `None` if the list is empty. - /// - /// # Examples - /// - /// The first item is often the first one that's pushed on: - /// - /// ``` - /// use bigtools::utils::indexlist::IndexList; - /// - /// let mut list = IndexList::new(); - /// - /// list.push_back(5); - /// - /// assert_eq!(list.head(), Some(&5)); - /// ``` - /// - /// But of course, not always! - /// - /// ``` - /// use bigtools::utils::indexlist::IndexList; - /// - /// let mut list = IndexList::new(); - /// - /// list.push_back(5); - /// - /// // this will append to the front, so it's now the head - /// list.push_front(10); - /// - /// assert_eq!(list.head(), Some(&10)); - /// ``` - 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 tail(&self) -> Option<&T> { - let index = self.tail?; - - self.contents.get(index).and_then(|e| match e { - Entry::Free { .. } => None, - Entry::Occupied(e) => Some(&e.item), - }) - } - - /// Returns a mutable reference to the first item in the list. - /// - /// Will return `None` if the list is empty. - /// - /// # Examples - /// - /// The first item is often the first one that's pushed on: - /// - /// ``` - /// use bigtools::utils::indexlist::IndexList; - /// - /// let mut list = IndexList::new(); - /// - /// list.push_back(5); - /// - /// assert_eq!(list.head_mut(), Some(&mut 5)); - /// ``` - /// - /// But of course, not always! - /// - /// ``` - /// use bigtools::utils::indexlist::IndexList; - /// - /// let mut list = IndexList::new(); - /// - /// list.push_back(5); - /// - /// // this will append to the front, so it's now the head - /// list.push_front(10); - /// - /// assert_eq!(list.head_mut(), Some(&mut 10)); - /// ``` - pub fn head_mut(&mut self) -> Option<&mut T> { - let index = self.head?; - - match &mut self.contents[index] { - Entry::Free { .. } => None, - Entry::Occupied(e) => Some(&mut e.item), - } - } - - pub fn head_index(&self) -> Option> { - let index = self.head?; - - self.contents.get(index).and_then(|e| match e { - Entry::Free { .. } => None, - Entry::Occupied(e) => Some(Index::new(index, e.generation)), - }) - } - - pub fn tail_index(&self) -> Option> { - let index = self.tail?; - - self.contents.get(index).and_then(|e| match e { - Entry::Free { .. } => None, - Entry::Occupied(e) => Some(Index::new(index, e.generation)), - }) - } - - /// Adds this item to the tail of the list. - /// - /// # Examples - /// - /// Pushing several numbers into a list: - /// - /// ``` - /// use bigtools::utils::indexlist::IndexList; - /// - /// let mut list = IndexList::new(); - /// - /// list.push_back(5); - /// list.push_back(10); - /// list.push_back(15); - /// - /// // This prints 5, 10, and then 15, each on its own line - /// for element in list.iter() { - /// println!("{}", element); - /// } - /// ``` - pub fn push_back(&mut self, item: T) -> Index { - // first, we need to find a suitable place to insert - - // is the head of the list empty? If so, that's easy. - 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::new(index, generation); - } - - // if it isn't empty, then we need to check the free list and put our - // new item in the proper place - - // we have a tail, so we can unwrap; we need this for appending - let tail_index = self.tail.unwrap(); - - let position = if let Some(position) = self.next_free { - // update 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 { - // we don't have any, so append to the end of the list - let position = self.contents.len(); - - self.contents.push(Entry::Occupied(OccupiedEntry { - item, - generation: self.generation, - next: None, - prev: Some(tail_index), - })); - - position - }; - - // and then fix up the tail to refer to it - let new_index = Index::new(position, self.generation); - - // we found this index before so we know it exists - match &mut self.contents[tail_index] { - Entry::Free { .. } => panic!("Corrupted list"), - Entry::Occupied(e) => e.next = Some(new_index.index), - } - - // update our tail to properly point at the newly inserted element - self.tail = Some(position); - - // and finally, return the index associated with our new tail - new_index - } - - /// Adds this item to the head of the list. - /// - /// # Examples - /// - /// Pushing several numbers into a list: - /// - /// ``` - /// use bigtools::utils::indexlist::IndexList; - /// - /// let mut list = IndexList::new(); - /// - /// list.push_front(5); - /// list.push_front(10); - /// list.push_front(15); - /// - /// // This prints 15, 10, and then 5, each on its own line - /// for element in list.iter() { - /// println!("{}", element); - /// } - /// ``` - pub fn push_front(&mut self, item: T) -> Index { - // first, we need to find a suitable place to insert - - // is the head of the list empty? If so, that's easy. - if self.head.is_none() { - return self.push_back(item); - } - - // if it isn't empty, then we need to check the free list and put our - // new item in the proper place - - // we have a head, so we can unwrap; we need this for appending - let head_index = self.head.unwrap(); - - let position = if let Some(position) = self.next_free { - // update 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: Some(head_index), - prev: None, - }); - - position - } else { - // we don't have any, so append to the end of the list - 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::new(position, self.generation); - - // and then fix up the head to refer to it - - // we found this index before so we know it exists - match &mut self.contents[head_index] { - Entry::Free { .. } => panic!("Corrupted list"), - Entry::Occupied(e) => e.prev = Some(new_index.index), - } - - // update our head to properly point at the newly inserted element - self.head = Some(position); - - // and finally, return the index associated with our new tail - new_index - } - - /// Does this list contain this element? - /// - /// Returns true if it does, and false if it does not. - /// - /// # Examples - /// - /// Checking both possibilities: - /// - /// ``` - /// use bigtools::utils::indexlist::IndexList; - /// - /// let mut list = IndexList::new(); - /// - /// list.push_back(5); - /// - /// // our list does contain five - /// assert!(list.contains(&5)); - /// - /// // our list does not contain ten - /// assert!(!list.contains(&10)); - /// ``` - pub fn contains(&self, value: &T) -> bool { - self.iter().any(|e| e == value) - } - - /// Returns the item at this index if it exists. - /// - /// If there's an item at this index, then this will return a reference to - /// it. If not, returns `None`. - /// - /// Indexes are generational, and so this method will use the generation to - /// determine if this element exists. For more, see [`Index`'s documentation]. - /// - /// [`Index`'s documentation]: struct.Index.html - /// - /// # Examples - /// - /// Getting an element at an index: - /// - /// ``` - /// use bigtools::utils::indexlist::IndexList; - /// - /// let mut list = IndexList::new(); - /// - /// let five = list.push_back(5); - /// - /// assert_eq!(list.get(five), Some(&5)); - /// ``` - /// - /// An element that doesn't exist returns `None`: - /// - /// ``` - /// use bigtools::utils::indexlist::IndexList; - /// - /// let mut list = IndexList::new(); - /// - /// let five = list.push_back(5); - /// - /// list.remove(five); - /// - /// assert!(list.get(five).is_none()); - /// ``` - /// - /// Generational indexes ensure that we don't access incorrect items: - /// - /// ``` - /// use bigtools::utils::indexlist::IndexList; - /// - /// let mut list = IndexList::new(); - /// - /// let five = list.push_back(5); - /// list.push_back(10); - /// - /// list.remove(five); - /// - /// // since we have a free spot, this will go where 5 was - /// list.push_back(15); - /// - /// // our index is out of date, and so will not return 15 here - /// assert!(list.get(five).is_none()); - /// ``` - 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, - } - } - - /// Returns the item at this index if it exists. - /// - /// If there's an item at this index, then this will return a mutable reference to - /// it. If not, returns `None`. - /// - /// Indexes are generational, and so this method will use the generation to - /// determine if this element exists. For more, see [`Index`'s documentation]. - /// - /// [`Index`'s documentation]: struct.Index.html - /// - /// # Examples - /// - /// Getting an element at an index: - /// - /// ``` - /// use bigtools::utils::indexlist::IndexList; - /// - /// let mut list = IndexList::new(); - /// - /// let five = list.push_back(5); - /// - /// assert_eq!(list.get_mut(five), Some(&mut 5)); - /// ``` - /// - /// An element that doesn't exist returns `None`: - /// - /// ``` - /// use bigtools::utils::indexlist::IndexList; - /// - /// let mut list = IndexList::new(); - /// - /// let five = list.push_back(5); - /// - /// list.remove(five); - /// - /// assert!(list.get_mut(five).is_none()); - /// ``` - /// - /// Generational indexes ensure that we don't access incorrect items: - /// - /// ``` - /// use bigtools::utils::indexlist::IndexList; - /// - /// let mut list = IndexList::new(); - /// - /// let five = list.push_back(5); - /// list.push_back(10); - /// - /// list.remove(five); - /// - /// // since we have a free spot, this will go where 5 was - /// list.push_back(15); - /// - /// // our index is out of date, and so will not return 15 here - /// assert!(list.get_mut(five).is_none()); - /// ``` - pub fn get_mut(&mut self, index: Index) -> Option<&mut T> { - match &mut self.contents[index.index] { - Entry::Occupied(e) if e.generation == index.generation => Some(&mut e.item), - _ => None, - } - } - - pub fn next_index(&self, index: Index) -> Option> { - match self.contents.get(index.index)? { - Entry::Occupied(e) if e.generation == index.generation => { - match e.next { - Some(index) => match self.contents.get(index)? { - Entry::Occupied(e) => Some(Index::new(index, e.generation)), - _ => panic!("Corrupted list"), - }, - _ => None, // this element was at the end of the list - } - } - _ => None, // this was an invalid or outdated index - } - } - - pub fn prev_index(&self, index: Index) -> Option> { - match self.contents.get(index.index)? { - Entry::Occupied(e) if e.generation == index.generation => { - match e.prev { - Some(index) => match self.contents.get(index)? { - Entry::Occupied(e) => Some(Index::new(index, e.generation)), - _ => panic!("Corrupted list"), - }, - _ => None, // this element was at the end of the list - } - } - _ => None, // this was an invalid or outdated index - } - } - - /// Removes the item at this index, and returns the removed item. - /// - /// If there's an item at this index, then this will remove it from the list - /// and return it. If there isn't, it will return `None`. - /// - /// Indexes are generational, and so this method will use the generation to - /// determine if this element exists. For more, see [`Index`'s documentation]. - /// - /// [`Index`'s documentation]: struct.Index.html - /// - /// # Examples - /// - /// Removing an element from an index: - /// - /// ``` - /// use bigtools::utils::indexlist::IndexList; - /// - /// let mut list = IndexList::new(); - /// - /// let five = list.push_back(5); - /// - /// let five = list.remove(five); - /// assert_eq!(five, Some(5)); - /// - /// assert!(!list.contains(&5)); - /// ``` - /// - /// An element that doesn't exist returns `None`: - /// - /// ``` - /// use bigtools::utils::indexlist::IndexList; - /// - /// let mut list = IndexList::new(); - /// - /// let five = list.push_back(5); - /// - /// list.remove(five); - /// - /// assert!(list.remove(five).is_none()); - /// ``` - /// - /// Generational indexes ensure that we don't access incorrect items: - /// - /// ``` - /// use bigtools::utils::indexlist::IndexList; - /// - /// let mut list = IndexList::new(); - /// - /// let five = list.push_back(5); - /// list.push_back(10); - /// - /// list.remove(five); - /// - /// // since we have a free spot, this will go where 5 was - /// list.push_back(15); - /// - /// // our index is out of date, and so will not return 15 here - /// assert!(list.remove(five).is_none()); - /// ``` - pub fn remove(&mut self, index: Index) -> Option { - // if we have no head or tail, then we have an emtpy list, so return - let head_index = self.head?; - let tail_index = self.tail?; - - // we want to do just get, but then we run into borrowing issues. - // - // we could implement Entry, but... ugh. So let's fetch just the indexes for now. - let (prev_index, index, next_index) = match self.contents.get(index.index)? { - Entry::Free { .. } => return None, - Entry::Occupied(e) => { - // are we of the right generation? - 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, - }, - ); - - // update our free list to point to this new space - self.next_free = Some(index); - - // when we remove a node, we need to increase the generation to invalidate - // older indexes that may be refering to this spot - self.generation += 1; - - // now we need to fix up any next or previous nodes. we have four cases: - // - // * index is at the head and tail (only item in the list) - // * index is at the head - // * index is at the tail - // * index is in the middle - - // index is at the head and tail (only item in the list) - if (index == head_index) && (index == tail_index) { - self.head = None; - self.tail = None; - - // index is at the head - } 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; - - // index is at the tail - } 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; - - // index is in the middle - } else if index != head_index && index != tail_index { - // fix up next - { - let next = match &mut self.contents[next_index.unwrap()] { - Entry::Free { .. } => panic!("Corrupted list"), - Entry::Occupied(e) => e, - }; - - next.prev = prev_index; - } - - // fix up prev - { - 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), - } - } - - /// Inserts an element immediately before the provided index. Returns `None` - /// if the element at the provided index was removed. - pub fn insert_before(&mut self, index: Index, item: T) -> Option> { - // Get the current index - let (prev_index, index, _next_index) = match self.contents.get(index.index)? { - Entry::Free { .. } => return None, - Entry::Occupied(e) => { - // are we of the right generation? - if index.generation != e.generation { - return None; - } - - (e.prev, index.index, e.next) - } - }; - - let entry = Entry::Occupied(OccupiedEntry { - item, - generation: self.generation, - next: Some(index), - prev: prev_index, - }); - - // Insert the item - let position = if let Some(position) = self.next_free { - // update next_free - match self.contents[position] { - Entry::Occupied { .. } => panic!("Corrupted list"), - Entry::Free { next_free } => self.next_free = next_free, - } - - self.contents[position] = entry; - - position - } else { - // we don't have any, so append to the end of the list - let position = self.contents.len(); - - self.contents.push(entry); - - position - }; - - match &mut self.contents[index] { - Entry::Free { .. } => panic!("Corrupted list"), - Entry::Occupied(e) => { - e.prev = Some(position); - } - } - - // Now, we need to update the prev node, if there was one, as well as - // the head, if there wasn't - match prev_index { - Some(index) => match &mut self.contents[index] { - Entry::Occupied(e) => { - e.next = Some(position); - } - _ => panic!("Corrupted list"), - }, - None => { - self.head = Some(position); - } - } - - Some(Index::new(position, self.generation)) - } - - /// Inserts an element immediately after the provided index. Returns `None` - /// if the element at the provided index was removed. - pub fn insert_after(&mut self, index: Index, item: T) -> Option> { - // Get the current index - let (_prev_index, index, next_index) = match self.contents.get(index.index)? { - Entry::Free { .. } => return None, - Entry::Occupied(e) => { - // are we of the right generation? - if index.generation != e.generation { - return None; - } - - (e.prev, index.index, e.next) - } - }; - - let entry = Entry::Occupied(OccupiedEntry { - item, - generation: self.generation, - next: next_index, - prev: Some(index), - }); - - // Insert the item - let position = if let Some(position) = self.next_free { - // update next_free - match self.contents[position] { - Entry::Occupied { .. } => panic!("Corrupted list"), - Entry::Free { next_free } => self.next_free = next_free, - } - - self.contents[position] = entry; - - position - } else { - // we don't have any, so append to the end of the list - let position = self.contents.len(); - - self.contents.push(entry); - - position - }; - - match &mut self.contents[index] { - Entry::Free { .. } => panic!("Corrupted list"), - Entry::Occupied(e) => { - e.next = Some(position); - } - } - - // Now, we need to update the prev node, if there was one, as well as - // the head, if there wasn't - match next_index { - Some(index) => match &mut self.contents[index] { - Entry::Occupied(e) => { - e.prev = Some(position); - } - _ => panic!("Corrupted list"), - }, - None => { - self.tail = Some(position); - } - } - - Some(Index::new(position, self.generation)) - } - - /// Returns an iterator of references to the items in the list. - /// - /// # Examples - /// - /// Using an iterator to print out all of the items in a list: - /// - /// ``` - /// use bigtools::utils::indexlist::IndexList; - /// - /// let mut list = IndexList::new(); - /// - /// list.push_back(5); - /// list.push_back(10); - /// list.push_back(15); - /// - /// // This prints 5, 10, and then 15, each on its own line - /// for element in list.iter() { - /// println!("{}", element); - /// } - /// ``` - pub fn iter(&self) -> impl Iterator { - Iter { - list: self, - next_index: self.head, - } - } - - /// Returns an `Index` to this item. - /// - /// If this item is not in the list, returns `None`. - /// - /// # Examples - /// - /// Finding an item: - /// - /// ``` - /// use bigtools::utils::indexlist::IndexList; - /// - /// let mut list = IndexList::new(); - /// - /// let five = list.push_back(5); - /// - /// let index = list.index_of(&5); - /// - /// assert_eq!(Some(five), index); - /// ``` - pub fn index_of(&self, item: &T) -> Option> { - let mut next = self.head; - - // iterate through entries from the front of the list - while let Some(index) = next { - // this should always be occupied because the index comes from a previous list items `next` field - let entry = match &self.contents[index] { - Entry::Free { .. } => panic!("Corrupt list"), - Entry::Occupied(entry) => entry, - }; - // if we find the item, return the index, otherwise check the next list item - if &entry.item == item { - return Some(Index::new(index, entry.generation)); - } else { - next = entry.next; - } - } - - None - } - - /// Removes the head of the list. - /// - /// If an item was removed, this will also return it. - /// - /// If this list is empty, returns `None`. - /// - /// # Examples - /// - /// Removing the head: - /// - /// ``` - /// use bigtools::utils::indexlist::IndexList; - /// - /// let mut list = IndexList::new(); - /// - /// list.push_back(5); - /// - /// assert_eq!(list.pop_front(), Some(5)); - /// - /// assert_eq!(list.iter().count(), 0); - /// ``` - pub fn pop_front(&mut self) -> Option { - // if we have no head, then we have an empty list, so return - let head_index = self.head?; - - // we want to do just get, but then we run into borrowing issues. - // - // we could implement Entry, but... ugh. So let's fetch just the indexes for now. - 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, - }, - ); - - // update our free list to point to this new space - self.next_free = Some(head_index); - - // when we remove a node, we need to increase the generation to invalidate - // older indexes that may be refering to this spot - self.generation += 1; - - // now we need to fix up any next or previous nodes. we have two cases: - // - // * index is at the head and tail (only item in the list) - // * index is at the head - - // index is at the head and tail (only item in the list) - if Some(head_index) == self.tail { - self.head = None; - self.tail = None; - - // index is at the head - } 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), - } - } -} - -impl IntoIterator for IndexList { - type Item = T; - type IntoIter = IntoIter; - - fn into_iter(self) -> Self::IntoIter { - let next_index = self.head; - - IntoIter { - list: self, - next_index, - } - } -} - -pub struct IntoIter { - list: IndexList, - next_index: Option, -} - -impl Iterator for IntoIter { - type Item = T; - - fn next(&mut self) -> Option { - let next_index = self.next_index?; - let entry = std::mem::replace( - &mut self.list.contents[next_index], - Entry::Free { next_free: None }, - ); - - match entry { - Entry::Free { .. } => panic!("Corrupted list"), - Entry::Occupied(e) => { - self.next_index = e.next; - - Some(e.item) - } - } - } -} - -struct Iter<'a, T> -where - T: 'a, -{ - list: &'a IndexList, - next_index: Option, -} - -impl<'a, T> Iterator for Iter<'a, T> { - type Item = &'a T; - - fn next(&mut self) -> Option { - // do we have a next thing? - let next_index = self.next_index?; - - // what is it? - match &self.list.contents[next_index] { - Entry::Free { .. } => panic!("Corrupted list"), - Entry::Occupied(e) => { - // set up our next iteration - self.next_index = e.next; - - Some(&e.item) - } - } - } -} - -impl std::ops::Index> for IndexList -where - T: PartialEq, - T: std::fmt::Debug, -{ - type Output = T; - - fn index(&self, index: Index) -> &Self::Output { - self.get(index).unwrap() - } -} - -impl std::ops::IndexMut> for IndexList -where - T: PartialEq, - T: std::fmt::Debug, -{ - fn index_mut(&mut self, index: Index) -> &mut Self::Output { - self.get_mut(index).unwrap() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn create_index() { - let index: Index = Index::new(1, 2); - - assert_eq!(index.index, 1); - assert_eq!(index.generation, 2); - } - - #[test] - fn create_list() { - let _list: IndexList = 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 get_mut() { - let mut list = IndexList::new(); - - let five = list.push_back(5); - - let entry = list.get_mut(five); - - assert!(entry.is_some()); - - assert_eq!(entry.unwrap(), &mut 5); - } - - #[test] - fn next_index() { - let mut list = IndexList::new(); - - let five = list.push_back(5); - let _ten = list.push_back(10); - - let ten_index = list.next_index(five).unwrap(); - - let ten_value = list.get(ten_index); - - assert_eq!(ten_value.unwrap(), &10); - assert_eq!(None, list.next_index(ten_index)); - } - - #[test] - fn prev_index() { - let mut list = IndexList::new(); - - let _five = list.push_back(5); - let ten = list.push_back(10); - - let five_index = list.prev_index(ten).unwrap(); - - let five_value = list.get(five_index); - - assert_eq!(five_value.unwrap(), &5); - assert_eq!(None, list.prev_index(five_index)); - } - - #[test] - fn index() { - let mut list = IndexList::new(); - - let five = list.push_back(5); - - let entry = list[five]; - - assert_eq!(entry, 5); - } - - #[test] - fn index_mut() { - let mut list = IndexList::new(); - - let five = list.push_back(5); - - let mut entry = list[five]; - - entry += 1; - - let six = list.push_back(entry); - - let new_entry = list[six]; - - assert_eq!(new_entry, 6); - } - - #[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 into_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.into_iter(); - - assert_eq!(iter.next().unwrap(), 5); - assert_eq!(iter.next().unwrap(), 15); - - assert!(iter.next().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); - - // since we reallocate, that twenty should have gone where the ten was. - // this means that ten should now be invalid. - assert!(list.get(ten).is_none()); - - // however, five should be fine! - assert!(list.get(five).is_some()); - - // as should twenty! - 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 head_mut() { - let mut list = IndexList::new(); - - assert!(list.head_mut().is_none()); - - let five = list.push_back(5); - - assert_eq!(list.head_mut().unwrap(), &mut 5); - - list.push_back(10); - - list.remove(five); - - assert_eq!(list.head_mut().unwrap(), &mut 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 head_index() { - let mut list = IndexList::new(); - - assert!(list.head_index().is_none()); - - let five = list.push_back(5); - - assert_eq!(list.head_index().unwrap(), five); - } - - #[test] - fn tail_index() { - let mut list = IndexList::new(); - - assert!(list.tail_index().is_none()); - - let _five = list.push_back(5); - let ten = list.push_back(10); - - assert_eq!(list.tail_index().unwrap(), ten); - } - - #[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::new(1, 0)); - - assert!(list.index_of(&20).is_none()); - } - - #[test] - fn index_of_get_correct_generation() { - let mut list = IndexList::new(); - - list.push_back(5); - let ten = list.push_back(10); - list.remove(ten); - list.push_back(15); - - assert_eq!( - list.index_of(&5).unwrap(), - Index { - index: 0, - generation: 0, - _marker: PhantomData - } - ); - } - - #[test] - fn index_of_get_first_occurrence() { - let mut list = IndexList::new(); - - list.push_back(3); - let six = list.push_back(6); - let first_nine = list.push_back(9); - list.push_back(12); - - list.remove(six); - - let _second_nine = list.push_back(9); - - assert_eq!(list.index_of(&9).unwrap(), first_nine); - } - - #[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, - } - ); - } - - #[test] - fn push_front_next_free() { - let mut list = IndexList::new(); - - list.push_front(0); - list.push_front(73); - list.pop_front(); - - list.push_front(1); - list.push_front(2); - - assert_eq!( - list, - IndexList { - contents: vec![ - Entry::Occupied(OccupiedEntry { - item: 0, - next: None, - prev: Some(1), - generation: 0 - }), - Entry::Occupied(OccupiedEntry { - item: 1, - next: Some(0), - prev: Some(2), - generation: 1 - }), - Entry::Occupied(OccupiedEntry { - item: 2, - next: Some(1), - prev: None, - generation: 1 - }) - ], - generation: 1, - next_free: None, - head: Some(2), - tail: Some(0), - } - ); - } - - #[test] - fn insert_before() { - let mut list = IndexList::new(); - - let index = list.push_front(2); - list.insert_before(index, 0); - - assert_eq!(list.iter().copied().collect::>(), vec![0, 2]); - assert_eq!(*list.get(list.prev_index(index).unwrap()).unwrap(), 0); - - list.insert_before(index, 1); - - assert_eq!(list.iter().copied().collect::>(), vec![0, 1, 2]); - assert_eq!(*list.get(list.prev_index(index).unwrap()).unwrap(), 1); - } - - #[test] - fn insert_after() { - let mut list = IndexList::new(); - - let index = list.push_front(0); - list.insert_after(index, 2); - - assert_eq!(list.iter().copied().collect::>(), vec![0, 2]); - assert_eq!(*list.get(list.next_index(index).unwrap()).unwrap(), 2); - - list.insert_after(index, 1); - - assert_eq!(list.iter().copied().collect::>(), vec![0, 1, 2]); - assert_eq!(*list.get(list.next_index(index).unwrap()).unwrap(), 1); - } -} diff --git a/bigtools/src/utils/mod.rs b/bigtools/src/utils/mod.rs index 012aeb9..feeb058 100644 --- a/bigtools/src/utils/mod.rs +++ b/bigtools/src/utils/mod.rs @@ -1,7 +1,6 @@ pub mod file; pub mod fill; pub mod idmap; -pub mod indexlist; pub mod merge; #[cfg(feature = "read")] pub mod misc;