use std::ptr::NonNull;
use rawdb::Region;
use crate::{HEADER_OFFSET, VecIndex, VecValue};
use super::super::RawStrategy;
use super::{RawIoSource, RawMmapSource};
enum Owner<'a, I, T, S> {
Mmap { _source: RawMmapSource<I, T, S> },
Io { source: RawIoSource<'a, I, T, S> },
}
pub struct RawRangeCursor<'a, I, T, S> {
owner: Owner<'a, I, T, S>,
window: *const u8,
window_position: usize,
window_len: usize,
unbuffered_bytes: usize,
range_end: usize,
}
impl<'a, I, T, S> RawRangeCursor<'a, I, T, S>
where
I: VecIndex,
T: VecValue,
S: RawStrategy<T>,
{
const SIZE_OF_T: usize = size_of::<T>();
pub(crate) fn new(region: &'a Region, stored_len: usize, from: usize, to: usize) -> Self {
let from = from.min(stored_len);
let to = to.min(stored_len).max(from);
let bytes = (to - from) * Self::SIZE_OF_T;
let offset = HEADER_OFFSET + from * Self::SIZE_OF_T;
if region.prefers_mmap(offset, bytes) {
let source = RawMmapSource::new_from_parts(region, stored_len, from, to);
let (window, window_len) = source.byte_window();
Self {
owner: Owner::Mmap { _source: source },
window,
window_position: 0,
window_len,
unbuffered_bytes: 0,
range_end: to,
}
} else {
Self {
owner: Owner::Io {
source: RawIoSource::new_from_parts(region, stored_len, from, to),
},
window: NonNull::dangling().as_ptr(),
window_position: 0,
window_len: 0,
unbuffered_bytes: bytes,
range_end: to,
}
}
}
#[inline(always)]
pub fn position(&self) -> usize {
self.range_end - self.remaining()
}
#[inline(always)]
pub fn remaining(&self) -> usize {
(self.unbuffered_bytes + self.window_len - self.window_position) / Self::SIZE_OF_T
}
#[cold]
#[inline(never)]
fn refill(&mut self) -> bool {
if self.unbuffered_bytes == 0 {
return false;
}
let Owner::Io { source } = &mut self.owner else {
unreachable!();
};
let Some((window, bytes)) = source.read_window() else {
return false;
};
self.window = window;
self.window_position = 0;
self.window_len = bytes;
self.unbuffered_bytes -= bytes;
true
}
pub fn advance(&mut self, n: usize) {
let values = n.min(self.remaining());
let bytes = values * Self::SIZE_OF_T;
let available = self.window_len - self.window_position;
if bytes <= available {
self.window_position += bytes;
return;
}
let skipped_file_bytes = bytes - available;
let Owner::Io { source } = &mut self.owner else {
unreachable!();
};
source.skip_file_bytes(skipped_file_bytes);
self.window_position = 0;
self.window_len = 0;
self.unbuffered_bytes -= skipped_file_bytes;
}
#[inline(always)]
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Option<T> {
if self.window_position == self.window_len && !self.refill() {
return None;
}
let value = unsafe { S::read_from_ptr(self.window, self.window_position) };
self.window_position += Self::SIZE_OF_T;
Some(value)
}
#[inline]
pub fn fold<B>(&mut self, n: usize, mut value: B, mut fold: impl FnMut(B, T) -> B) -> B {
let mut remaining = n.min(self.remaining());
while remaining > 0 {
if self.window_position == self.window_len && !self.refill() {
break;
}
let available = (self.window_len - self.window_position) / Self::SIZE_OF_T;
let values = remaining.min(available);
let end = self.window_position + values * Self::SIZE_OF_T;
while self.window_position < end {
value = fold(value, unsafe {
S::read_from_ptr(self.window, self.window_position)
});
self.window_position += Self::SIZE_OF_T;
}
remaining -= values;
}
value
}
#[inline]
pub fn for_each(&mut self, n: usize, mut f: impl FnMut(T)) {
for _ in 0..n.min(self.remaining()) {
f(self.next().unwrap());
}
}
}