From efb5166f4322092f143f8cecc904fed8a253469e Mon Sep 17 00:00:00 2001 From: jeff washington Date: Fri, 7 Jul 2023 18:57:46 -0500 Subject: [PATCH] pull IndexList into crate, get_mut -> get --- accounts-db/src/index_list.rs | 388 ++++++++++++++++++++ accounts-db/src/lib.rs | 1 + accounts-db/src/read_only_accounts_cache.rs | 5 +- 3 files changed, 391 insertions(+), 3 deletions(-) create mode 100644 accounts-db/src/index_list.rs diff --git a/accounts-db/src/index_list.rs b/accounts-db/src/index_list.rs new file mode 100644 index 00000000000000..8cd1944a27709d --- /dev/null +++ b/accounts-db/src/index_list.rs @@ -0,0 +1,388 @@ +//! A doubly linked list, backed by a vector. + +use std::{ + convert::TryFrom, + fmt::{Debug, Formatter}, + mem, + num::NonZeroU32, +}; + +/// Vector index for the elements in the list. They are typically not +/// squential. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub(crate) struct Index(Option); + +impl Index { + #[inline] + pub(crate) fn new() -> Index { + Index(None) + } + #[inline] + /// Returns `true` for a valid index. + /// + /// A valid index can be used in IndexList method calls. + pub(crate) fn is_some(&self) -> bool { + self.0.is_some() + } + #[inline] + /// Returns `true` for an invalid index. + /// + /// An invalid index will always be ignored and have `None` returned from + /// any IndexList method call that returns something. + pub(crate) fn is_none(&self) -> bool { + self.0.is_none() + } + #[inline] + fn get(&self) -> Option { + Some(self.0?.get() as usize - 1) + } + #[inline] + fn set(mut self, index: Option) -> Self { + if let Some(n) = index { + if let Ok(num) = NonZeroU32::try_from(n as u32 + 1) { + self.0 = Some(num); + } else { + self.0 = None; + } + } else { + self.0 = None; + } + self + } +} + +impl From for Index { + fn from(index: usize) -> Index { + Index::new().set(Some(index)) + } +} + +#[derive(Clone, Default)] +struct IndexNode { + next: Index, + prev: Index, +} + +impl IndexNode { + #[inline] + fn new() -> IndexNode { + IndexNode { + next: Index::new(), + prev: Index::new(), + } + } + #[inline] + fn new_next(&mut self, next: Index) -> Index { + mem::replace(&mut self.next, next) + } + #[inline] + fn new_prev(&mut self, prev: Index) -> Index { + mem::replace(&mut self.prev, prev) + } +} + +#[derive(Clone, Default)] +struct IndexEnds { + head: Index, + tail: Index, +} + +impl IndexEnds { + #[inline] + fn new() -> Self { + IndexEnds { + head: Index::new(), + tail: Index::new(), + } + } + #[inline] + fn clear(&mut self) { + self.new_both(Index::new()); + } + #[inline] + fn is_empty(&self) -> bool { + self.head.is_none() + } + #[inline] + fn new_head(&mut self, head: Index) -> Index { + mem::replace(&mut self.head, head) + } + #[inline] + fn new_tail(&mut self, tail: Index) -> Index { + mem::replace(&mut self.tail, tail) + } + #[inline] + fn new_both(&mut self, both: Index) { + self.head = both; + self.tail = both; + } +} + +/// Doubly-linked list implemented in safe Rust. +pub(crate) struct IndexList { + elems: Vec>, + nodes: Vec, + used: IndexEnds, + free: IndexEnds, + size: usize, +} + +impl Default for IndexList { + fn default() -> Self { + Self::new() + } +} + +impl Debug for IndexList { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + writeln!(f, "IndexList")?; + Ok(()) + } +} + +impl IndexList { + /// Creates a new empty index list. + /// + /// Example: + /// ```rust + /// use index_list::IndexList; + /// + /// let list = IndexList::::new(); + /// ``` + pub(crate) fn new() -> Self { + IndexList { + elems: Vec::new(), + nodes: Vec::new(), + used: IndexEnds::new(), + free: IndexEnds::new(), + size: 0, + } + } + /// Clears the list be removing all elements, making it empty. + /// + /// Example: + /// ```rust + /// # use index_list::IndexList; + /// # let mut list = IndexList::::new(); + /// list.clear(); + /// assert!(list.is_empty()); + /// ``` + #[inline] + pub(crate) fn clear(&mut self) { + self.elems.clear(); + self.nodes.clear(); + self.used.clear(); + self.free.clear(); + self.size = 0; + } + /// Returns `true` if the index is valid. + #[inline] + pub(crate) fn is_index_used(&self, index: Index) -> bool { + self.get(index).is_some() + } + /// Returns the index of the first element, or `None` if the list is empty. + /// + /// Example: + /// ```rust + /// # use index_list::IndexList; + /// # let list = IndexList::::new(); + /// let index = list.first_index(); + /// ``` + #[inline] + pub(crate) fn first_index(&self) -> Index { + self.used.head + } + /// Get a reference to the first element data, or `None`. + /// + /// Example: + /// ```rust + /// # use index_list::IndexList; + /// # let list = IndexList::::new(); + /// let data = list.get_first(); + /// ``` + #[inline] + pub(crate) fn get_first(&self) -> Option<&T> { + self.get(self.first_index()) + } + /// Get an immutable reference to the element data at the index, or `None`. + /// + /// Example: + /// ```rust + /// # use index_list::IndexList; + /// # let list = IndexList::::new(); + /// # let index = list.first_index(); + /// let data = list.get(index); + /// ``` + #[inline] + pub(crate) fn get(&self, index: Index) -> Option<&T> { + let ndx = index.get().unwrap_or(usize::MAX); + self.elems.get(ndx)?.as_ref() + } + /// Insert a new element at the end. + /// + /// It is typically not necessary to store the index, as the data will be + /// there when walking the list. + /// + /// Example: + /// ```rust + /// # use index_list::IndexList; + /// # let mut list = IndexList::::new(); + /// let index = list.insert_last(42); + /// ``` + pub(crate) fn insert_last(&mut self, elem: T) -> Index { + let this = self.new_node(Some(elem)); + self.linkin_last(this); + this + } + /// Remove the element at the index and return its data. + /// + /// Example: + /// ```rust + /// # use index_list::IndexList; + /// # let mut list = IndexList::from(&mut vec!["A", "B", "C"]); + /// # let mut index = list.first_index(); + /// # index = list.next_index(index); + /// let data = list.remove(index); + /// # assert_eq!(data, Some("B")); + /// ``` + pub(crate) fn remove(&mut self, index: Index) -> Option { + let elem_opt = self.remove_elem_at_index(index); + if elem_opt.is_some() { + self.linkout_used(index); + self.linkin_free(index); + } + elem_opt + } + + /// Move the element at the index to the end. + /// The index remains the same. + pub(crate) fn move_to_last(&mut self, index: Index) { + if self.is_index_used(index) { + // unlink where it is + self.linkout_used(index); + // insert it as last + self.linkin_last(index); + } + } + + #[inline] + fn get_mut_indexnode(&mut self, at: usize) -> &mut IndexNode { + &mut self.nodes[at] + } + #[inline] + fn set_prev(&mut self, index: Index, new_prev: Index) -> Index { + if let Some(at) = index.get() { + self.get_mut_indexnode(at).new_prev(new_prev) + } else { + index + } + } + #[inline] + fn set_next(&mut self, index: Index, new_next: Index) -> Index { + if let Some(at) = index.get() { + self.get_mut_indexnode(at).new_next(new_next) + } else { + index + } + } + #[inline] + fn insert_elem_at_index(&mut self, this: Index, elem: Option) { + if let Some(at) = this.get() { + self.elems[at] = elem; + self.size += 1; + } + } + #[inline] + fn remove_elem_at_index(&mut self, this: Index) -> Option { + this.get().and_then(|at| { + self.size -= 1; + self.elems[at].take() + }) + } + fn new_node(&mut self, elem: Option) -> Index { + let reuse = self.free.head; + if reuse.is_some() { + self.insert_elem_at_index(reuse, elem); + self.linkout_free(reuse); + return reuse; + } + let pos = self.nodes.len(); + self.nodes.push(IndexNode::new()); + self.elems.push(elem); + self.size += 1; + Index::from(pos) + } + fn linkin_free(&mut self, this: Index) { + debug_assert!(!self.is_index_used(this)); + let prev = self.free.tail; + self.set_next(prev, this); + self.set_prev(this, prev); + if self.free.is_empty() { + self.free.new_both(this); + } else { + let old_tail = self.free.new_tail(this); + debug_assert_eq!(old_tail, prev); + } + } + fn linkin_last(&mut self, this: Index) { + debug_assert!(self.is_index_used(this)); + let prev = self.used.tail; + self.set_next(prev, this); + self.set_prev(this, prev); + if self.used.is_empty() { + self.used.new_both(this); + } else { + let old_tail = self.used.new_tail(this); + debug_assert_eq!(old_tail, prev); + } + } + // prev >< this >< next => prev >< next + fn linkout_node(&mut self, this: Index) -> (Index, Index) { + let next = self.set_next(this, Index::new()); + let prev = self.set_prev(this, Index::new()); + let old_prev = self.set_prev(next, prev); + if old_prev.is_some() { + debug_assert_eq!(old_prev, this); + } + let old_next = self.set_next(prev, next); + if old_next.is_some() { + debug_assert_eq!(old_next, this); + } + (prev, next) + } + fn linkout_used(&mut self, this: Index) { + let (prev, next) = self.linkout_node(this); + if next.is_none() { + let old_tail = self.used.new_tail(prev); + debug_assert_eq!(old_tail, this); + } + if prev.is_none() { + let old_head = self.used.new_head(next); + debug_assert_eq!(old_head, this); + } + } + fn linkout_free(&mut self, this: Index) { + let (prev, next) = self.linkout_node(this); + if next.is_none() { + let old_tail = self.free.new_tail(prev); + debug_assert_eq!(old_tail, this); + } + if prev.is_none() { + let old_head = self.free.new_head(next); + debug_assert_eq!(old_head, this); + } + } +} + +#[cfg(test)] +mod tests { + use {super::*, std::mem::size_of}; + + #[test] + fn test_struct_sizes() { + assert_eq!(size_of::(), 4); + assert_eq!(size_of::(), 8); + assert_eq!(size_of::(), 8); + assert_eq!(size_of::>(), 72); + } +} diff --git a/accounts-db/src/lib.rs b/accounts-db/src/lib.rs index 8f62ea8ffa9257..f00181a949799c 100644 --- a/accounts-db/src/lib.rs +++ b/accounts-db/src/lib.rs @@ -30,6 +30,7 @@ pub mod contains; pub mod epoch_accounts_hash; pub mod hardened_unpack; pub mod in_mem_accounts_index; +mod index_list; pub mod inline_spl_token; pub mod inline_spl_token_2022; pub mod nonce_info; diff --git a/accounts-db/src/read_only_accounts_cache.rs b/accounts-db/src/read_only_accounts_cache.rs index bd1720b760984d..6a95b7f487561e 100644 --- a/accounts-db/src/read_only_accounts_cache.rs +++ b/accounts-db/src/read_only_accounts_cache.rs @@ -1,8 +1,8 @@ //! ReadOnlyAccountsCache used to store accounts, such as executable accounts, //! which can be large, loaded many times, and rarely change. use { + crate::index_list::{Index, IndexList}, dashmap::{mapref::entry::Entry, DashMap}, - index_list::{Index, IndexList}, solana_measure::measure_us, solana_sdk::{ account::{AccountSharedData, ReadableAccount}, @@ -86,8 +86,7 @@ impl ReadOnlyAccountsCache { // so that another thread cannot write to the same key. { let mut queue = self.queue.lock().unwrap(); - queue.remove(entry.index()); - entry.set_index(queue.insert_last(key)); + queue.move_to_last(entry.index()); } let account = entry.account.clone(); drop(entry);