aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-12-24 09:02:17 +0100
committermetamuffin <metamuffin@disroot.org>2025-12-24 09:02:17 +0100
commitd543f6fe11a32dcead2310f1fb4c2abd303f5f8c (patch)
tree3ba8274f0906250809ca5fa1b08c9b5f53b88cc7
parenteac0de36221440571fe686074b04b71bf98cf727 (diff)
downloadjellything-d543f6fe11a32dcead2310f1fb4c2abd303f5f8c.tar
jellything-d543f6fe11a32dcead2310f1fb4c2abd303f5f8c.tar.bz2
jellything-d543f6fe11a32dcead2310f1fb4c2abd303f5f8c.tar.zst
db abstraction looks good
-rw-r--r--database/src/backends/memory.rs42
-rw-r--r--database/src/backends/mod.rs7
-rw-r--r--database/src/backends/redb.rs59
-rw-r--r--database/src/backends/rocksdb.rs30
-rw-r--r--database/src/indices/key.rs36
-rw-r--r--database/src/iterator.rs70
-rw-r--r--database/src/lib.rs2
-rw-r--r--database/src/prefix_iterator.rs22
8 files changed, 125 insertions, 143 deletions
diff --git a/database/src/backends/memory.rs b/database/src/backends/memory.rs
index 3c2fdea..302b232 100644
--- a/database/src/backends/memory.rs
+++ b/database/src/backends/memory.rs
@@ -41,33 +41,31 @@ impl ReadTransaction for RwLockWriteGuard<'_, Inner> {
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
Ok((**self).get(key).cloned())
}
- fn next(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
- Ok((**self)
- .range(key.to_vec()..)
- .next()
- .map(|(k, _)| k.to_owned()))
- }
- fn prev(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
- Ok((**self)
- .range(..key.to_vec())
- .next_back()
- .map(|(k, _)| k.to_owned()))
+ fn iter<'a>(
+ &'a self,
+ key: &[u8],
+ reverse: bool,
+ ) -> Result<Box<dyn Iterator<Item = Result<Vec<u8>>> + 'a>> {
+ Ok(if reverse {
+ Box::new(self.range(key.to_vec()..).map(|e| Ok(e.0.to_vec())))
+ } else {
+ Box::new(self.range(..=key.to_vec()).rev().map(|e| Ok(e.0.to_vec())))
+ })
}
}
impl ReadTransaction for RwLockReadGuard<'_, Inner> {
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
Ok((**self).get(key).cloned())
}
- fn next(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
- Ok((**self)
- .range(key.to_vec()..)
- .next()
- .map(|(k, _)| k.to_owned()))
- }
- fn prev(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
- Ok((**self)
- .range(..key.to_vec())
- .next_back()
- .map(|(k, _)| k.to_owned()))
+ fn iter<'a>(
+ &'a self,
+ key: &[u8],
+ reverse: bool,
+ ) -> Result<Box<dyn Iterator<Item = Result<Vec<u8>>> + 'a>> {
+ Ok(if reverse {
+ Box::new(self.range(key.to_vec()..).map(|e| Ok(e.0.to_vec())))
+ } else {
+ Box::new(self.range(..=key.to_vec()).rev().map(|e| Ok(e.0.to_vec())))
+ })
}
}
diff --git a/database/src/backends/mod.rs b/database/src/backends/mod.rs
index a95b00a..dd028f4 100644
--- a/database/src/backends/mod.rs
+++ b/database/src/backends/mod.rs
@@ -25,8 +25,11 @@ pub trait WriteTransaction: ReadTransaction {
}
pub trait ReadTransaction {
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>>;
- fn next(&self, key: &[u8]) -> Result<Option<Vec<u8>>>;
- fn prev(&self, key: &[u8]) -> Result<Option<Vec<u8>>>;
+ fn iter<'a>(
+ &'a self,
+ key: &[u8],
+ reverse: bool,
+ ) -> Result<Box<dyn Iterator<Item = Result<Vec<u8>>> + 'a>>;
}
pub fn create_backend(driver: &str, path: &Path) -> Result<Arc<dyn Db>> {
diff --git a/database/src/backends/redb.rs b/database/src/backends/redb.rs
index d2849de..409a551 100644
--- a/database/src/backends/redb.rs
+++ b/database/src/backends/redb.rs
@@ -6,7 +6,7 @@
use crate::backends::{Db, ReadTransaction, ReadTxnFunction, WriteTransaction, WriteTxnFunction};
use anyhow::Result;
-use redb::{Database, ReadableDatabase, ReadableTable, TableDefinition};
+use redb::{AccessGuard, Database, ReadableDatabase, ReadableTable, StorageError, TableDefinition};
use std::path::Path;
pub struct Redb {
@@ -52,21 +52,20 @@ impl ReadTransaction for redb::WriteTransaction {
None => Ok(None),
}
}
- fn next(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
- let table = self.open_table(TABLE)?;
- let mut iter = table.range(key..)?;
- match iter.next() {
- Some(k) => Ok(Some(k?.0.value().to_vec())),
- None => Ok(None),
- }
- }
- fn prev(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
- let table = self.open_table(TABLE)?;
- let mut iter = table.range(..key)?;
- match iter.next_back() {
- Some(k) => Ok(Some(k?.0.value().to_vec())),
- None => Ok(None),
- }
+ fn iter<'a>(
+ &'a self,
+ key: &[u8],
+ reverse: bool,
+ ) -> Result<Box<dyn Iterator<Item = Result<Vec<u8>>> + 'a>> {
+ // let k = |e: Result<(AccessGuard<'_, &[u8]>, AccessGuard<'_, &[u8]>), StorageError>| {
+ // e.map(|e| e.0.value().to_vec()).map_err(|e| e.into())
+ // };
+ // Ok(if reverse {
+ // Box::new(self.open_table(TABLE)?.range(..=key)?.rev().map(k))
+ // } else {
+ // Box::new(self.open_table(TABLE)?.range(key..)?.map(k))
+ // })
+ todo!()
}
}
impl ReadTransaction for redb::ReadTransaction {
@@ -76,20 +75,18 @@ impl ReadTransaction for redb::ReadTransaction {
None => Ok(None),
}
}
- fn next(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
- let table = self.open_table(TABLE)?;
- let mut iter = table.range(key..)?;
- match iter.next() {
- Some(k) => Ok(Some(k?.0.value().to_vec())),
- None => Ok(None),
- }
- }
- fn prev(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
- let table = self.open_table(TABLE)?;
- let mut iter = table.range(..key)?;
- match iter.next_back() {
- Some(k) => Ok(Some(k?.0.value().to_vec())),
- None => Ok(None),
- }
+ fn iter<'a>(
+ &'a self,
+ key: &[u8],
+ reverse: bool,
+ ) -> Result<Box<dyn Iterator<Item = Result<Vec<u8>>> + 'a>> {
+ let k = |e: Result<(AccessGuard<'_, &[u8]>, AccessGuard<'_, &[u8]>), StorageError>| {
+ e.map(|e| e.0.value().to_vec()).map_err(|e| e.into())
+ };
+ Ok(if reverse {
+ Box::new(self.open_table(TABLE)?.range(..=key)?.rev().map(k))
+ } else {
+ Box::new(self.open_table(TABLE)?.range(key..)?.map(k))
+ })
}
}
diff --git a/database/src/backends/rocksdb.rs b/database/src/backends/rocksdb.rs
index cdcb60a..92c6a6d 100644
--- a/database/src/backends/rocksdb.rs
+++ b/database/src/backends/rocksdb.rs
@@ -6,7 +6,7 @@
use crate::backends::{Db, ReadTransaction, WriteTransaction, WriteTxnFunction};
use anyhow::Result;
-use rocksdb::{ErrorKind, OptimisticTransactionDB};
+use rocksdb::{Direction, ErrorKind, IteratorMode, OptimisticTransactionDB};
use std::path::Path;
pub struct Rocksdb {
@@ -57,17 +57,23 @@ impl ReadTransaction for rocksdb::Transaction<'_, OptimisticTransactionDB> {
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
Ok(self.get(key)?)
}
- fn next(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
- let mut it = self.raw_iterator();
- it.seek_for_prev(key);
- it.next();
- Ok(it.key().map(Vec::from))
- }
- fn prev(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
- let mut it = self.raw_iterator();
- it.seek(key);
- it.prev();
- Ok(it.key().map(Vec::from))
+ fn iter<'a>(
+ &'a self,
+ key: &[u8],
+ reverse: bool,
+ ) -> Result<Box<dyn Iterator<Item = Result<Vec<u8>>> + 'a>> {
+ let mut iter = self.iterator(IteratorMode::Start);
+ iter.set_mode(IteratorMode::From(
+ key,
+ if reverse {
+ Direction::Reverse
+ } else {
+ Direction::Forward
+ },
+ ));
+ Ok(Box::new(iter.map(|e| {
+ e.map(|(k, _)| k.into_vec()).map_err(|e| e.into())
+ })))
}
}
diff --git a/database/src/indices/key.rs b/database/src/indices/key.rs
index ab56f76..9f9511b 100644
--- a/database/src/indices/key.rs
+++ b/database/src/indices/key.rs
@@ -5,8 +5,9 @@
*/
use crate::{
- backends::WriteTransaction,
+ backends::{ReadTransaction, WriteTransaction},
indices::Index,
+ prefix_iterator::PrefixIterator,
table::{RowNum, Table, TableNum},
};
use anyhow::Result;
@@ -21,22 +22,47 @@ impl<T: 'static> KeyIndex<T> {
table.indices.push(Box::new(Self { id, key }));
Self { id, key }
}
- pub fn key(&self, id: RowNum, val: &T) -> Vec<u8> {
+ pub fn key(&self, id: RowNum, key: &[u8]) -> Vec<u8> {
let mut v = Vec::new();
v.extend(self.id.to_be_bytes());
- v.extend((self.key)(val));
+ v.extend(key);
v.extend(id.to_be_bytes());
v
}
+ pub fn lookup<'a>(
+ &self,
+ db: &'a dyn ReadTransaction,
+ key: &[u8],
+ ) -> Result<PrefixIterator<'a>> {
+ let mut prefix = Vec::new();
+ prefix.extend(self.id.to_be_bytes());
+ prefix.extend(key);
+ Ok(PrefixIterator {
+ inner: db.iter(&prefix, false)?,
+ prefix: prefix.into(),
+ })
+ }
}
impl<T: 'static> Index<T> for KeyIndex<T> {
fn add(&self, db: &mut dyn WriteTransaction, id: RowNum, val: &T) -> Result<()> {
- db.set(&self.key(id, val), &[])
+ db.set(&self.key(id, (self.key)(val)), &[])
}
fn remove(&self, db: &mut dyn WriteTransaction, id: RowNum, val: &T) -> Result<()> {
- db.del(&self.key(id, val))
+ db.del(&self.key(id, (self.key)(val)))
}
fn compare(&self, before: &T, after: &T) -> bool {
(self.key)(before) == (self.key)(after)
}
}
+
+// fn inc_key(key: &[u8]) -> Vec<u8> {
+// let mut key = key.to_owned();
+// for i in (0..key.len()).rev() {
+// let (nv, carry) = key[i].overflowing_add(1);
+// key[i] = nv;
+// if !carry {
+// break;
+// }
+// }
+// key
+// }
diff --git a/database/src/iterator.rs b/database/src/iterator.rs
deleted file mode 100644
index 2db349b..0000000
--- a/database/src/iterator.rs
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- This file is part of jellything (https://codeberg.org/metamuffin/jellything)
- which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
- Copyright (C) 2025 metamuffin <metamuffin.org>
-*/
-
-use crate::backends::ReadTransaction;
-use anyhow::Result;
-use std::{borrow::Cow, ops::Range};
-
-pub struct PrefixIterator<'a> {
- db: &'a dyn ReadTransaction,
- prefix: &'a [u8],
- cursor: Cow<'a, [u8]>,
-}
-impl Iterator for PrefixIterator<'_> {
- type Item = Result<Vec<u8>>;
- fn next(&mut self) -> Option<Self::Item> {
- match self.db.next(&self.cursor) {
- Err(e) => Some(Err(e)),
- Ok(None) => None,
- Ok(Some(next)) => {
- if next.starts_with(self.prefix) {
- self.cursor = next.clone().into();
- Some(Ok(next))
- } else {
- None
- }
- }
- }
- }
-}
-
-pub struct RangeIterator<'a> {
- db: &'a dyn ReadTransaction,
- range: Range<Cow<'a, [u8]>>,
-}
-impl Iterator for RangeIterator<'_> {
- type Item = Result<Vec<u8>>;
- fn next(&mut self) -> Option<Self::Item> {
- match self.db.next(&self.range.start) {
- Err(e) => Some(Err(e)),
- Ok(None) => None,
- Ok(Some(next)) => {
- if next.as_slice() < self.range.end.as_ref() {
- self.range.start = next.clone().into();
- Some(Ok(next))
- } else {
- None
- }
- }
- }
- }
-}
-impl DoubleEndedIterator for RangeIterator<'_> {
- fn next_back(&mut self) -> Option<Self::Item> {
- match self.db.prev(&self.range.end) {
- Err(e) => Some(Err(e)),
- Ok(None) => None,
- Ok(Some(prev)) => {
- if prev.as_slice() >= self.range.start.as_ref() {
- self.range.end = prev.clone().into();
- Some(Ok(prev))
- } else {
- None
- }
- }
- }
- }
-}
diff --git a/database/src/lib.rs b/database/src/lib.rs
index 87baae1..e9e69bb 100644
--- a/database/src/lib.rs
+++ b/database/src/lib.rs
@@ -6,6 +6,6 @@
pub mod backends;
pub mod indices;
pub mod table;
-pub mod iterator;
+pub mod prefix_iterator;
pub type Pad32 = u32;
diff --git a/database/src/prefix_iterator.rs b/database/src/prefix_iterator.rs
new file mode 100644
index 0000000..3586ae2
--- /dev/null
+++ b/database/src/prefix_iterator.rs
@@ -0,0 +1,22 @@
+/*
+ This file is part of jellything (https://codeberg.org/metamuffin/jellything)
+ which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
+ Copyright (C) 2025 metamuffin <metamuffin.org>
+*/
+
+use anyhow::Result;
+use std::borrow::Cow;
+
+pub struct PrefixIterator<'a> {
+ pub inner: Box<dyn Iterator<Item = Result<Vec<u8>>> + 'a>,
+ pub prefix: Cow<'a, [u8]>,
+}
+impl Iterator for PrefixIterator<'_> {
+ type Item = Result<Vec<u8>>;
+ fn next(&mut self) -> Option<Self::Item> {
+ self.inner.next().filter(|k| match k {
+ Ok(v) => v.starts_with(&self.prefix),
+ Err(_) => true,
+ })
+ }
+}