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 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
use crate::{
fragment::fragment_struct::Fragment, growth::growth_trait::GrowthWithConstantTimeAccess,
Doubling, Growth,
};
/// A split vector; i.e., a vector of fragments, with the following features:
///
/// * Flexible in growth strategies; custom strategies can be defined.
/// * Growth does not cause any memory copies.
/// * Capacity of an already created fragment is never changed.
/// * The above feature allows the data to stay pinned in place. Memory location of an item added to the split vector will never change unless it is removed from the vector or the vector is dropped.
pub struct SplitVec<T, G = Doubling>
where
G: Growth,
{
pub(crate) len: usize,
pub(crate) fragments: Vec<Fragment<T>>,
/// Growth strategy of the split vector.
///
/// Note that allocated data of split vector is pinned and allocated in fragments.
/// Therefore, growth does not require copying data.
///
/// The growth strategy determines the capacity of each fragment
/// that will be added to the split vector when needed.
///
/// Furthermore, it has an impact on index-access to the elements.
/// See below for the complexities:
///
/// * `Linear` (`SplitVec::with_linear_growth`) -> O(1)
/// * `Doubling` (`SplitVec::with_doubling_growth`) -> O(1)
/// * `Recursive` (`SplitVec::with_recursive_growth`) -> O(f) where f is the number of fragments; and O(1) append time complexity
pub growth: G,
}
impl<T, G> SplitVec<T, G>
where
G: Growth,
{
/// Returns a mutable reference to the vector of fragments.
///
/// # Safety
///
/// Fragments of the split vector maintain the following structure:
/// * the fragments vector is never empty, it has at least one fragment;
/// * all fragments have a positive capacity;
/// * capacity of fragment f is equal to `self.growth.get_capacity(f)`.
/// * if there exist F fragments in the vector:
/// * none of the fragments with indices `0..F-2` has capacity; i.e., len==capacity,
/// * the last fragment at position `F-1` might or might not have capacity.
///
/// Breaking this structure invalidates the `SplitVec` struct,
/// and its methods lead to UB.
pub unsafe fn fragments_mut(&mut self) -> &mut Vec<Fragment<T>> {
&mut self.fragments
}
/// Returns the fragments of the split vector.
///
/// The fragments of the split vector satisfy the following structure:
/// * the fragments vector is never empty, it has at least one fragment;
/// * all fragments have a positive capacity;
/// * capacity of fragment f is equal to `self.growth.get_capacity(f)`.
/// * if there exist F fragments in the vector:
/// * none of the fragments with indices `0..F-2` has capacity; i.e., len==capacity,
/// * the last fragment at position `F-1` might or might not have capacity.
///
/// # Examples
///
/// ```
/// use orx_split_vec::prelude::*;
///
/// let mut vec = SplitVec::with_linear_growth(2);
///
/// for i in 0..6 {
/// vec.push(i);
/// }
///
/// assert_eq!(2, vec.fragments().len());
/// assert_eq!(&[0, 1, 2, 3], vec.fragments()[0].as_slice());
/// assert_eq!(&[4, 5], vec.fragments()[1].as_slice());
///
/// ```
pub fn fragments(&self) -> &[Fragment<T>] {
&self.fragments
}
/// Returns the fragment index and the index within fragment of the item with the given `index`;
/// None if the index is out of bounds.
///
/// # Examples
///
/// ```
/// use orx_split_vec::prelude::*;
///
/// let mut vec = SplitVec::with_linear_growth(2);
///
/// for i in 0..6 {
/// vec.push(i);
/// }
///
/// assert_eq!(&[0, 1, 2, 3], vec.fragments()[0].as_slice());
/// assert_eq!(&[4, 5], vec.fragments()[1].as_slice());
///
/// // first fragment
/// assert_eq!(Some((0, 0)), vec.get_fragment_and_inner_indices(0));
/// assert_eq!(Some((0, 1)), vec.get_fragment_and_inner_indices(1));
/// assert_eq!(Some((0, 2)), vec.get_fragment_and_inner_indices(2));
/// assert_eq!(Some((0, 3)), vec.get_fragment_and_inner_indices(3));
///
/// // second fragment
/// assert_eq!(Some((1, 0)), vec.get_fragment_and_inner_indices(4));
/// assert_eq!(Some((1, 1)), vec.get_fragment_and_inner_indices(5));
///
/// // out of bounds
/// assert_eq!(None, vec.get_fragment_and_inner_indices(6));
/// ```
#[inline(always)]
pub fn get_fragment_and_inner_indices(&self, index: usize) -> Option<(usize, usize)> {
self.growth
.get_fragment_and_inner_indices(self.len, &self.fragments, index)
}
// helpers
pub(crate) fn has_capacity_for_one(&self) -> bool {
self.fragments
.last()
.map(|f| f.has_capacity_for_one())
.unwrap_or(false)
}
pub(crate) fn add_fragment(&mut self) {
let capacity = self.growth.new_fragment_capacity(&self.fragments);
let new_fragment = Fragment::new(capacity);
self.fragments.push(new_fragment);
}
pub(crate) fn add_fragment_with_first_value(&mut self, first_value: T) {
let capacity = self.growth.new_fragment_capacity(&self.fragments);
let new_fragment = Fragment::new_with_first_value(capacity, first_value);
self.fragments.push(new_fragment);
}
pub(crate) fn drop_last_empty_fragment(&mut self) {
let drop_empty_last_fragment = self.fragments.last().map(|f| f.is_empty()).unwrap_or(false);
if drop_empty_last_fragment {
_ = self.fragments.pop();
}
}
}
impl<T, G: GrowthWithConstantTimeAccess> SplitVec<T, G> {
/// Returns a mutable reference to the `index`-th element of the vector if it belongs to the owned memory by the vector,
/// None otherwise.
///
/// # Safety
///
/// This method does not check whether or not `index` is out-of-bounds;
/// * Therefore, allows to write to an element which is not pushed yet.
///
/// On the other hand, it makes sure that the memory location is owned by this vector;
/// * Hence, does not lead to memory access violation.
/// * It returns `None` if the assumed position does not belong to this vector.
///
/// On the other hand, reading from the returned pointer is also unsafe.
/// Since, the method does not perform bounds check, caller might be reading an uninitialized value of `T`.
pub unsafe fn ptr_mut(&mut self, index: usize) -> Option<*mut T> {
let (f, i) = self.growth.get_fragment_and_inner_indices_unchecked(index);
self.fragments
.get_mut(f)
.map(|fragment| fragment.as_mut_ptr().add(i))
}
}
#[cfg(test)]
mod tests {
use crate::growth::growth_trait::GrowthWithConstantTimeAccess;
use crate::prelude::*;
use crate::test_all_growth_types;
#[test]
fn fragments() {
fn test<G: Growth>(mut vec: SplitVec<usize, G>) {
for i in 0..42 {
vec.push(i);
}
let mut combined = vec![];
for fra in vec.fragments() {
combined.extend_from_slice(fra);
}
for i in 0..42 {
assert_eq!(i, vec[i]);
assert_eq!(i, combined[i]);
}
}
test_all_growth_types!(test);
}
#[test]
fn get_fragment_and_inner_indices() {
fn test<G: Growth>(mut vec: SplitVec<usize, G>) {
for i in 0..432 {
vec.push(i);
assert_eq!(None, vec.get_fragment_and_inner_indices(i + 1));
}
for i in 0..432 {
let (f, ii) = vec.get_fragment_and_inner_indices(i).expect("is-some");
assert_eq!(vec[i], vec.fragments[f][ii]);
}
}
test_all_growth_types!(test);
}
#[test]
fn ptr_mut() {
fn test<G: GrowthWithConstantTimeAccess>(mut vec: SplitVec<usize, G>) {
for i in 0..65 {
vec.push(i);
}
for i in 0..64 {
let p = unsafe { vec.ptr_mut(i) }.expect("is-some");
assert_eq!(i, unsafe { *p });
}
for i in 64..vec.capacity() {
let p = unsafe { vec.ptr_mut(i) };
assert!(p.is_some());
}
for i in vec.capacity()..(vec.capacity() * 2) {
let p = unsafe { vec.ptr_mut(i) };
assert!(p.is_none());
}
}
test(SplitVec::with_doubling_growth());
test(SplitVec::with_linear_growth(6));
}
}