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 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
use alloc::collections::VecDeque;
use core::marker::PhantomData;
use crate::cursor::{Cursor, CursorMut};
use crate::entry::{ItemEntry, XEntry};
use crate::mark::{NoneMark, XMark};
use crate::node::{Height, XNode};
use crate::range::Range;
pub(super) const BITS_PER_LAYER: usize = 6;
pub(super) const SLOT_SIZE: usize = 1 << BITS_PER_LAYER;
pub(super) const SLOT_MASK: usize = SLOT_SIZE - 1;
pub(super) const MAX_HEIGHT: usize = 64 / BITS_PER_LAYER + 1;
/// `XArray` is an abstract data type functioning like an expansive array of items where each item
/// must be an 8-byte object, such as `Arc<T>` or `Box<T>`.
///
/// User-stored pointers must have a minimum alignment of 4 bytes. `XArray` facilitates efficient
/// sequential access to adjacent entries, supporting multiple concurrent reads and exclusively
/// allowing one write operation at a time.
///
/// # Features
/// **Copy-on-write (COW):** If items within `XArray` implement the `Clone` trait, cloning can
/// leverage a COW mechanism. A clone of an `XArray` initially shares the head node with the
/// original, avoiding immediate deep copying. If a mutable operation is required on either
/// `XArray`, a deep copy of the relevant nodes is made first, ensuring isolated operations.
///
/// **Cursors:** Interaction with `XArray` is mediated through [`Cursor`] and [`CursorMut`]. A
/// `Cursor` requires an immutable reference, while `CursorMut` requires a mutable reference. As
/// such, multiple `Cursor` instances can coexist, but `CursorMut` operations are singular,
/// reflecting the behavior of shared (`&`) and exclusive (`&mut`) references. Cursors offer
/// precise index positioning and traversal capabilities in the `XArray`.
///
/// **Marking:** `XArray` enables marking of individual items or the `XArray` itself for user
/// convenience. Items and the `XArray` can have up to three distinct marks by default, with each
/// mark independently maintained. Users can use self-defined types as marks by implementing the
/// `From<Type>` trait for [`XMark`]. Marking is also applicable to internal nodes, indicating
/// marked descendant nodes, though such marking is not transparent to users.
///
/// # Example
///
/// ```
/// use std::sync::Arc;
/// use std::sync::{Mutex, MutexGuard};
/// use xarray::*;
///
/// let mut xarray_arc: XArray<Arc<i32>> = XArray::new();
/// let value = Arc::new(10);
/// xarray_arc.store(10, value);
/// assert!(*xarray_arc.load(10).unwrap().as_ref() == 10);
///
/// let mut xarray_clone = xarray_arc.clone();
/// assert!(*xarray_clone.load(10).unwrap().as_ref() == 10);
/// let value = Arc::new(100);
/// xarray_clone.store(10, value);
///
/// assert!(*xarray_arc.load(10).unwrap().as_ref() == 10);
/// assert!(*xarray_clone.load(10).unwrap().as_ref() == 100);
/// ```
///
/// The XArray concept was originally introduced by Linux, which keeps the data structure of [Linux
/// Radix Trees](https://lwn.net/Articles/175432/).
pub struct XArray<I, M = NoneMark>
where
I: ItemEntry,
M: Into<XMark>,
{
marks: [bool; 3],
head: XEntry<I>,
_marker: PhantomData<M>,
}
impl<I: ItemEntry, M: Into<XMark>> XArray<I, M> {
/// Makes a new, empty `XArray`.
pub const fn new() -> Self {
Self {
marks: [false; 3],
head: XEntry::EMPTY,
_marker: PhantomData,
}
}
/// Marks the `XArray` with the input `mark`.
pub fn set_mark(&mut self, mark: M) {
self.marks[mark.into().index()] = true;
}
/// Unsets the input `mark` for the `XArray`.
pub fn unset_mark(&mut self, mark: M) {
self.marks[mark.into().index()] = false;
}
/// Checks whether the `XArray` is marked with the input `mark`.
pub fn is_marked(&self, mark: M) -> bool {
self.marks[mark.into().index()]
}
/// Returns a shared reference to the head `XEntry`.
pub(super) fn head(&self) -> &XEntry<I> {
&self.head
}
/// Returns an exclusive reference to the head `XEntry`.
pub(super) fn head_mut(&mut self) -> &mut XEntry<I> {
&mut self.head
}
/// Increases the height of the `XArray` so that the `index`-th element can be stored.
pub(super) fn reserve(&mut self, index: u64) {
if self.head.is_null() {
let height = Height::from_index(index);
self.head = XEntry::from_node(XNode::new_root(height));
return;
}
loop {
let height = self.head.as_node_ref().unwrap().height();
if height.max_index() >= index {
return;
}
let old_entry = core::mem::replace(&mut self.head, XEntry::EMPTY);
let mut new_node = XNode::new_root(height.go_root());
new_node.set_entry(0, old_entry);
self.head = XEntry::from_node(new_node);
}
}
/// Calculates the maximum index of elements that can be stored with the current height of the
/// `XArray`.
pub(super) fn max_index(&self) -> u64 {
self.head()
.as_node_ref()
.map(|node| node.height().max_index())
.unwrap_or(0)
}
/// Loads the `index`-th item.
///
/// If the target item exists, it will be returned with `Some(_)`, otherwise, `None` will be
/// returned.
pub fn load(&self, index: u64) -> Option<I::Ref<'_>> {
let mut cursor = self.cursor(index);
cursor.load()
}
/// Stores the provided item in the `XArray` at the target index, and returns the old item if
/// some item was previously stored in the same position.
pub fn store(&mut self, index: u64, item: I) -> Option<I> {
let mut cursor = self.cursor_mut(index);
cursor.store(item)
}
/// Unsets the input `mark` for all of the items in the `XArray`.
pub fn unset_mark_all(&mut self, mark: M) {
let mut pending_nodes = VecDeque::new();
if let Some(node) = self.head.as_node_mut_or_cow() {
pending_nodes.push_back(node);
}
let mark_index = mark.into().index();
while let Some(node) = pending_nodes.pop_front() {
let node_mark = node.mark(mark_index);
node.clear_mark(mark_index);
node.entries_mut()
.iter_mut()
.enumerate()
.filter(|(offset, _)| node_mark.is_marked(*offset as u8))
.filter_map(|(_, next_entry)| next_entry.as_node_mut_or_cow())
.for_each(|next_node| pending_nodes.push_back(next_node));
}
}
/// Removes the `XEntry` in the `XArray` at the target index, and returns the removed item if
/// some item was previously stored in the same position.
pub fn remove(&mut self, index: u64) -> Option<I> {
let mut cursor = self.cursor_mut(index);
cursor.remove()
}
/// Creates a [`Cursor`] to perform read-related operations in the `XArray`.
pub fn cursor(&self, index: u64) -> Cursor<'_, I, M> {
Cursor::new(self, index)
}
/// Creates a [`CursorMut`] to perform read- and write-related operations in the `XArray`.
pub fn cursor_mut(&mut self, index: u64) -> CursorMut<'_, I, M> {
CursorMut::new(self, index)
}
/// Creates a [`Range`] which can be immutably iterated over the indexes corresponding to the
/// specified `range`.
pub fn range(&self, range: core::ops::Range<u64>) -> Range<'_, I, M> {
let cursor = Cursor::new(self, range.start);
Range::new(cursor, range.end)
}
}
impl<I: ItemEntry + Clone, M: Into<XMark>> Clone for XArray<I, M> {
/// Clones the `XArray` with the COW mechanism.
fn clone(&self) -> Self {
let cloned_head = self.head.clone();
Self {
marks: self.marks,
head: cloned_head,
_marker: PhantomData,
}
}
}