diff options
| author | metamuffin <metamuffin@disroot.org> | 2025-12-24 09:02:17 +0100 |
|---|---|---|
| committer | metamuffin <metamuffin@disroot.org> | 2025-12-24 09:02:17 +0100 |
| commit | d543f6fe11a32dcead2310f1fb4c2abd303f5f8c (patch) | |
| tree | 3ba8274f0906250809ca5fa1b08c9b5f53b88cc7 | |
| parent | eac0de36221440571fe686074b04b71bf98cf727 (diff) | |
| download | jellything-d543f6fe11a32dcead2310f1fb4c2abd303f5f8c.tar jellything-d543f6fe11a32dcead2310f1fb4c2abd303f5f8c.tar.bz2 jellything-d543f6fe11a32dcead2310f1fb4c2abd303f5f8c.tar.zst | |
db abstraction looks good
| -rw-r--r-- | database/src/backends/memory.rs | 42 | ||||
| -rw-r--r-- | database/src/backends/mod.rs | 7 | ||||
| -rw-r--r-- | database/src/backends/redb.rs | 59 | ||||
| -rw-r--r-- | database/src/backends/rocksdb.rs | 30 | ||||
| -rw-r--r-- | database/src/indices/key.rs | 36 | ||||
| -rw-r--r-- | database/src/iterator.rs | 70 | ||||
| -rw-r--r-- | database/src/lib.rs | 2 | ||||
| -rw-r--r-- | database/src/prefix_iterator.rs | 22 |
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, + }) + } +} |