use crate::util::sys::Vec;
pub(crate) struct FrontBackVecBuilder<T> {
vec: Vec<T>,
capacity: usize,
front_len: usize,
back_len: usize,
}
impl<T> FrontBackVecBuilder<T> {
#[inline(always)]
pub(crate) fn with_capacity(capacity: usize) -> Self {
Self { vec: Vec::with_capacity(capacity), capacity, front_len: 0, back_len: 0 }
}
#[inline(always)]
pub(crate) fn push_front(&mut self, value: T) {
self.vec.spare_capacity_mut()[self.front_len].write(value);
self.front_len += 1;
}
#[inline(always)]
pub(crate) fn push_back(&mut self, value: T) {
self.vec.spare_capacity_mut()[self.capacity - self.back_len - 1].write(value);
self.back_len += 1;
}
#[inline(always)]
pub(crate) fn into_vec(self) -> (Vec<T>, usize) {
let Self { mut vec, capacity, front_len, back_len } = self;
assert_eq!(
front_len + back_len,
capacity,
"the number of items pushed to a FrontBackVecBuilder must equal its capacity"
);
#[allow(unsafe_code)]
unsafe {
vec.set_len(capacity);
}
(vec, front_len)
}
}
#[cfg(test)]
mod tests {
use super::FrontBackVecBuilder;
use crate::util::sys::Vec;
#[test]
fn front_items_keep_their_order_and_back_items_are_reversed() {
let mut builder = FrontBackVecBuilder::with_capacity(6);
for value in 0..6 {
if value % 2 == 0 {
builder.push_front(value);
} else {
builder.push_back(value);
}
}
let (vec, front_len) = builder.into_vec();
assert_eq!(vec, [0, 2, 4, 5, 3, 1]);
assert_eq!(front_len, 3);
}
#[test]
fn all_items_pushed_to_one_end() {
let mut builder = FrontBackVecBuilder::with_capacity(3);
(0..3).for_each(|value| builder.push_front(value));
assert_eq!(builder.into_vec(), (Vec::from([0, 1, 2]), 3));
let mut builder = FrontBackVecBuilder::with_capacity(3);
(0..3).for_each(|value| builder.push_back(value));
assert_eq!(builder.into_vec(), (Vec::from([2, 1, 0]), 0));
}
#[test]
fn zero_capacity() {
let (vec, front_len) = FrontBackVecBuilder::<u8>::with_capacity(0).into_vec();
assert!(vec.is_empty());
assert_eq!(front_len, 0);
}
#[test]
fn owned_items_are_moved_into_the_vec() {
let mut builder = FrontBackVecBuilder::with_capacity(2);
builder.push_back(Vec::from([1, 2]));
builder.push_front(Vec::from([3]));
let (vec, front_len) = builder.into_vec();
assert_eq!(vec, [Vec::from([3]), Vec::from([1, 2])]);
assert_eq!(front_len, 1);
}
#[test]
#[should_panic(expected = "must equal its capacity")]
fn pushing_too_many_items_to_the_front_panics_in_into_vec() {
let mut builder = FrontBackVecBuilder::with_capacity(1);
builder.push_back(1);
builder.push_front(2);
let _ = builder.into_vec();
}
#[test]
#[should_panic(expected = "must equal its capacity")]
fn pushing_too_many_items_to_the_back_panics_in_into_vec() {
let mut builder = FrontBackVecBuilder::with_capacity(1);
builder.push_front(1);
builder.push_back(2);
let _ = builder.into_vec();
}
#[test]
#[should_panic]
fn push_front_out_of_bounds_panics() {
let mut builder = FrontBackVecBuilder::with_capacity(0);
builder.push_front(1);
}
#[test]
#[should_panic]
fn push_back_out_of_bounds_panics() {
let mut builder = FrontBackVecBuilder::with_capacity(1);
builder.push_back(1);
builder.push_back(2);
}
#[test]
#[should_panic(expected = "must equal its capacity")]
fn into_vec_before_full_panics() {
let mut builder = FrontBackVecBuilder::with_capacity(2);
builder.push_front(1);
let _ = builder.into_vec();
}
}