use crate::{
Cursor, MdbxError, ReadResult, TableObject, TableObjectOwned, Transaction, TransactionKind,
error::mdbx_result,
};
use std::{borrow::Cow, marker::PhantomData, ptr};
pub type IterKeyVals<'tx, 'cur, K, Key = Cow<'tx, [u8]>, Value = Cow<'tx, [u8]>> =
Iter<'tx, 'cur, K, Key, Value, { ffi::MDBX_NEXT }>;
pub type IterDupKeys<'tx, 'cur, K, Key = Cow<'tx, [u8]>, Value = Cow<'tx, [u8]>> =
Iter<'tx, 'cur, K, Key, Value, { ffi::MDBX_NEXT_NODUP }>;
pub type IterDupVals<'tx, 'cur, K, Key = Cow<'tx, [u8]>, Value = Cow<'tx, [u8]>> =
Iter<'tx, 'cur, K, Key, Value, { ffi::MDBX_NEXT_DUP }>;
enum IterState<'tx, Key = Cow<'tx, [u8]>, Value = Cow<'tx, [u8]>> {
Init((Key, Value), PhantomData<&'tx ()>),
Active,
End,
}
pub struct Iter<
'tx,
'cur,
K: TransactionKind,
Key = Cow<'tx, [u8]>,
Value = Cow<'tx, [u8]>,
const OP: u32 = { ffi::MDBX_NEXT },
> {
cursor: Cow<'cur, Cursor<'tx, K>>,
state: IterState<'tx, Key, Value>,
_marker: PhantomData<fn() -> (Key, Value)>,
}
impl<K, Key, Value, const OP: u32> core::fmt::Debug for Iter<'_, '_, K, Key, Value, OP>
where
K: TransactionKind,
Key: core::fmt::Debug,
Value: core::fmt::Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Iter").finish()
}
}
impl<'tx: 'cur, 'cur, K, Key, Value, const OP: u32> Iter<'tx, 'cur, K, Key, Value, OP>
where
K: TransactionKind,
{
pub(crate) fn new(cursor: Cow<'cur, Cursor<'tx, K>>) -> Self {
Iter { cursor, state: IterState::Active, _marker: PhantomData }
}
pub(crate) fn from_ref(cursor: &'cur mut Cursor<'tx, K>) -> Self {
Self::new(Cow::Borrowed(cursor))
}
pub fn from_owned(cursor: Cursor<'tx, K>) -> Self {
Self::new(Cow::Owned(cursor))
}
pub fn new_end(cursor: Cow<'cur, Cursor<'tx, K>>) -> Self {
Iter { cursor, state: IterState::End, _marker: PhantomData }
}
pub fn end_from_ref(cursor: &'cur mut Cursor<'tx, K>) -> Self {
Self::new_end(Cow::Borrowed(cursor))
}
pub fn end_from_owned(cursor: Cursor<'tx, K>) -> Self {
Self::new_end(Cow::Owned(cursor))
}
pub(crate) fn new_with(cursor: Cow<'cur, Cursor<'tx, K>>, first: (Key, Value)) -> Self {
Iter { cursor, state: IterState::Init(first, PhantomData), _marker: PhantomData }
}
pub(crate) fn from_ref_with(cursor: &'cur mut Cursor<'tx, K>, first: (Key, Value)) -> Self {
Self::new_with(Cow::Borrowed(cursor), first)
}
pub fn from_owned_with(cursor: Cursor<'tx, K>, first: (Key, Value)) -> Self {
Self::new_with(Cow::Owned(cursor), first)
}
fn execute_op(
&self,
key: &mut ffi::MDBX_val,
data: &mut ffi::MDBX_val,
) -> Result<bool, MdbxError> {
self.cursor.txn().txn_execute(|_tx| {
let res = unsafe { ffi::mdbx_cursor_get(self.cursor.cursor(), key, data, OP) };
match res {
ffi::MDBX_SUCCESS => Ok(true),
ffi::MDBX_NOTFOUND | ffi::MDBX_ENODATA | ffi::MDBX_RESULT_TRUE => Ok(false),
_ => mdbx_result(res).map(|_| false),
}
})?
}
}
impl<K, Key, Value, const OP: u32> Iter<'_, '_, K, Key, Value, OP>
where
K: TransactionKind,
Key: TableObjectOwned,
Value: TableObjectOwned,
{
pub fn owned_next(&mut self) -> ReadResult<Option<(Key, Value)>>
where
Key: TableObjectOwned,
Value: TableObjectOwned,
{
match self.state {
IterState::Active => {}
IterState::End => return Ok(None),
IterState::Init(_, _) => {
let IterState::Init(init, _) =
std::mem::replace(&mut self.state, IterState::Active)
else {
unreachable!()
};
return Ok(Some(init));
}
}
let mut key = ffi::MDBX_val { iov_len: 0, iov_base: ptr::null_mut() };
let mut data = ffi::MDBX_val { iov_len: 0, iov_base: ptr::null_mut() };
if !self.execute_op(&mut key, &mut data)? {
self.state = IterState::End;
return Ok(None);
}
let key = TableObject::decode_val::<K>(self.cursor.txn(), key)?;
let data = TableObject::decode_val::<K>(self.cursor.txn(), data)?;
Ok(Some((key, data)))
}
}
impl<'tx: 'cur, 'cur, K, Key, Value, const OP: u32> Iter<'tx, 'cur, K, Key, Value, OP>
where
K: TransactionKind,
Key: TableObject<'tx>,
Value: TableObject<'tx>,
{
pub fn borrow_next(&mut self) -> ReadResult<Option<(Key, Value)>> {
match self.state {
IterState::Active => {}
IterState::End => return Ok(None),
IterState::Init(_, _) => {
let IterState::Init(init, _) =
std::mem::replace(&mut self.state, IterState::Active)
else {
unreachable!()
};
return Ok(Some(init));
}
}
let mut key = ffi::MDBX_val { iov_len: 0, iov_base: ptr::null_mut() };
let mut data = ffi::MDBX_val { iov_len: 0, iov_base: ptr::null_mut() };
if !self.execute_op(&mut key, &mut data)? {
self.state = IterState::End;
return Ok(None);
}
let key = TableObject::decode_val::<K>(self.cursor.txn(), key)?;
let data = TableObject::decode_val::<K>(self.cursor.txn(), data)?;
Ok(Some((key, data)))
}
}
impl<K, Key, Value, const OP: u32> Iterator for Iter<'_, '_, K, Key, Value, OP>
where
K: TransactionKind,
Key: TableObjectOwned,
Value: TableObjectOwned,
{
type Item = ReadResult<(Key, Value)>;
fn next(&mut self) -> Option<Self::Item> {
self.owned_next().transpose()
}
}
pub struct IterDup<'tx, 'cur, K: TransactionKind, Key = Cow<'tx, [u8]>, Value = Cow<'tx, [u8]>> {
inner: IterDupKeys<'tx, 'cur, K, Key, Value>,
}
impl<'tx, 'cur, K, Key, Value> core::fmt::Debug for IterDup<'tx, 'cur, K, Key, Value>
where
K: TransactionKind,
Key: core::fmt::Debug,
Value: core::fmt::Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("IterDup").finish()
}
}
impl<'tx, 'cur, K, Key, Value> IterDup<'tx, 'cur, K, Key, Value>
where
K: TransactionKind,
{
pub(crate) fn new(cursor: Cow<'cur, Cursor<'tx, K>>) -> Self {
IterDup { inner: IterDupKeys::new(cursor) }
}
pub(crate) fn from_ref(cursor: &'cur mut Cursor<'tx, K>) -> Self {
Self::new(Cow::Borrowed(cursor))
}
pub fn from_owned(cursor: Cursor<'tx, K>) -> Self {
Self::new(Cow::Owned(cursor))
}
pub(crate) fn new_with(cursor: Cow<'cur, Cursor<'tx, K>>, first: (Key, Value)) -> Self {
IterDup { inner: Iter::new_with(cursor, first) }
}
pub fn from_ref_with(cursor: &'cur mut Cursor<'tx, K>, first: (Key, Value)) -> Self {
Self::new_with(Cow::Borrowed(cursor), first)
}
pub fn new_end(cursor: Cow<'cur, Cursor<'tx, K>>) -> Self {
IterDup { inner: Iter::new_end(cursor) }
}
pub fn end_from_ref(cursor: &'cur mut Cursor<'tx, K>) -> Self {
Self::new_end(Cow::Borrowed(cursor))
}
pub fn end_from_owned(cursor: Cursor<'tx, K>) -> Self {
Self::new_end(Cow::Owned(cursor))
}
}
impl<'tx: 'cur, 'cur, K, Key, Value> IterDup<'tx, 'cur, K, Key, Value>
where
K: TransactionKind,
Key: TableObject<'tx>,
Value: TableObject<'tx>,
{
pub fn borrow_next(&mut self) -> ReadResult<Option<IterDupVals<'tx, 'cur, K, Key, Value>>> {
let cursor_ptr = self.inner.cursor.as_ref().cursor();
let tx: *const Transaction<K> = self.inner.cursor.txn();
let tx = unsafe { tx.as_ref().unwrap() };
match self.inner.borrow_next()? {
Some((key, value)) => {
let db = self.inner.cursor.as_ref().db();
let dup_cursor = tx.txn_execute(move |_| unsafe {
let new_cursor = ffi::mdbx_cursor_create(ptr::null_mut());
let res = ffi::mdbx_cursor_copy(cursor_ptr, new_cursor);
mdbx_result(res)?;
Ok::<_, MdbxError>(Cursor::new_raw(tx, new_cursor, db))
})??;
Ok(Some(IterDupVals::from_owned_with(dup_cursor, (key, value))))
}
None => Ok(None),
}
}
}
impl<'tx: 'cur, 'cur, K, Key, Value> IterDup<'tx, 'cur, K, Key, Value>
where
K: TransactionKind,
Key: TableObjectOwned,
Value: TableObjectOwned,
{
pub fn owned_next(&mut self) -> ReadResult<Option<IterDupVals<'tx, 'cur, K, Key, Value>>> {
self.borrow_next()
}
}
impl<'tx: 'cur, 'cur, K, Key, Value> Iterator for IterDup<'tx, 'cur, K, Key, Value>
where
K: TransactionKind,
Key: TableObjectOwned,
Value: TableObjectOwned,
{
type Item = ReadResult<IterDupVals<'tx, 'cur, K, Key, Value>>;
fn next(&mut self) -> Option<Self::Item> {
self.owned_next().transpose()
}
}