// Copyright 2016 Amanieu d'Antras // Copyright 2020 Amari Robinson // // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be // copied, modified, or distributed except according to those terms. //! Intrusive collections for Rust. //! //! This library provides a set of high-performance intrusive collections which //! can offer better performance and more flexibility than standard collections. //! //! The main difference between an intrusive collection and a normal one is that //! while normal collections allocate memory behind your back to keep track of a //! set of *values*, intrusive collections never allocate memory themselves and //! instead keep track of a set of *objects*. Such collections are called //! intrusive because they requires explicit support in objects to allow them to //! be inserted into a collection. //! //! # Example //! //! ``` //! use intrusive_collections::intrusive_adapter; //! use intrusive_collections::{LinkedList, LinkedListLink}; //! use std::cell::Cell; //! //! // A simple struct containing an instrusive link and a value //! struct Test { //! link: LinkedListLink, //! value: Cell, //! } //! //! // The adapter describes how an object can be inserted into an intrusive //! // collection. This is automatically generated using a macro. //! intrusive_adapter!(TestAdapter = Box: Test { link: LinkedListLink }); //! //! // Create a list and some objects //! let mut list = LinkedList::new(TestAdapter::new()); //! let a = Box::new(Test { //! link: LinkedListLink::new(), //! value: Cell::new(1), //! }); //! let b = Box::new(Test { //! link: LinkedListLink::new(), //! value: Cell::new(2), //! }); //! let c = Box::new(Test { //! link: LinkedListLink::new(), //! value: Cell::new(3), //! }); //! //! // Insert the objects at the front of the list //! list.push_front(a); //! list.push_front(b); //! list.push_front(c); //! assert_eq!(list.iter().map(|x| x.value.get()).collect::>(), [3, 2, 1]); //! //! // At this point, the objects are owned by the list, and we can modify //! // them through the list. //! list.front().get().unwrap().value.set(4); //! assert_eq!(list.iter().map(|x| x.value.get()).collect::>(), [4, 2, 1]); //! //! // Removing an object from an instrusive collection gives us back the //! // Box that we originally inserted into it. //! let a = list.pop_front().unwrap(); //! assert_eq!(a.value.get(), 4); //! assert_eq!(list.iter().map(|x| x.value.get()).collect::>(), [2, 1]); //! //! // Dropping the collection will automatically free b and c by //! // transforming them back into Box and dropping them. //! drop(list); //! ``` //! //! # Links and adapters //! //! Intrusive collections track objects through links which are embedded within //! the objects themselves. It also allows a single object to be part of //! multiple intrusive collections at once by having multiple links in it. //! //! The relationship between an object and a link inside it is described by the //! `Adapter` trait. Intrusive collections use an implementation of this trait //! to determine which link in an object should be used by the collection. In //! most cases you do not need to write an implementation manually: the //! `intrusive_adapter!` macro will automatically generate the necessary code. //! //! For red-black trees, the adapter must also implement the `KeyAdapter` trait //! which allows a key to be extracted from an object. This key is then used to //! keep all elements in the tree in ascending order. //! //! ``` //! use intrusive_collections::intrusive_adapter; //! use intrusive_collections::{SinglyLinkedListLink, SinglyLinkedList}; //! use intrusive_collections::{LinkedListLink, LinkedList}; //! use intrusive_collections::{XorLinkedList, XorLinkedListLink}; //! use intrusive_collections::{RBTreeLink, RBTree, KeyAdapter}; //! use std::rc::Rc; //! //! // This struct can be inside three lists and one tree simultaneously //! #[derive(Default)] //! struct Test { //! link: LinkedListLink, //! link2: SinglyLinkedListLink, //! link3: XorLinkedListLink, //! link4: RBTreeLink, //! value: i32, //! } //! //! intrusive_adapter!(MyAdapter = Rc: Test { link: LinkedListLink }); //! intrusive_adapter!(MyAdapter2 = Rc: Test { link2: SinglyLinkedListLink }); //! intrusive_adapter!(MyAdapter3 = Rc: Test { link3: XorLinkedListLink }); //! intrusive_adapter!(MyAdapter4 = Rc: Test { link4: RBTreeLink }); //! impl<'a> KeyAdapter<'a> for MyAdapter4 { //! type Key = i32; //! fn get_key(&self, x: &'a Test) -> i32 { x.value } //! } //! //! let mut a = LinkedList::new(MyAdapter::new()); //! let mut b = SinglyLinkedList::new(MyAdapter2::new()); //! let mut c = XorLinkedList::new(MyAdapter3::new()); //! let mut d = RBTree::new(MyAdapter4::new()); //! //! let test = Rc::new(Test::default()); //! a.push_front(test.clone()); //! b.push_front(test.clone()); //! c.push_front(test.clone()); //! d.insert(test); //! ``` //! //! # Cursors //! //! Intrusive collections are manipulated using cursors. A cursor is similar to //! an iterator, except that it can freely seek back-and-forth, and can safely //! mutate the list during iteration. This is similar to how a C++ iterator //! works. //! //! A cursor views an intrusive collection as a circular list, with a special //! null object between the last and first elements of the collection. A cursor //! will either point to a valid object in the collection or to this special //! null object. //! //! Cursors come in two forms: `Cursor` and `CursorMut`. A `Cursor` gives a //! read-only view of a collection, but you are allowed to use multiple `Cursor` //! objects simultaneously on the same collection. On the other hand, //! `CursorMut` can be used to mutate the collection, but you may only use one //! of them at a time. //! //! Cursors are a very powerful abstraction since they allow a collection to be //! mutated safely while it is being iterated on. For example, here is a //! function which removes all values within a given range from a `RBTree`: //! //! ``` //! use intrusive_collections::intrusive_adapter; //! use intrusive_collections::{RBTreeLink, RBTree, KeyAdapter, Bound}; //! //! struct Element { //! link: RBTreeLink, //! value: i32, //! } //! //! intrusive_adapter!(ElementAdapter = Box: Element { link: RBTreeLink }); //! impl<'a> KeyAdapter<'a> for ElementAdapter { //! type Key = i32; //! fn get_key(&self, e: &'a Element) -> i32 { e.value } //! } //! //! fn remove_range(tree: &mut RBTree, min: i32, max: i32) { //! // Find the first element which is greater than or equal to min //! let mut cursor = tree.lower_bound_mut(Bound::Included(&min)); //! //! // Iterate over all elements in the range [min, max] //! while cursor.get().map_or(false, |e| e.value <= max) { //! // CursorMut::remove will return a Some(), which we //! // simply drop here. This will also advance the cursor to the next //! // element. //! cursor.remove(); //! } //! } //! ``` //! //! # Scoped collections //! //! Instead of taking ownership of objects inserted into them, intrusive //! collections can also work with borrowed values. This works by using //! lifetimes and the borrow checker to ensure that any objects inserted into an //! intrusive collection will outlive the collection itself. //! //! ``` //! use intrusive_collections::intrusive_adapter; //! use intrusive_collections::{LinkedListLink, LinkedList}; //! use typed_arena::Arena; //! use std::cell::Cell; //! //! struct Value { //! link: LinkedListLink, //! value: Cell, //! } //! //! // Note that we use a plain reference as the pointer type for the collection. //! intrusive_adapter!(ValueAdapter<'a> = &'a Value: Value { link: LinkedListLink }); //! //! // Create an arena and a list. Note that since stack objects are dropped in //! // reverse order, the Arena must be created before the LinkedList. This //! // ensures that the list is dropped before the values are freed by the //! // arena. This is enforced by the Rust lifetime system. //! let arena = Arena::new(); //! let mut list = LinkedList::new(ValueAdapter::new()); //! //! // We can now insert values allocated from the arena into the linked list //! list.push_back(arena.alloc(Value { //! link: LinkedListLink::new(), //! value: Cell::new(1), //! })); //! list.push_back(arena.alloc(Value { //! link: LinkedListLink::new(), //! value: Cell::new(2), //! })); //! list.push_back(arena.alloc(Value { //! link: LinkedListLink::new(), //! value: Cell::new(3), //! })); //! assert_eq!(list.iter().map(|x| x.value.get()).collect::>(), [1, 2, 3]); //! //! // We can also insert stack allocated values into an intrusive list. //! // Again, the values must outlive the LinkedList. //! let a = Value { //! link: LinkedListLink::new(), //! value: Cell::new(4), //! }; //! let b = Value { //! link: LinkedListLink::new(), //! value: Cell::new(5), //! }; //! let c = Value { //! link: LinkedListLink::new(), //! value: Cell::new(6), //! }; //! let mut list2 = LinkedList::new(ValueAdapter::new()); //! list2.push_back(&a); //! list2.push_back(&b); //! list2.push_back(&c); //! assert_eq!(list2.iter().map(|x| x.value.get()).collect::>(), [4, 5, 6]); //! //! // Since these are shared references, any changes in the values are reflected in //! // the list. //! a.value.set(7); //! assert_eq!(list2.iter().map(|x| x.value.get()).collect::>(), [7, 5, 6]); //! ``` //! //! # Safety //! //! While it is possible to use intrusive collections without any unsafe code, //! this crate also exposes a few unsafe features. //! //! The `cursor_from_ptr` and `cursor_mut_from_ptr` allow you to create a //! cursor pointing to a specific element in the collection from a pointer to //! that element. This is unsafe because it assumes that the objected pointed to //! is currently inserted in the collection. //! //! The `UnsafeRef` type acts like `Rc`, except without the reference count. //! Instead, you are responsible for keeping track of the number of active //! references to an object and for freeing it once the last reference is //! dropped. The advantage of `UnsafeRef` over `Rc` is that it reduces the size //! of the allocation by two `usize` and avoids the overhead of maintaining //! reference counts. #![warn(missing_docs)] #![warn(rust_2018_idioms)] #![no_std] #![cfg_attr(feature = "nightly", feature(const_fn))] #![allow(clippy::declare_interior_mutable_const, clippy::collapsible_if)] #[cfg(feature = "alloc")] extern crate alloc; #[cfg(test)] extern crate std; mod unsafe_ref; #[macro_use] mod adapter; mod key_adapter; mod link_ops; mod pointer_ops; mod unchecked_option; pub mod linked_list; pub mod rbtree; pub mod singly_linked_list; pub mod xor_linked_list; pub use crate::adapter::Adapter; pub use crate::key_adapter::KeyAdapter; pub use crate::link_ops::{DefaultLinkOps, LinkOps}; pub use crate::linked_list::Link as LinkedListLink; pub use crate::linked_list::LinkedList; pub use crate::pointer_ops::{DefaultPointerOps, PointerOps}; pub use crate::rbtree::Link as RBTreeLink; pub use crate::rbtree::RBTree; pub use crate::singly_linked_list::Link as SinglyLinkedListLink; pub use crate::singly_linked_list::SinglyLinkedList; pub use crate::unsafe_ref::UnsafeRef; pub use crate::xor_linked_list::Link as XorLinkedListLink; pub use crate::xor_linked_list::XorLinkedList; pub use memoffset::offset_of; /// An endpoint of a range of keys. #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] pub enum Bound { /// An inclusive bound. Included(T), /// An exclusive bound. Excluded(T), /// An infinite endpoint. Indicates that there is no bound in this direction. Unbounded, }