use super::constants::FIXED_CAPACITIES;
use crate::growth::growth_trait::{Growth, GrowthWithConstantTimeAccess};
use crate::{Fragment, SplitVec};
use alloc::string::String;
use orx_pseudo_default::PseudoDefault;
#[derive(Debug, Clone, PartialEq)]
pub struct Linear {
constant_fragment_capacity_exponent: usize,
constant_fragment_capacity: usize,
}
impl Linear {
pub fn new(constant_fragment_capacity_exponent: usize) -> Self {
assert!(
constant_fragment_capacity_exponent > 0
&& constant_fragment_capacity_exponent < FIXED_CAPACITIES.len(),
"constant_fragment_capacity_exponent must be within 1..32 (1..29) for 64-bit (32-bit) platforms."
);
let constant_fragment_capacity = FIXED_CAPACITIES[constant_fragment_capacity_exponent];
Self {
constant_fragment_capacity_exponent,
constant_fragment_capacity,
}
}
}
impl PseudoDefault for Linear {
fn pseudo_default() -> Self {
Self::new(10)
}
}
impl Growth for Linear {
#[inline(always)]
fn new_fragment_capacity_from(
&self,
_fragment_capacities: impl ExactSizeIterator<Item = usize>,
) -> usize {
self.constant_fragment_capacity
}
#[inline(always)]
fn get_fragment_and_inner_indices<T>(
&self,
vec_len: usize,
_fragments: &[Fragment<T>],
element_index: usize,
) -> Option<(usize, usize)> {
match element_index < vec_len {
true => Some(self.get_fragment_and_inner_indices_unchecked(element_index)),
false => None,
}
}
#[inline(always)]
fn get_ptr<T>(&self, fragments: &[Fragment<T>], index: usize) -> Option<*const T> {
<Self as GrowthWithConstantTimeAccess>::get_ptr(self, fragments, index)
}
#[inline(always)]
fn get_ptr_mut<T>(&self, fragments: &mut [Fragment<T>], index: usize) -> Option<*mut T> {
<Self as GrowthWithConstantTimeAccess>::get_ptr_mut(self, fragments, index)
}
fn get_ptr_mut_and_indices<T>(
&self,
fragments: &mut [Fragment<T>],
index: usize,
) -> Option<(*mut T, usize, usize)> {
<Self as GrowthWithConstantTimeAccess>::get_ptr_mut_and_indices(self, fragments, index)
}
fn maximum_concurrent_capacity<T>(
&self,
fragments: &[Fragment<T>],
fragments_capacity: usize,
) -> usize {
assert!(fragments_capacity >= fragments.len());
fragments_capacity * self.constant_fragment_capacity
}
fn required_fragments_len<T>(
&self,
_: &[Fragment<T>],
maximum_capacity: usize,
) -> Result<usize, String> {
let bound = self.maximum_concurrent_capacity_bound::<T>(&[], 0);
if maximum_capacity > bound {
return Err(alloc::format!(
"Maximum cumulative capacity that can be reached by the Linear strategy is {}.",
bound,
));
}
let num_full_fragments = maximum_capacity / self.constant_fragment_capacity;
let remainder = maximum_capacity % self.constant_fragment_capacity;
let additional_fragment = if remainder > 0 { 1 } else { 0 };
Ok(num_full_fragments + additional_fragment)
}
fn maximum_concurrent_capacity_bound<T>(&self, _: &[Fragment<T>], _: usize) -> usize {
*FIXED_CAPACITIES
.last()
.expect("fixed capacities is non-empty")
}
}
impl GrowthWithConstantTimeAccess for Linear {
#[inline(always)]
fn get_fragment_and_inner_indices_unchecked(&self, element_index: usize) -> (usize, usize) {
let f = element_index >> self.constant_fragment_capacity_exponent;
let i = element_index % self.constant_fragment_capacity;
(f, i)
}
fn fragment_capacity_of(&self, _: usize) -> usize {
self.constant_fragment_capacity
}
}
impl<T> SplitVec<T, Linear> {
pub fn with_linear_growth(constant_fragment_capacity_exponent: usize) -> Self {
assert!(
constant_fragment_capacity_exponent > 0
&& constant_fragment_capacity_exponent < FIXED_CAPACITIES.len(),
"constant_fragment_capacity_exponent must be within 1..32 (1..29) for 64-bit (32-bit) platforms."
);
let constant_fragment_capacity = FIXED_CAPACITIES[constant_fragment_capacity_exponent];
let fragments = Fragment::new_empty(constant_fragment_capacity).into_fragments();
let growth = Linear::new(constant_fragment_capacity_exponent);
Self::from_raw_parts(0, fragments, growth)
}
pub fn with_linear_growth_and_fragments_capacity(
constant_fragment_capacity_exponent: usize,
fragments_capacity: usize,
) -> Self {
assert!(
constant_fragment_capacity_exponent > 0
&& constant_fragment_capacity_exponent < FIXED_CAPACITIES.len(),
"constant_fragment_capacity_exponent must be within 1..{}",
FIXED_CAPACITIES.len()
);
assert!(fragments_capacity > 0);
let constant_fragment_capacity = FIXED_CAPACITIES[constant_fragment_capacity_exponent];
let fragments = Fragment::new_empty(constant_fragment_capacity)
.into_fragments_with_capacity(fragments_capacity);
let growth = Linear::new(constant_fragment_capacity_exponent);
Self::from_raw_parts(0, fragments, growth)
}
}