use crate::{Doubling, Fragment, Growth, SplitVec};
use alloc::string::String;
use orx_pseudo_default::PseudoDefault;
#[derive(Debug, Default, Clone, PartialEq)]
pub struct Recursive;
impl PseudoDefault for Recursive {
fn pseudo_default() -> Self {
Default::default()
}
}
impl Growth for Recursive {
#[inline(always)]
fn new_fragment_capacity_from(
&self,
fragment_capacities: impl ExactSizeIterator<Item = usize>,
) -> usize {
Doubling.new_fragment_capacity_from(fragment_capacities)
}
fn maximum_concurrent_capacity<T>(
&self,
fragments: &[Fragment<T>],
fragments_capacity: usize,
) -> usize {
assert!(fragments_capacity >= fragments.len());
let current_capacity = fragments.iter().map(|x| x.capacity()).sum();
let mut last_capacity = fragments.last().map(|x| x.capacity()).unwrap_or(2);
let mut total_capacity = current_capacity;
for _ in fragments.len()..fragments_capacity {
last_capacity *= 2;
total_capacity += last_capacity;
}
total_capacity
}
fn required_fragments_len<T>(
&self,
fragments: &[Fragment<T>],
maximum_capacity: usize,
) -> Result<usize, String> {
fn overflown_err() -> String {
alloc::format!(
"Maximum cumulative capacity that can be reached by the Recursive strategy is {}.",
usize::MAX
)
}
let current_capacity: usize = fragments.iter().map(|x| x.capacity()).sum();
let mut last_capacity = fragments.last().map(|x| x.capacity()).unwrap_or(2);
let mut total_capacity = current_capacity;
let mut f = fragments.len();
while total_capacity < maximum_capacity {
let (new_last_capacity, overflown) = last_capacity.overflowing_mul(2);
if overflown {
return Err(overflown_err());
}
last_capacity = new_last_capacity;
let (new_total_capacity, overflown) = total_capacity.overflowing_add(last_capacity);
if overflown {
return Err(overflown_err());
}
total_capacity = new_total_capacity;
f += 1;
}
Ok(f)
}
}
impl<T> SplitVec<T, Recursive> {
pub fn with_recursive_growth() -> Self {
SplitVec::with_doubling_growth().into()
}
pub fn with_recursive_growth_and_fragments_capacity(fragments_capacity: usize) -> Self {
SplitVec::with_doubling_growth_and_fragments_capacity(fragments_capacity).into()
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec::Vec;
use orx_pinned_vec::PinnedVec;
#[test]
fn get_fragment_and_inner_indices() {
let growth = Recursive;
let vecs = alloc::vec![
alloc::vec![0, 1, 2, 3],
alloc::vec![4, 5],
alloc::vec![6, 7, 8],
alloc::vec![9],
alloc::vec![10, 11, 12, 13, 14],
];
let mut fragments: Vec<Fragment<_>> = vecs.clone().into_iter().map(|x| x.into()).collect();
let len = fragments.iter().map(|x| x.len()).sum();
let mut index = 0;
for (f, vec) in vecs.iter().enumerate() {
for (i, _) in vec.iter().enumerate() {
let maybe_fi = growth.get_fragment_and_inner_indices(len, &fragments, index);
assert_eq!(maybe_fi, Some((f, i)));
let ptr = unsafe { growth.get_ptr_mut(&mut fragments, index) }.expect("is-some");
assert_eq!(unsafe { *ptr }, index);
unsafe { *ptr = 10 * index };
assert_eq!(unsafe { *ptr }, 10 * index);
index += 1;
}
}
}
#[test]
fn get_fragment_and_inner_indices_exhaustive() {
let growth = Recursive;
let mut fragments: Vec<Fragment<_>> = alloc::vec![];
let lengths = [30, 1, 7, 3, 79, 147, 530];
let mut index = 0;
for _ in 0..10 {
for &len in &lengths {
let mut vec = Vec::with_capacity(len);
for _ in 0..len {
vec.push(index);
index += 1;
}
fragments.push(vec.into());
}
}
let total_len = fragments.iter().map(|x| x.len()).sum();
let mut index = 0;
let mut f = 0;
for _ in 0..10 {
for &len in &lengths {
for i in 0..len {
let maybe_fi =
growth.get_fragment_and_inner_indices(total_len, &fragments, index);
assert_eq!(maybe_fi, Some((f, i)));
let ptr =
unsafe { growth.get_ptr_mut(&mut fragments, index) }.expect("is-some");
assert_eq!(unsafe { *ptr }, index);
unsafe { *ptr = 10 * index };
assert_eq!(unsafe { *ptr }, 10 * index);
index += 1;
}
f += 1;
}
}
}
#[test]
fn maximum_concurrent_capacity() {
fn max_cap<T>(vec: &SplitVec<T, Recursive>) -> usize {
vec.growth()
.maximum_concurrent_capacity(vec.fragments(), vec.fragments.capacity())
}
let mut vec: SplitVec<char, Recursive> = SplitVec::with_recursive_growth();
assert_eq!(max_cap(&vec), 4 + 8 + 16 + 32);
let until = max_cap(&vec);
for _ in 0..until {
vec.push('x');
assert_eq!(max_cap(&vec), 4 + 8 + 16 + 32);
}
vec.push('x');
assert_eq!(max_cap(&vec), 4 + 8 + 16 + 32 + 64 + 128 + 256 + 512);
}
#[test]
fn maximum_concurrent_capacity_when_appended() {
fn max_cap<T>(vec: &SplitVec<T, Recursive>) -> usize {
vec.growth()
.maximum_concurrent_capacity(vec.fragments(), vec.fragments.capacity())
}
let mut vec: SplitVec<char, Recursive> = SplitVec::with_recursive_growth();
assert_eq!(max_cap(&vec), 4 + 8 + 16 + 32);
vec.append(alloc::vec!['x'; 10]);
assert_eq!(max_cap(&vec), 4 + 10 + 20 + 40);
}
#[test]
fn with_recursive_growth_and_fragments_capacity_normal_growth() {
let mut vec: SplitVec<char, _> = SplitVec::with_recursive_growth_and_fragments_capacity(1);
assert_eq!(1, vec.fragments.capacity());
for _ in 0..100_000 {
vec.push('x');
}
assert!(vec.fragments.capacity() > 4);
}
#[test]
#[should_panic]
fn with_recursive_growth_and_fragments_capacity_zero() {
let _: SplitVec<char, _> = SplitVec::with_recursive_growth_and_fragments_capacity(0);
}
#[test]
fn required_fragments_len() {
let vec: SplitVec<char, Recursive> = SplitVec::with_recursive_growth();
let num_fragments = |max_cap| {
vec.growth()
.required_fragments_len(vec.fragments(), max_cap)
};
assert_eq!(num_fragments(0), Ok(1));
assert_eq!(num_fragments(1), Ok(1));
assert_eq!(num_fragments(4), Ok(1));
assert_eq!(num_fragments(5), Ok(2));
assert_eq!(num_fragments(12), Ok(2));
assert_eq!(num_fragments(13), Ok(3));
assert_eq!(num_fragments(36), Ok(4));
assert_eq!(num_fragments(67), Ok(5));
assert_eq!(num_fragments(136), Ok(6));
}
#[test]
fn required_fragments_len_when_appended() {
let mut vec: SplitVec<char, Recursive> = SplitVec::with_recursive_growth();
vec.append(alloc::vec!['x'; 10]);
let num_fragments = |max_cap| {
vec.growth()
.required_fragments_len(vec.fragments(), max_cap)
};
assert_eq!(num_fragments(0), Ok(2));
assert_eq!(num_fragments(1), Ok(2));
assert_eq!(num_fragments(14), Ok(2));
assert_eq!(num_fragments(15), Ok(3));
assert_eq!(num_fragments(21), Ok(3));
assert_eq!(num_fragments(34), Ok(3));
assert_eq!(num_fragments(35), Ok(4));
assert_eq!(num_fragments(74), Ok(4));
assert_eq!(num_fragments(75), Ok(5));
assert_eq!(num_fragments(154), Ok(5));
assert_eq!(num_fragments(155), Ok(6));
}
}