use crate::{Cursor, ReadResult, TableObject, TransactionKind};
use std::{borrow::Cow, marker::PhantomData};
pub struct IterDupFixed<
'tx,
'cur,
K: TransactionKind,
Key = Cow<'tx, [u8]>,
const VALUE_SIZE: usize = 0,
> {
cursor: Cow<'cur, Cursor<'tx, K>>,
current_key: Option<Key>,
current_page: Cow<'tx, [u8]>,
page_offset: usize,
exhausted: bool,
_marker: PhantomData<fn() -> Key>,
}
impl<K, Key, const VALUE_SIZE: usize> core::fmt::Debug for IterDupFixed<'_, '_, K, Key, VALUE_SIZE>
where
K: TransactionKind,
Key: core::fmt::Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let remaining = self.current_page.len().saturating_sub(self.page_offset) / VALUE_SIZE;
f.debug_struct("IterDupFixed")
.field("exhausted", &self.exhausted)
.field("remaining_in_page", &remaining)
.finish()
}
}
impl<'tx: 'cur, 'cur, K, Key, const VALUE_SIZE: usize> IterDupFixed<'tx, 'cur, K, Key, VALUE_SIZE>
where
K: TransactionKind,
{
pub(crate) fn new_end(cursor: Cow<'cur, Cursor<'tx, K>>) -> Self {
IterDupFixed {
cursor,
current_key: None,
current_page: Cow::Borrowed(&[]),
page_offset: 0,
exhausted: true,
_marker: PhantomData,
}
}
pub(crate) fn end_from_ref(cursor: &'cur mut Cursor<'tx, K>) -> Self {
Self::new_end(Cow::Borrowed(cursor))
}
pub(crate) fn new_with(
cursor: Cow<'cur, Cursor<'tx, K>>,
key: Key,
page: Cow<'tx, [u8]>,
) -> Self {
IterDupFixed {
cursor,
current_key: Some(key),
current_page: page,
page_offset: 0,
exhausted: false,
_marker: PhantomData,
}
}
pub(crate) fn from_ref_with(
cursor: &'cur mut Cursor<'tx, K>,
key: Key,
page: Cow<'tx, [u8]>,
) -> Self {
Self::new_with(Cow::Borrowed(cursor), key, page)
}
}
impl<'tx: 'cur, 'cur, K, Key, const VALUE_SIZE: usize> IterDupFixed<'tx, 'cur, K, Key, VALUE_SIZE>
where
K: TransactionKind,
Key: TableObject<'tx> + Clone,
{
fn consume_value(&mut self) -> Option<Cow<'tx, [u8]>> {
let end = self.page_offset.checked_add(VALUE_SIZE)?;
if end > self.current_page.len() {
return None;
}
let start = self.page_offset;
self.page_offset = end;
match &self.current_page {
Cow::Borrowed(slice) => Some(Cow::Borrowed(&slice[start..end])),
Cow::Owned(vec) => Some(Cow::Owned(vec[start..end].to_vec())),
}
}
fn fetch_next_page(&mut self) -> ReadResult<bool> {
let cursor = self.cursor.to_mut();
if let Some((key, page)) = cursor.next_multiple::<Key, Cow<'tx, [u8]>>()? {
self.current_key = Some(key);
self.current_page = page;
self.page_offset = 0;
return Ok(true);
}
if cursor.next_nodup::<Key, ()>()?.is_none() {
self.exhausted = true;
return Ok(false);
}
let Some(page) = cursor.get_multiple::<Cow<'tx, [u8]>>()? else {
self.exhausted = true;
return Ok(false);
};
let Some((key, _)) = cursor.get_current::<Key, ()>()? else {
self.exhausted = true;
return Ok(false);
};
self.current_key = Some(key);
self.current_page = page;
self.page_offset = 0;
Ok(true)
}
pub fn borrow_next(&mut self) -> ReadResult<Option<(Key, Cow<'tx, [u8]>)>> {
if self.exhausted {
return Ok(None);
}
if let Some(value) = self.consume_value() {
let key = self.current_key.clone().expect("key should be set when page is non-empty");
return Ok(Some((key, value)));
}
if !self.fetch_next_page()? {
return Ok(None);
}
let value = self.consume_value().expect("freshly fetched page should have values");
let key = self.current_key.clone().expect("key should be set after fetch");
Ok(Some((key, value)))
}
pub fn owned_next(&mut self) -> ReadResult<Option<(Key, [u8; VALUE_SIZE])>> {
self.borrow_next().map(|opt| {
opt.map(|(key, value)| {
let mut arr = [0u8; VALUE_SIZE];
arr.copy_from_slice(&value);
(key, arr)
})
})
}
}
impl<'tx: 'cur, 'cur, K, Key, const VALUE_SIZE: usize> Iterator
for IterDupFixed<'tx, 'cur, K, Key, VALUE_SIZE>
where
K: TransactionKind,
Key: TableObject<'tx> + Clone,
{
type Item = ReadResult<(Key, [u8; VALUE_SIZE])>;
fn next(&mut self) -> Option<Self::Item> {
self.owned_next().transpose()
}
}