use core::mem;
use smallvec::SmallVec;
use crate::{AnyBin, BinBuilder, BinFactory, BinSegment, SBin, Segment, StackBin, StackBinBuilder};
use std::marker::PhantomData;
pub struct DefaultBinBuilder<'a, TFactory: BinFactory, TConfig> {
state: State<'a, TFactory::T>,
_phantom: PhantomData<TConfig>,
}
impl<'a, TFactory: BinFactory, TConfig> DefaultBinBuilder<'a, TFactory, TConfig> {
pub fn new() -> Self {
Self {
state: State::Stage0Empty,
_phantom: Default::default(),
}
}
}
pub trait BuilderCfg<TAnyBin: AnyBin> {
fn convert_from_sbin_to_t(sbin: SBin) -> TAnyBin;
fn vec_excess_capacity() -> usize;
}
const SMALL_VEC_MAX_SEGMENTS: usize = 12;
impl<'a, TFactory, TConfig> BinBuilder<'a> for DefaultBinBuilder<'a, TFactory, TConfig>
where
TFactory: BinFactory,
TConfig: BuilderCfg<TFactory::T>,
{
type T = TFactory::T;
#[inline]
fn push(&mut self, segment: impl Into<BinSegment<'a, Self::T>>) {
let segment = segment.into();
if segment.is_empty() {
return;
}
match &mut self.state {
State::Stage0Empty => self.state = State::Stage1Single(segment),
State::Stage1Single(single) => {
let single = mem::replace(single, BinSegment::Empty);
let stack_builder = {
let mut stack_builder = StackBinBuilder::new(0);
let fits_onto_stack = stack_builder.try_extend_from_slice(single.as_slice());
if !fits_onto_stack {
None
} else {
let fits_onto_stack =
stack_builder.try_extend_from_slice(segment.as_slice());
if !fits_onto_stack {
None
} else {
Some(stack_builder)
}
}
};
if let Some(stack_builder) = stack_builder {
self.state = State::Stage2Stack(stack_builder);
} else {
let mut segments = SegmentsSmallVec::new();
let number_of_bytes = single
.number_of_bytes()
.checked_add(segment.number_of_bytes())
.unwrap();
segments.push(single);
segments.push(segment);
self.state = State::Stage3Large {
segments,
number_of_bytes,
}
}
}
State::Stage2Stack(stack_builder) => {
let fits_onto_stack = stack_builder.try_extend_from_slice(segment.as_slice());
if fits_onto_stack {
} else {
let sbin = stack_builder.build_stack_only().expect(
"Implementation \
error: We made sure that the stack builder does not grow too large.",
);
let mut segments = SegmentsSmallVec::new();
let number_of_bytes =
sbin.len().checked_add(segment.number_of_bytes()).unwrap();
segments.push(BinSegment::Bin(TConfig::convert_from_sbin_to_t(sbin)));
segments.push(segment);
maybe_compress::<Self::T, TConfig>(&mut segments);
self.state = State::Stage3Large {
segments,
number_of_bytes,
}
}
}
State::Stage3Large {
segments,
number_of_bytes,
} => {
let new_number_of_bytes = (*number_of_bytes)
.checked_add(segment.number_of_bytes())
.unwrap();
*number_of_bytes = new_number_of_bytes;
segments.push(segment);
maybe_compress::<Self::T, TConfig>(segments);
}
}
}
fn build(&mut self) -> Self::T {
let taken_state = mem::replace(&mut self.state, State::Stage0Empty);
match taken_state {
State::Stage0Empty => TFactory::empty(),
State::Stage1Single(single) => TFactory::from_segment(single),
State::Stage2Stack(stack_builder) => {
let sbin = stack_builder.build_stack_only().expect(
"Implementation \
error: We made sure that the stack builder does not grow too large.",
);
TConfig::convert_from_sbin_to_t(sbin)
}
State::Stage3Large {
segments,
number_of_bytes,
} => {
let mut vec = Vec::with_capacity(
TConfig::vec_excess_capacity()
.checked_add(number_of_bytes)
.unwrap(),
);
for segment in segments {
vec.extend_from_slice(segment.as_slice());
}
TFactory::from_given_vec(vec)
}
}
}
}
type SegmentsSmallVec<'a, TAnyBin> = SmallVec<[BinSegment<'a, TAnyBin>; SMALL_VEC_MAX_SEGMENTS]>;
enum State<'a, TAnyBin: AnyBin> {
Stage0Empty,
Stage1Single(BinSegment<'a, TAnyBin>),
Stage2Stack(StackBinBuilder),
Stage3Large {
segments: SegmentsSmallVec<'a, TAnyBin>,
number_of_bytes: usize,
},
}
#[inline]
fn maybe_compress<TAnyBin, TConfig>(vec: &mut SegmentsSmallVec<TAnyBin>)
where
TConfig: BuilderCfg<TAnyBin>,
TAnyBin: AnyBin,
{
let len = vec.len();
if len > SMALL_VEC_MAX_SEGMENTS {
} else if len > 1 {
let vec_slice = vec.as_slice();
let last = &vec_slice[len - 1];
let second_last = &vec_slice[len - 2];
let len_of_two_last = last
.number_of_bytes()
.checked_add(second_last.number_of_bytes())
.unwrap();
if len_of_two_last <= StackBin::max_len() {
let mut stack_builder = StackBinBuilder::new(0);
if !stack_builder.try_extend_from_slice(second_last.as_slice()) {
panic!("Implementation error (must fit onto the stack)")
}
if !stack_builder.try_extend_from_slice(last.as_slice()) {
panic!("Implementation error (must fit onto the stack)");
}
vec.pop()
.expect("Expected element missing (maybe_compress)");
vec.pop()
.expect("Expected element missing (maybe_compress)");
vec.push(BinSegment::Bin(TConfig::convert_from_sbin_to_t(
stack_builder
.build_stack_only()
.expect("Implementation error (must fit onto the stack)."),
)));
}
}
}