use super::DupItem;
use crate::{Cursor, ReadResult, TableObject, TableObjectOwned, TransactionKind};
use std::{borrow::Cow, marker::PhantomData};
pub struct IterDupFixed<'tx, 'cur, K: TransactionKind, Key = Cow<'tx, [u8]>, Value = Cow<'tx, [u8]>>
{
cursor: &'cur mut Cursor<'tx, K>,
current_key: Option<Key>,
current_page: Cow<'tx, [u8]>,
page_offset: usize,
value_size: usize,
remaining: usize,
exhausted: bool,
_marker: PhantomData<fn() -> (Key, Value)>,
}
impl<K, Key, Value> core::fmt::Debug for IterDupFixed<'_, '_, K, Key, Value>
where
K: TransactionKind,
Key: core::fmt::Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let remaining_in_page = if self.value_size > 0 {
self.current_page.len().saturating_sub(self.page_offset) / self.value_size
} else {
0
};
f.debug_struct("IterDupFixed")
.field("exhausted", &self.exhausted)
.field("value_size", &self.value_size)
.field("remaining_in_page", &remaining_in_page)
.field("remaining_for_key", &self.remaining)
.finish()
}
}
impl<'tx: 'cur, 'cur, K, Key, Value> IterDupFixed<'tx, 'cur, K, Key, Value>
where
K: TransactionKind,
{
pub const fn value_size(&self) -> usize {
self.value_size
}
pub(crate) fn new_end(cursor: &'cur mut Cursor<'tx, K>) -> Self {
IterDupFixed {
cursor,
current_key: None,
current_page: Cow::Borrowed(&[]),
page_offset: 0,
value_size: 0,
remaining: 0,
exhausted: true,
_marker: PhantomData,
}
}
pub(crate) fn new_with(
cursor: &'cur mut Cursor<'tx, K>,
key: Key,
page: Cow<'tx, [u8]>,
value_size: usize,
) -> Self {
debug_assert!(value_size > 0, "DUPFIXED value size must be greater than zero");
let remaining = cursor.dup_count().unwrap_or(1);
IterDupFixed {
cursor,
current_key: Some(key),
current_page: page,
page_offset: 0,
value_size,
remaining,
exhausted: false,
_marker: PhantomData,
}
}
}
impl<'tx: 'cur, 'cur, K, Key, Value> IterDupFixed<'tx, 'cur, K, Key, Value>
where
K: TransactionKind,
Key: TableObject<'tx>,
{
fn consume_value(&mut self) -> Option<Cow<'tx, [u8]>> {
let end = self.page_offset.checked_add(self.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> {
if let Some((key, page)) = self.cursor.next_multiple::<Key, Cow<'tx, [u8]>>()? {
self.current_key = Some(key);
self.current_page = page;
self.page_offset = 0;
return Ok(true);
}
if self.cursor.next_nodup::<Key, ()>()?.is_none() {
self.exhausted = true;
return Ok(false);
}
let Some(page) = self.cursor.get_multiple::<Cow<'tx, [u8]>>()? else {
self.exhausted = true;
return Ok(false);
};
let Some((key, _)) = self.cursor.get_current::<Key, ()>()? else {
self.exhausted = true;
return Ok(false);
};
self.remaining = self.cursor.dup_count().unwrap_or(1);
self.current_key = Some(key);
self.current_page = page;
self.page_offset = 0;
Ok(true)
}
pub fn borrow_next(&mut self) -> ReadResult<Option<DupItem<Key, Cow<'tx, [u8]>>>> {
if self.exhausted {
return Ok(None);
}
let value = match self.consume_value() {
Some(v) => v,
None => {
if !self.fetch_next_page()? {
return Ok(None);
}
self.consume_value().expect("freshly fetched page should have values")
}
};
if self.remaining == 0 {
self.remaining = self.remaining.saturating_sub(1);
let key = self.current_key.take().expect("key should be set after fetch");
return Ok(Some(DupItem::NewKey(key, value)));
}
if self.current_key.is_some() {
self.remaining -= 1;
let key = self.current_key.take().expect("key should be set");
return Ok(Some(DupItem::NewKey(key, value)));
}
self.remaining = self.remaining.saturating_sub(1);
Ok(Some(DupItem::SameKey(value)))
}
pub fn owned_next(&mut self) -> ReadResult<Option<DupItem<Key, Value>>>
where
Value: TableObjectOwned,
{
self.borrow_next()?
.map(|item| match item {
DupItem::NewKey(k, cow) => Value::decode(&cow).map(|v| DupItem::NewKey(k, v)),
DupItem::SameKey(cow) => Value::decode(&cow).map(DupItem::SameKey),
})
.transpose()
}
}
impl<'tx: 'cur, 'cur, K, Key, Value> Iterator for IterDupFixed<'tx, 'cur, K, Key, Value>
where
K: TransactionKind,
Key: TableObject<'tx>,
Value: TableObjectOwned,
{
type Item = ReadResult<DupItem<Key, Value>>;
fn next(&mut self) -> Option<Self::Item> {
self.owned_next().transpose()
}
fn size_hint(&self) -> (usize, Option<usize>) {
if self.exhausted || self.value_size == 0 {
return (0, Some(0));
}
(self.remaining, None)
}
}