use std::marker::PhantomData;
use crate::{READ_CHUNK_SIZE, ReadableVec, VecIndex, VecValue};
pub struct Cursor<'a, I: VecIndex, T: VecValue, V: ReadableVec<I, T> + ?Sized = dyn ReadableVec<I, T>> {
source: &'a V,
buf: Vec<T>,
buf_start: usize,
pos: usize,
chunk_size: usize,
len: usize,
_phantom: PhantomData<I>,
}
impl<'a, I: VecIndex, T: VecValue, V: ReadableVec<I, T> + ?Sized> Cursor<'a, I, T, V> {
#[inline]
pub fn new(source: &'a V) -> Self {
let len = source.len();
Self {
source,
buf: Vec::with_capacity(READ_CHUNK_SIZE.min(len)),
buf_start: 0,
pos: 0,
chunk_size: READ_CHUNK_SIZE,
len,
_phantom: PhantomData,
}
}
#[inline]
pub fn position(&self) -> usize {
self.pos
}
#[inline]
pub fn remaining(&self) -> usize {
self.len.saturating_sub(self.pos)
}
#[inline]
pub fn advance(&mut self, n: usize) {
self.pos = self.pos.saturating_add(n).min(self.len);
}
#[inline]
pub fn get(&mut self, index: usize) -> Option<T> {
if index >= self.len {
return None;
}
let local = self.ensure_buffered_at(index)?;
Some(self.buf[local].clone())
}
#[inline]
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Option<T> {
let local = self.ensure_buffered_at(self.pos)?;
let val = self.buf[local].clone();
self.pos += 1;
Some(val)
}
#[inline]
pub fn fold<B>(&mut self, n: usize, init: B, mut f: impl FnMut(B, T) -> B) -> B {
let target = self.pos.saturating_add(n).min(self.len);
let mut acc = init;
while self.pos < target {
if self.ensure_buffered_at(self.pos).is_none() {
break;
}
let local = self.pos - self.buf_start;
let local_end = (target - self.buf_start).min(self.buf.len());
for val in self.buf[local..local_end].iter().cloned() {
acc = f(acc, val);
}
self.pos = self.buf_start + local_end;
}
acc
}
#[inline]
pub fn for_each(&mut self, n: usize, mut f: impl FnMut(T)) {
self.fold(n, (), |(), v| f(v));
}
#[inline]
fn ensure_buffered_at(&mut self, at: usize) -> Option<usize> {
if at >= self.len {
return None;
}
let buf_end = self.buf_start + self.buf.len();
if at >= self.buf_start && at < buf_end {
return Some(at - self.buf_start);
}
self.buf.clear();
let aligned = (at / self.chunk_size) * self.chunk_size;
let end = (aligned + self.chunk_size).min(self.len);
self.buf_start = aligned;
self.source.read_into_at(aligned, end, &mut self.buf);
if self.buf.is_empty() { None } else { Some(at - aligned) }
}
}