use core::{fmt, iter::FusedIterator, ops::Range};
use crate::{BaoChunk, BlockSize, CHUNK_SIZE, TreeNode};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RangeTraversalError {
InvalidBlockSize,
InvalidRange,
}
impl fmt::Display for RangeTraversalError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::InvalidBlockSize => "invalid traversal block size",
Self::InvalidRange => "invalid traversal byte range",
})
}
}
impl core::error::Error for RangeTraversalError {}
#[derive(Debug, Clone, Copy)]
struct Frame {
first: u64,
count: u64,
is_root: bool,
}
pub struct RangeTraversal {
size: u64,
group_bytes: u64,
selected: Range<u64>,
frames: [Frame; 53],
pending: usize,
}
impl fmt::Debug for RangeTraversal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RangeTraversal")
.field("size", &self.size)
.field("group_bytes", &self.group_bytes)
.field("selected", &self.selected)
.field("pending", &self.pending)
.finish()
}
}
impl RangeTraversal {
pub fn new(
size: u64,
block_size: BlockSize,
request: Range<u64>,
) -> Result<Self, RangeTraversalError> {
if block_size.0 > 51 {
return Err(RangeTraversalError::InvalidBlockSize);
}
let group_bytes = (CHUNK_SIZE as u64) << block_size.0;
usize::try_from(group_bytes).map_err(|_| RangeTraversalError::InvalidBlockSize)?;
if request.start > request.end || request.end > size {
return Err(RangeTraversalError::InvalidRange);
}
let mut result = Self {
size,
group_bytes,
selected: 0..0,
frames: [Frame {
first: 0,
count: 0,
is_root: false,
}; 53],
pending: 0,
};
if request.start < request.end {
result.selected = request.start / group_bytes..(request.end - 1) / group_bytes + 1;
result.push(Frame {
first: 0,
count: size.div_ceil(group_bytes),
is_root: true,
});
}
Ok(result)
}
pub fn is_complete(&self) -> bool {
self.pending == 0
}
fn push(&mut self, frame: Frame) {
self.frames[self.pending] = frame;
self.pending += 1;
}
}
impl Iterator for RangeTraversal {
type Item = BaoChunk;
fn next(&mut self) -> Option<Self::Item> {
if self.is_complete() {
return None;
}
self.pending -= 1;
let frame = self.frames[self.pending];
if frame.count == 1 {
let start = frame.first * self.group_bytes;
return Some(BaoChunk::Leaf {
start_chunk: start / CHUNK_SIZE as u64,
size: self.group_bytes.min(self.size - start) as usize,
is_root: frame.is_root,
});
}
let left_count = 1u64 << (frame.count - 1).ilog2();
let middle = frame.first + left_count;
let end = frame.first + frame.count;
let left = self.selected.start < middle && frame.first < self.selected.end;
let right = self.selected.start < end && middle < self.selected.end;
if right {
self.push(Frame {
first: middle,
count: frame.count - left_count,
is_root: false,
});
}
if left {
self.push(Frame {
first: frame.first,
count: left_count,
is_root: false,
});
}
Some(BaoChunk::Parent {
node: TreeNode::new(2 * middle - 1),
is_root: frame.is_root,
left,
right,
})
}
}
impl FusedIterator for RangeTraversal {}