1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
//! # orx-linked-list
//!
//! An efficient doubly linked list using regular `&` references with a focus to avoid smart pointers and improve cache locality.
//!
//! ## A. Motivation
//!
//! Self referential, often recursive, collections contain an important set of useful data structures including linked lists. However, building these structures with references `&` is not possible in safe rust.
//!
//! Alternatively, these collections can be built using reference counted smart pointers such as `std::rc::Rc` and independent heap allocations. However, independent heap allocations is a drawback as the elements do not live close to each other leading to poor cache locality. Further, reference counted pointers have a runtime overhead. This crate makes use of [`orx_imp_vec::ImpVec`](https://crates.io/crates/orx-imp-vec) as the underlying storage which specializes in building such collections.
//!
//! ### Standard LinkedList
//!
//! `std::collections::LinkedList` implementation avoids reference counted pointers and uses `NonNull` instead, most likely to avoid this overhead. However, this leads to a risky and difficult implementation that feels more low level than it should. You may see the implementation [here](https://doc.rust-lang.org/src/alloc/collections/linked_list.rs.html). The `unsafe` keyword is used more than 60 times in this file. These are usually related to reading from and writing to memory through raw pointers.
//!
//! ***Motivation:*** We do not need to count references provided that all elements and inter-element references belong to the same owner or container. This is because all elements will be dropped at the same time together with their inter-element references when the container `ImpVec` is dropped.
//!
//! ***Motivation:*** We should be able to define these structures without directly accessing memory through raw pointers. This is unnecessarily powerful and risky. Instead, unsafe code must be limited to methods which are specialized for and only allow defining required connections of self referential collections.
//!
//! ### orx_linked_list::LinkedList
//!
//! Linked list implementation in this crate uses an `ImpVec` as the underlying storage and makes use of its specialized methods. This brings the following advantages:
//!
//! * Allows for a higher level implementation without any use of raw pointers.
//! * Avoids smart pointers.
//! * Avoids almost completely accessing through integer indices.
//! * All nodes belong to the same `ImpVec` living close to each other. This allows for better cache locality.
//! * Full fetched doubly-linked-list implementation uses the `unsafe` keyword seven times, which are repeated uses of three methods:
//! * `ImpVec::push_get_ref`
//! * `ImpVec::move_get_ref`
//! * `ImpVec::unsafe_truncate` (*a deref method from [`PinnedVec`](https://crates.io/crates/orx-pinned-vec)*)
//!
//! Furthermore, this implementation is more performant than the standard library implementation, a likely indicator of better cache locality. You may below the benchmark results for a series of random push/pop mutations after pushing "number of elements" elements to the list.
//!
//! <img src="https://raw.githubusercontent.com/orxfun/orx-linked-list/main/docs/img/bench_mutation_ends.PNG" alt="https://raw.githubusercontent.com/orxfun/orx-linked-list/main/docs/img/bench_mutation_ends.PNG" />
//!
//! ## B. Features
//!
//! `orx_linked_list::LinkedList` implementation provides standard linked list opeartions such as constant time insertions and removals. Further, it reflects the **recursive** nature of the data structure through so called `LinkedListSlice`s. The caller can move to the desired element of the linked list and get the rest of the list as a linked list slice; which is nothing but an immutable linked list. Furthermore, slices can simply be `collect`ed as an owned linked list.
//!
//! ```rust
//! use orx_linked_list::*;
//!
//! // BASIC
//! let mut list = LinkedList::new();
//! list.extend(vec!['a', 'b', 'c']);
//!
//! assert_eq!(list.len(), 3);
//! assert!(list.contains(&'b'));
//! assert_eq!(list.index_of(&'c'), Some(2));
//! assert_eq!(list.from_back_index_of(&'c'), Some(0));
//!
//! list.push_back('d');
//! assert_eq!(Some(&'d'), list.back());
//!
//! *list.get_at_mut(0).unwrap() = 'x';
//!
//! list.push_front('e');
//! *list.front_mut().unwrap() = 'f';
//!
//! _ = list.remove_at(1);
//! _ = list.pop_back();
//! list.insert_at(0, 'x');
//! list.clear();
//! list.push_front('y');
//! list.pop_front();
//!
//! // ITER
//! let list: LinkedList<_> = ['a', 'b', 'c', 'd', 'e'].into_iter().collect();
//!
//! let forward: Vec<_> = list.iter().copied().collect();
//! assert_eq!(forward, &['a', 'b', 'c', 'd', 'e']);
//!
//! let backward: Vec<_> = list.iter_from_back().copied().collect();
//! assert_eq!(backward, &['e', 'd', 'c', 'b', 'a']);
//!
//! // SPLITS
//! let (left, right) = list.split(2).unwrap();
//! assert_eq!(left, &['a', 'b']);
//! assert_eq!(right, &['c', 'd', 'e']);
//! // left & right are also nothing but immutable linked lists
//! assert_eq!(right.front(), Some(&'c'));
//! assert_eq!(left.back(), Some(&'b'));
//!
//! let (front, after) = list.split_front().unwrap();
//! assert_eq!(front, &'a');
//! assert_eq!(after, &['b', 'c', 'd', 'e']);
//!
//! let (before, back) = list.split_back().unwrap();
//! assert_eq!(before, &['a', 'b', 'c', 'd']);
//! assert_eq!(back, &'e');
//!
//! let (left, right) = list.split_before(&'d').unwrap();
//! assert_eq!(left, &['a', 'b', 'c']);
//! assert_eq!(right, &['d', 'e']);
//!
//! let (left, right) = list.split_after(&'d').unwrap();
//! assert_eq!(left, &['a', 'b', 'c', 'd']);
//! assert_eq!(right, &['e']);
//!
//! // RECURSIVE SPLITS
//! let (left1, left2) = left.split(1).unwrap();
//! assert_eq!(left1, &['a']);
//! assert_eq!(left2, &['b', 'c', 'd']);
//!
//! // SPLIT TO OWNED
//! let mut left_list = left.collect();
//!
//! assert_eq!(left_list, &['a', 'b', 'c', 'd']);
//! _ = left_list.pop_front();
//! _ = left_list.pop_back();
//! assert_eq!(left_list, &['b', 'c']);
//! ```
//!
//! ## License
//!
//! This library is licensed under MIT license. See LICENSE for details.
#![warn(
missing_docs,
clippy::unwrap_in_result,
clippy::unwrap_used,
clippy::panic,
clippy::panic_in_result_fn,
clippy::float_cmp,
clippy::float_cmp_const,
clippy::missing_panics_doc,
clippy::todo
)]
mod eq;
mod extend;
mod from_iter;
mod iter;
mod linked_list;
mod linked_list_slice;
mod linked_list_view;
mod mem;
mod node;
pub use linked_list::LinkedList;
pub use linked_list_view::LinkedListView;
pub use mem::{MemoryStatus, MemoryUtilization};