use std::marker::PhantomData;
use std::mem::size_of;
use crate::Key;
use crate::codec::Codec;
use crate::error::{DbError, DbResult};
use crate::lsm::map_fjall;
pub(crate) struct LsmKeyIter<K> {
inner: fjall::Iter,
reversed: bool,
_marker: PhantomData<K>,
}
pub struct TryLsmKeyIter<K> {
inner: fjall::Iter,
reversed: bool,
failed: bool,
#[cfg(test)]
force_guard_error: bool,
_marker: PhantomData<K>,
}
impl<K> TryLsmKeyIter<K> {
pub(crate) fn new(inner: fjall::Iter, reversed: bool) -> Self {
Self {
inner,
reversed,
failed: false,
#[cfg(test)]
force_guard_error: false,
_marker: PhantomData,
}
}
#[cfg(test)]
pub(crate) fn new_with_forced_guard_error(inner: fjall::Iter, reversed: bool) -> Self {
Self {
force_guard_error: true,
..Self::new(inner, reversed)
}
}
}
impl<K: Key> Iterator for TryLsmKeyIter<K> {
type Item = DbResult<K>;
fn next(&mut self) -> Option<Self::Item> {
if self.failed {
return None;
}
#[cfg(test)]
if self.force_guard_error {
self.force_guard_error = false;
self.failed = true;
return Some(Err(DbError::Fjall(
"test-injected lsm key guard error".to_owned(),
)));
}
let guard = if self.reversed {
self.inner.next_back()?
} else {
self.inner.next()?
};
let result = map_fjall(guard.key()).and_then(|raw| {
(raw.len() == size_of::<K>())
.then(|| K::from_bytes(raw.as_ref()))
.ok_or(DbError::CorruptedEntry { offset: 0 })
});
self.failed = result.is_err();
Some(result)
}
}
impl<K> LsmKeyIter<K> {
pub(crate) fn new(inner: fjall::Iter, reversed: bool) -> Self {
Self {
inner,
reversed,
_marker: PhantomData,
}
}
}
impl<K: Key> Iterator for LsmKeyIter<K> {
type Item = K;
fn next(&mut self) -> Option<Self::Item> {
loop {
let g = if self.reversed {
self.inner.next_back()
} else {
self.inner.next()
}?;
match g.key() {
Ok(raw) => return Some(K::from_bytes(raw.as_ref())),
Err(e) => tracing::warn!(%e, "lsm_key_iter: guard error, skipping"),
}
}
}
}
pub struct LsmIter<'a, K, T, C> {
inner: fjall::Iter,
reversed: bool,
codec: &'a C,
_marker: PhantomData<fn() -> (K, T)>,
}
impl<'a, K, T, C> LsmIter<'a, K, T, C> {
pub(crate) fn new(inner: fjall::Iter, reversed: bool, codec: &'a C) -> Self {
Self {
inner,
reversed,
codec,
_marker: PhantomData,
}
}
}
impl<K: Key, T, C: Codec<T>> LsmIter<'_, K, T, C> {
fn decode_guard(&self, g: fjall::Guard) -> Option<(K, T)> {
match g.into_inner() {
Ok((uk, uv)) => match self.codec.decode_from(uv.as_ref()) {
Ok(v) => Some((K::from_bytes(uk.as_ref()), v)),
Err(_) => {
tracing::debug!(value_len = uv.len(), "lsm_iter: decode error, skipping");
None
}
},
Err(e) => {
tracing::warn!(%e, "lsm_iter: guard error, skipping");
None
}
}
}
}
impl<K: Key, T, C: Codec<T>> Iterator for LsmIter<'_, K, T, C> {
type Item = (K, T);
fn next(&mut self) -> Option<Self::Item> {
loop {
let g = if self.reversed {
self.inner.next_back()
} else {
self.inner.next()
}?;
if let Some(kv) = self.decode_guard(g) {
return Some(kv);
}
}
}
}
impl<K: Key, T, C: Codec<T>> DoubleEndedIterator for LsmIter<'_, K, T, C> {
fn next_back(&mut self) -> Option<Self::Item> {
loop {
let g = if self.reversed {
self.inner.next()
} else {
self.inner.next_back()
}?;
if let Some(kv) = self.decode_guard(g) {
return Some(kv);
}
}
}
}
pub struct TryLsmIter<'a, K, T, C> {
inner: fjall::Iter,
codec: &'a C,
reversed: bool,
failed: bool,
_marker: PhantomData<fn() -> (K, T)>,
}
impl<'a, K, T, C> TryLsmIter<'a, K, T, C> {
pub(crate) fn new(inner: fjall::Iter, reversed: bool, codec: &'a C) -> Self {
Self {
inner,
codec,
reversed,
failed: false,
_marker: PhantomData,
}
}
}
impl<K: Key, T, C: Codec<T>> TryLsmIter<'_, K, T, C> {
fn decode_guard(&self, g: fjall::Guard) -> DbResult<(K, T)> {
let (uk, uv) = map_fjall(g.into_inner())?;
if uk.len() != size_of::<K>() {
return Err(DbError::CorruptedEntry { offset: 0 });
}
let key = K::from_bytes(uk.as_ref());
let value = self.codec.decode_from(uv.as_ref())?;
Ok((key, value))
}
}
impl<K: Key, T, C: Codec<T>> Iterator for TryLsmIter<'_, K, T, C> {
type Item = DbResult<(K, T)>;
fn next(&mut self) -> Option<Self::Item> {
if self.failed {
return None;
}
let raw = if self.reversed {
self.inner.next_back()?
} else {
self.inner.next()?
};
let result = self.decode_guard(raw);
if result.is_err() {
self.failed = true;
}
Some(result)
}
}