use std::mem::size_of;
const FIRST: usize = 128;
const LARGEST: usize = 8_192;
pub(crate) const WITHIN_BITS: u32 = LARGEST.ilog2();
const STEPS: usize = (LARGEST / FIRST).ilog2() as usize;
const RAMP: usize = LARGEST - FIRST;
#[inline]
pub(crate) fn size(block: usize) -> usize {
if block < STEPS { FIRST << block } else { LARGEST }
}
pub(crate) fn start(block: usize) -> usize {
if block < STEPS { FIRST * ((1 << block) - 1) } else { RAMP + (block - STEPS) * LARGEST }
}
#[inline]
pub(crate) fn locate(at: usize) -> (usize, usize) {
if at < RAMP {
let block = (at / FIRST + 1).ilog2() as usize;
(block, at - FIRST * ((1 << block) - 1))
} else {
let past = at - RAMP;
(STEPS + past / LARGEST, past % LARGEST)
}
}
#[derive(Debug)]
pub(crate) struct Blocks<T> {
full: Vec<Vec<T>>,
held: usize,
tail: Vec<T>,
}
impl<T> Default for Blocks<T> {
fn default() -> Self {
Self { full: Vec::new(), held: 0, tail: Vec::new() }
}
}
impl<T: Copy> Blocks<T> {
#[inline]
pub(crate) fn push(&mut self, value: T) {
if self.tail.len() == self.tail.capacity() {
self.grow();
}
self.tail.push(value);
}
#[cold]
fn grow(&mut self) {
if self.tail.capacity() != 0 {
let full = std::mem::take(&mut self.tail);
self.held += full.len();
self.full.push(full);
}
self.tail = Vec::with_capacity(size(self.full.len()));
}
pub(crate) fn into_blocks(self) -> impl Iterator<Item = Vec<T>> {
self.full.into_iter().chain(std::iter::once(self.tail))
}
pub(crate) fn len(&self) -> usize {
self.held + self.tail.len()
}
pub(crate) fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub(crate) fn slot(&self, block: usize, within: usize) -> &T {
if block < self.full.len() { &self.full[block][within] } else { &self.tail[within] }
}
#[inline]
pub(crate) fn slot_mut(&mut self, block: usize, within: usize) -> &mut T {
if block < self.full.len() { &mut self.full[block][within] } else { &mut self.tail[within] }
}
pub(crate) fn slices(&self) -> impl Iterator<Item = &[T]> {
self.full.iter().map(Vec::as_slice).chain(std::iter::once(self.tail.as_slice()))
}
pub(crate) fn footprint(&self) -> usize {
let blocks: usize = self.full.iter().map(Vec::capacity).sum();
(blocks + self.tail.capacity()) * size_of::<T>()
+ self.full.capacity() * size_of::<Vec<T>>()
}
}
impl<T: Copy> FromIterator<T> for Blocks<T> {
fn from_iter<I: IntoIterator<Item = T>>(values: I) -> Self {
let mut blocks = Self::default();
for value in values {
blocks.push(value);
}
blocks
}
}
#[cfg(test)]
mod tests {
use super::Blocks;
#[test]
fn blocks_give_back_what_was_pushed_in_order() {
let mut blocks = Blocks::default();
assert!(blocks.is_empty());
for value in 0..200_000_u64 {
blocks.push(value);
}
assert_eq!(blocks.len(), 200_000);
let read: Vec<u64> = blocks.slices().flatten().copied().collect();
assert_eq!(read, (0..200_000).collect::<Vec<_>>());
assert!(blocks.slices().all(|slice| slice.len() <= super::LARGEST));
assert!(blocks.footprint() >= 200_000 * 8);
assert!(blocks.footprint() < 200_000 * 8 + super::LARGEST * 8 + 4096);
}
#[test]
fn blocks_are_read_and_written_by_place() {
let mut blocks: Blocks<[u64; 3]> = Blocks::default();
for value in 0..100_000_u64 {
blocks.push([value, 0, 0]);
}
for at in [0, 1, 127, 128, 383, 384, 1_000, 8_063, 8_064, 20_000, 99_999] {
let (block, within) = super::locate(at);
assert_eq!(super::start(block) + within, at);
assert!(within < super::size(block) && within < 1 << super::WITHIN_BITS);
assert_eq!(blocks.slot(block, within)[0], at as u64);
blocks.slot_mut(block, within)[1] = 1;
}
let read: Vec<[u64; 3]> = blocks.into_blocks().flatten().collect();
assert_eq!(read.len(), 100_000);
assert!(read.iter().enumerate().all(|(at, value)| value[0] == at as u64));
assert_eq!(read.iter().filter(|value| value[1] == 1).count(), 11);
}
}