diff --git a/src/db/lib.rs b/src/db/lib.rs index 0f23a9b4..4e400a1d 100644 --- a/src/db/lib.rs +++ b/src/db/lib.rs @@ -18,6 +18,7 @@ pub struct Transaction<'a>(pub(crate) &'a dyn ITx<'a>); pub struct Tree(pub(crate) Arc, pub(crate) usize); pub type Value<'a> = Cow<'a, [u8]>; +pub type ValueIter<'a> = Box, Value<'a>)>> + 'a>; // ---- @@ -89,6 +90,14 @@ impl Tree { pub fn put, U: AsRef<[u8]>>(&self, key: T, value: U) -> Result<()> { self.0.put(self.1, key.as_ref(), value.as_ref()) } + + pub fn iter<'a>(&'a self, reverse: bool) -> Result> { + self.0.range(self.1, None, reverse) + } + + pub fn range<'a, T: AsRef<[u8]>>(&'a self, start: T, reverse: bool) -> Result> { + self.0.range(self.1, Some(start.as_ref()), reverse) + } } impl<'a> Transaction<'a> { @@ -126,6 +135,12 @@ pub(crate) trait IDb: Send + Sync { fn get<'a>(&'a self, tree: usize, key: &[u8]) -> Result>>; fn put(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<()>; + fn range<'a>( + &'a self, + tree: usize, + start: Option<&[u8]>, + reverse: bool, + ) -> Result>; fn transaction(&self, f: &dyn ITxFn) -> TxResult<(), ()>; } diff --git a/src/db/sled_adapter.rs b/src/db/sled_adapter.rs index 617b4844..6e375f03 100644 --- a/src/db/sled_adapter.rs +++ b/src/db/sled_adapter.rs @@ -4,10 +4,11 @@ use std::sync::{Arc, RwLock}; use arc_swap::ArcSwapOption; use sled::transaction::{ - ConflictableTransactionError, TransactionError, Transactional, TransactionalTree, UnabortableTransactionError + ConflictableTransactionError, TransactionError, Transactional, TransactionalTree, + UnabortableTransactionError, }; -use crate::{Error, IDb, ITx, ITxFn, Result, TxError, TxFnResult, TxResult, Value, Db}; +use crate::{Db, Error, IDb, ITx, ITxFn, Result, TxError, TxFnResult, TxResult, Value, ValueIter}; impl From for Error { fn from(e: sled::Error) -> Error { @@ -58,12 +59,45 @@ impl IDb for SledDb { let tree = self.get_tree(tree)?; Ok(tree.get(key)?.map(|v| v.to_vec().into())) } + fn put(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<()> { let tree = self.get_tree(tree)?; tree.insert(key, value)?; Ok(()) } + fn range<'a>( + &'a self, + tree: usize, + start: Option<&[u8]>, + reverse: bool, + ) -> Result> { + let tree = self.get_tree(tree)?; + if reverse { + match start { + Some(start) => Ok(Box::new(tree.range(..=start).rev().map(|v| { + v.map(|(x, y)| (x.to_vec().into(), y.to_vec().into())) + .map_err(Into::into) + }))), + None => Ok(Box::new(tree.iter().rev().map(|v| { + v.map(|(x, y)| (x.to_vec().into(), y.to_vec().into())) + .map_err(Into::into) + }))), + } + } else { + match start { + Some(start) => Ok(Box::new(tree.range(start..).map(|v| { + v.map(|(x, y)| (x.to_vec().into(), y.to_vec().into())) + .map_err(Into::into) + }))), + None => Ok(Box::new(tree.iter().map(|v| { + v.map(|(x, y)| (x.to_vec().into(), y.to_vec().into())) + .map_err(Into::into) + }))), + } + } + } + fn transaction(&self, f: &dyn ITxFn) -> TxResult<(), ()> { let trees = self.trees.read().unwrap(); let res = trees.0.transaction(|txtrees| { @@ -76,7 +110,10 @@ impl IDb for SledDb { assert!(tx.err.into_inner().is_none()); Ok(()) } - TxFnResult::Abort => Err(ConflictableTransactionError::Abort(())), + TxFnResult::Abort => { + assert!(tx.err.into_inner().is_none()); + Err(ConflictableTransactionError::Abort(())) + } TxFnResult::Err => { let err_arc = tx .err @@ -117,14 +154,18 @@ impl<'a> SledTx<'a> { impl<'a> ITx<'a> for SledTx<'a> { fn get(&self, tree: usize, key: &[u8]) -> Result>> { - let tree = self.trees.get(tree) + let tree = self + .trees + .get(tree) .ok_or(Error("invalid tree id".into()))?; let tmp = self.save_error(tree.get(key))?; Ok(tmp.map(|v| v.to_vec().into())) } fn put(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<()> { - let tree = self.trees.get(tree) + let tree = self + .trees + .get(tree) .ok_or(Error("invalid tree id".into()))?; self.save_error(tree.insert(key, value))?; Ok(()) diff --git a/src/db/test.rs b/src/db/test.rs index f0e6c5de..7e389271 100644 --- a/src/db/test.rs +++ b/src/db/test.rs @@ -5,36 +5,63 @@ use crate::sled_adapter::SledDb; fn test_suite(db: Db) -> Result<()> { let tree = db.tree("tree")?; + let ka: &[u8] = &b"test"[..]; + let kb: &[u8] = &b"zwello"[..]; let va: &[u8] = &b"plop"[..]; let vb: &[u8] = &b"plip"[..]; let vc: &[u8] = &b"plup"[..]; - tree.put(b"test", va)?; - assert_eq!(tree.get(b"test")?, Some(va.into())); + tree.put(ka, va)?; + assert_eq!(tree.get(ka)?, Some(va.into())); let res = db.transaction::<_, (), _>(|tx| { - assert_eq!(tx.get(&tree, b"test")?, Some(va.into())); + assert_eq!(tx.get(&tree, ka)?, Some(va.into())); - tx.put(&tree, b"test", vb)?; + tx.put(&tree, ka, vb)?; - assert_eq!(tx.get(&tree, b"test")?, Some(vb.into())); + assert_eq!(tx.get(&tree, ka)?, Some(vb.into())); tx.commit(12) }); assert!(matches!(res, Ok(12))); - assert_eq!(tree.get(b"test")?, Some(vb.into())); + assert_eq!(tree.get(ka)?, Some(vb.into())); let res = db.transaction::<(), _, _>(|tx| { - assert_eq!(tx.get(&tree, b"test")?, Some(vb.into())); + assert_eq!(tx.get(&tree, ka)?, Some(vb.into())); - tx.put(&tree, b"test", vc)?; + tx.put(&tree, ka, vc)?; - assert_eq!(tx.get(&tree, b"test")?, Some(vc.into())); + assert_eq!(tx.get(&tree, ka)?, Some(vc.into())); tx.abort(42) }); assert!(matches!(res, Err(TxError::Abort(42)))); - assert_eq!(tree.get(b"test")?, Some(vb.into())); + assert_eq!(tree.get(ka)?, Some(vb.into())); + + let mut iter = tree.iter(false)?; + assert_eq!(iter.next().unwrap().unwrap(), (ka.into(), vb.into())); + assert!(iter.next().is_none()); + + tree.put(kb, vc)?; + assert_eq!(tree.get(kb)?, Some(vc.into())); + + let mut iter = tree.iter(false)?; + assert_eq!(iter.next().unwrap().unwrap(), (ka.into(), vb.into())); + assert_eq!(iter.next().unwrap().unwrap(), (kb.into(), vc.into())); + assert!(iter.next().is_none()); + + let mut iter = tree.range("tz", false)?; + assert_eq!(iter.next().unwrap().unwrap(), (kb.into(), vc.into())); + assert!(iter.next().is_none()); + + let mut iter = tree.range("tz", true)?; + assert_eq!(iter.next().unwrap().unwrap(), (ka.into(), vb.into())); + assert!(iter.next().is_none()); + + let mut iter = tree.iter(true)?; + assert_eq!(iter.next().unwrap().unwrap(), (kb.into(), vc.into())); + assert_eq!(iter.next().unwrap().unwrap(), (ka.into(), vb.into())); + assert!(iter.next().is_none()); Ok(()) }