فهرست منبع

Add API for reading without an extra copy

x 1 روز پیش
والد
کامیت
825e9ea770

+ 8 - 0
Cargo.toml

@@ -13,12 +13,20 @@ thiserror = "2.0.18"
 fjall = { version = "3.1", optional = true }
 sled = { version = "0.34", optional = true }
 tempfile = "3.20.0"
+allocation-counter = { version = "0.8.1", optional = true }
 
 [dev-dependencies]
 # To execute async tests
 smol = "2"
+criterion = { version = "0.5.1", features = ["html_reports"] }
+
+[[bench]]
+name = "value_reads"
+harness = false
 
 [features]
 default = ["fjall-backend"]
 fjall-backend = ["fjall"]
 sled-backend = ["sled"]
+# Only used by the allocation-only mode of benches/value_reads.rs.
+bench-allocations = ["dep:allocation-counter"]

+ 36 - 0
README.md

@@ -22,6 +22,42 @@ Currently supported databases:
 Usage examples are offered in the repository as test units, and docs
 can be found on [docs.rs/kvdb-overlay](https://docs.rs/kvdb-overlay).
 
+## Reading Without an Extra Copy
+
+`Tree::get`, `TreeOverlay::get`, and `DatabaseOverlay::get` return a
+`Value` handle without copying the bytes into a `Vec<u8>`:
+
+```rust
+if let Some(value) = overlay.get("my_tree", b"key")? {
+    let bytes: &[u8] = value.as_ref();
+    // Pass bytes to a slice deserializer, or use Cursor::new(value)
+    // with a deserializer that accepts std::io::Read.
+}
+```
+
+Cached values borrow the overlay; backend values retain an owned backend
+handle. Keep the value alive while borrowing its bytes. An overlay cannot
+be mutated while its returned value is still in use. `Tree::get`
+returns an owned handle that can outlive the tree.
+
+To obtain an independent buffer, consume the handle with `into_vec()`:
+
+```rust
+let owned: Option<Vec<u8>> = overlay.get("my_tree", b"key")?.map(|value| value.into_vec());
+```
+
+This explicitly copies the bytes, allowing the vector to outlive overlay
+mutation or destruction. `clone()` and `to_owned()` clone the handle, not
+the bytes; they do not detach cached values from the overlay. This is a
+breaking change from the previous vector-returning `get`; the separate
+`get_value` method has been removed. Other APIs such as `insert`, `remove`,
+and iterators still return owned vectors.
+
+Reading through the handle avoids an extra value-buffer allocation and copy. It does
+**not** stream from disk or avoid loading the entire value into memory;
+backend reads and deserialization may still allocate. Wrapping the value
+in a synchronous or async cursor does not change that.
+
 ## License
 
 GNU AGPLv3.

+ 219 - 0
benches/value_reads.rs

@@ -0,0 +1,219 @@
+use std::{
+    hint::black_box,
+    io::{Cursor, Read},
+};
+
+#[cfg(feature = "bench-allocations")]
+use std::fmt::Write as _;
+#[cfg(not(feature = "bench-allocations"))]
+use std::time::Duration;
+
+#[cfg(not(feature = "bench-allocations"))]
+use criterion::{BenchmarkId, SamplingMode, Throughput};
+use criterion::{Criterion, criterion_group, criterion_main};
+use kvdb_overlay::{Database, DatabaseOverlay, Value};
+
+#[cfg(feature = "fjall-backend")]
+const BACKEND: &str = "fjall";
+#[cfg(feature = "sled-backend")]
+const BACKEND: &str = "sled";
+
+const TREE: &str = "value_reads";
+const KEY: &[u8] = b"value";
+const SIZES: [(&str, usize); 7] = [
+    ("64B", 64),
+    ("4KiB", 4096),
+    ("1MiB", 1024 * 1024),
+    ("32MiB", 32 * 1024 * 1024),
+    ("128MiB", 128 * 1024 * 1024),
+    ("256MiB", 256 * 1024 * 1024),
+    ("512MiB", 512 * 1024 * 1024),
+];
+
+// Both decoders use the same generic Read implementation, with no decode heap allocation.
+fn decode(mut reader: impl Read, words: usize) -> u64 {
+    let mut checksum = 0u64;
+    let mut word = [0u8; 8];
+    for _ in 0..words {
+        reader.read_exact(&mut word).unwrap();
+        checksum = checksum.wrapping_add(u64::from_le_bytes(word));
+    }
+    checksum
+}
+
+type ReadValue = fn(&DatabaseOverlay, usize) -> u64;
+const READERS: [(&str, ReadValue); 4] = [
+    ("vec_cursor", |overlay, words| {
+        let value = overlay.get(TREE, KEY).unwrap().unwrap().into_vec();
+        decode(Cursor::new(value), words)
+    }),
+    ("vec_slice", |overlay, words| {
+        let value = overlay.get(TREE, KEY).unwrap().unwrap().into_vec();
+        decode(value.as_slice(), words)
+    }),
+    ("handle_slice", |overlay, words| {
+        let value = overlay.get(TREE, KEY).unwrap().unwrap();
+        decode(value.as_ref(), words)
+    }),
+    ("handle_cursor", |overlay, words| {
+        let value = overlay.get(TREE, KEY).unwrap().unwrap();
+        decode(Cursor::new(value), words)
+    }),
+];
+
+fn value_reads(_criterion: &mut Criterion) {
+    let selection = std::env::var("VALUE_READ_SIZE").unwrap_or_else(|_| "all".into());
+    assert!(
+        selection == "all"
+            || selection == "large"
+            || SIZES.iter().any(|(name, _)| *name == selection),
+        "VALUE_READ_SIZE must be all, large, 64B, 4KiB, 1MiB, 32MiB, 128MiB, 256MiB, or 512MiB"
+    );
+    #[cfg(feature = "bench-allocations")]
+    let mut report = format!(
+        "# Value Read Allocations ({BACKEND})\n\n\
+         Generated by `VALUE_READ_SIZE={selection} cargo bench --bench value_reads --no-default-features \
+         --features {BACKEND}-backend,bench-allocations`.\n\n\
+         Warm repeated get + decode + drop; foreground thread only. \
+         Totals are cumulative allocations, not live/peak memory or RSS. \
+         Backend worker allocations are excluded. No Criterion timings in this run.\n\n\
+         | backend | source | size_bytes | decode | approach | operations | allocations_total | bytes_total | allocations_per_op | bytes_per_op |\n\
+         | --- | --- | ---: | --- | --- | ---: | ---: | ---: | ---: | ---: |\n"
+    );
+
+    for source in ["backend_fallback", "overlay_cache"] {
+        for (size_name, size) in SIZES {
+            let large = size >= 32 * 1024 * 1024;
+            if selection != "all" && selection != size_name && !(selection == "large" && large) {
+                continue;
+            }
+            // Bound fixed validation/allocation work for large payloads.
+            let repeats = (100 * 1024 * 1024 / size).clamp(3, 100);
+            // Build deterministic, non-uniform words and independent expected checksums.
+            let mut payload = Vec::with_capacity(size);
+            let mut expected_full = 0u64;
+            let mut expected_header = 0u64;
+            for index in 0..size / 8 {
+                let word = (index as u64 + 1).wrapping_mul(0x9e3779b97f4a7c15);
+                if index == 0 {
+                    expected_header = word;
+                }
+                expected_full = expected_full.wrapping_add(word);
+                payload.extend_from_slice(&word.to_le_bytes());
+            }
+
+            // Keep the directory alive until all database/overlay handles are dropped.
+            let directory = tempfile::tempdir().unwrap();
+            let database = Database::open_default(directory.path()).unwrap();
+            let tree = database
+                .open_tree(
+                    TREE,
+                    #[cfg(feature = "fjall-backend")]
+                    || {
+                        let options = kvdb_overlay::fjall::KeyspaceCreateOptions::default();
+                        if large {
+                            // Keep the warm-handle experiment on the same storage path.
+                            options.max_memtable_size(1024 * 1024 * 1024)
+                        } else {
+                            options
+                        }
+                    },
+                )
+                .unwrap();
+            tree.insert(KEY, &payload).unwrap();
+            database.flush_default_mode().unwrap();
+            let mut overlay = DatabaseOverlay::new(&database, vec![]).unwrap();
+            overlay.open_tree_default(TREE, false).unwrap();
+            if source == "overlay_cache" {
+                overlay.insert(TREE, KEY, &payload).unwrap();
+            }
+            assert_eq!(
+                matches!(overlay.get(TREE, KEY).unwrap().unwrap(), Value::Cached(_)),
+                source == "overlay_cache"
+            );
+            drop(payload);
+
+            #[cfg(feature = "fjall-backend")]
+            let assert_resident = || {
+                assert_eq!(tree.tree().sealed_memtable_count(), 0);
+                assert_eq!(tree.tree().table_count(), 0);
+            };
+
+            #[cfg(not(feature = "bench-allocations"))]
+            let mut group =
+                _criterion.benchmark_group(format!("value_reads/{BACKEND}/{source}/{size_name}"));
+            #[cfg(not(feature = "bench-allocations"))]
+            {
+                group.sample_size(25);
+                group.measurement_time(Duration::from_secs(1));
+                group.warm_up_time(Duration::from_millis(300));
+                group.throughput(Throughput::Elements(1));
+                if large {
+                    group.sampling_mode(SamplingMode::Flat);
+                    group.measurement_time(Duration::from_secs(3));
+                }
+            }
+
+            for (decode_name, words, expected) in [
+                ("header_8B", 1, expected_header),
+                ("full_payload", size / 8, expected_full),
+            ] {
+                for (approach, read_value) in READERS {
+                    // Validate every approach and warm this exact path before measuring.
+                    for _ in 0..repeats {
+                        assert_eq!(read_value(black_box(&overlay), black_box(words)), expected);
+                    }
+                    #[cfg(feature = "fjall-backend")]
+                    assert_resident();
+
+                    #[cfg(feature = "bench-allocations")]
+                    {
+                        let allocations = allocation_counter::measure(|| {
+                            for _ in 0..repeats {
+                                black_box(read_value(black_box(&overlay), black_box(words)));
+                            }
+                        });
+                        writeln!(
+                            report,
+                            "| {BACKEND} | {source} | {size} | {decode_name} | {approach} | {repeats} | {} | {} | {:.2} | {:.2} |",
+                            allocations.count_total,
+                            allocations.bytes_total,
+                            allocations.count_total as f64 / repeats as f64,
+                            allocations.bytes_total as f64 / repeats as f64,
+                        ).unwrap();
+                    }
+
+                    #[cfg(not(feature = "bench-allocations"))]
+                    group.bench_function(BenchmarkId::new(decode_name, approach), |bencher| {
+                        bencher.iter(|| {
+                            // The owned Vec/handle is dropped inside read_value, before return.
+                            black_box(read_value(black_box(&overlay), black_box(words)))
+                        });
+                    });
+                    #[cfg(feature = "fjall-backend")]
+                    assert_resident();
+                }
+            }
+            #[cfg(not(feature = "bench-allocations"))]
+            group.finish();
+        }
+    }
+
+    #[cfg(feature = "bench-allocations")]
+    {
+        let directory = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("target/criterion");
+        std::fs::create_dir_all(&directory).unwrap();
+        let suffix = if selection == "all" {
+            String::new()
+        } else {
+            format!("-{selection}")
+        };
+        let path = directory.join(format!("value-allocations-{BACKEND}{suffix}.md"));
+        std::fs::write(&path, &report).unwrap();
+        print!("{report}");
+        eprintln!("Allocation report: {}", path.display());
+    }
+}
+
+criterion_group!(benches, value_reads);
+criterion_main!(benches);

+ 8 - 9
src/database_overlay.rs

@@ -23,7 +23,7 @@ use sled::{Transactional, transaction::ConflictableTransactionError};
 
 use crate::{
     Batch, Database, DatabaseOverlayState, DatabaseOverlayStateDiff, Error, Result, Tree,
-    TreeOverlay, TreeOverlayIter, TreeOverlayStateDiff,
+    TreeOverlay, TreeOverlayIter, TreeOverlayStateDiff, Value,
 };
 
 /// An overlay on top of an entire [`Database`] which can span multiple trees
@@ -180,16 +180,15 @@ impl DatabaseOverlay {
 
     /// Fetch the cache for a given tree.
     fn get_cache(&self, name: &str) -> Result<&TreeOverlay> {
-        let name = name.to_string();
-        if self.state.dropped_trees.contains_key(&name) {
-            return Err(Error::CollectionNotFound(name));
+        if self.state.dropped_trees.contains_key(name) {
+            return Err(Error::CollectionNotFound(name.to_string()));
         }
 
-        if let Some(v) = self.state.caches.get(&name) {
+        if let Some(v) = self.state.caches.get(name) {
             return Ok(v);
         }
 
-        Err(Error::CollectionNotFound(name))
+        Err(Error::CollectionNotFound(name.to_string()))
     }
 
     /// Fetch a mutable reference to the cache for a given tree.
@@ -224,9 +223,9 @@ impl DatabaseOverlay {
         cache.contains_key(key)
     }
 
-    /// Retrieve a value from the overlay if it exists in the specified
-    /// tree cache.
-    pub fn get(&self, name: &str, key: &[u8]) -> Result<Option<Vec<u8>>> {
+    /// Retrieve a value from the specified tree cache without copying it into
+    /// a new value buffer. See [`TreeOverlay::get`] for ownership details.
+    pub fn get(&self, name: &str, key: &[u8]) -> Result<Option<Value<'_>>> {
         let cache = self.get_cache(name)?;
         cache.get(key)
     }

+ 1 - 1
src/lib.rs

@@ -55,7 +55,7 @@ pub mod database_overlay_state;
 pub use database_overlay_state::{DatabaseOverlayState, DatabaseOverlayStateDiff};
 
 pub mod tree;
-pub use tree::{Tree, TreeIter};
+pub use tree::{Tree, TreeIter, Value};
 
 pub mod tree_overlay;
 pub use tree_overlay::{TreeOverlay, TreeOverlayIter};

+ 63 - 3
src/tree.rs

@@ -20,6 +20,64 @@ use std::{iter::FusedIterator, ops::RangeBounds};
 
 use crate::Result;
 
+/// A value borrowed from an overlay cache or owned through a backend handle.
+///
+/// Use [`AsRef::as_ref`] to borrow its bytes, or wrap it in [`std::io::Cursor`]
+/// for an owning reader. Keep the handle alive while borrowing its bytes.
+/// Cached values also borrow the overlay, preventing mutation while in use.
+/// This avoids an extra value-buffer copy, not backend allocations or loading
+/// the entire value into memory.
+/// Use [`Value::into_vec`] when an independent owned buffer is needed.
+///
+/// ```
+/// use std::io::{Cursor, Read};
+/// use kvdb_overlay::{Database, TreeOverlay};
+///
+/// # fn main() -> kvdb_overlay::Result<()> {
+/// let (db, _folder) = Database::open_temp()?;
+/// let tree = db.open_tree_default("values")?;
+/// let mut overlay = TreeOverlay::new(&tree);
+/// overlay.insert(b"key", &42_u64.to_le_bytes())?;
+/// if let Some(value) = overlay.get(b"key")? {
+///     let mut reader = Cursor::new(value);
+///     let mut bytes = [0; 8];
+///     reader.read_exact(&mut bytes)?;
+///     assert_eq!(u64::from_le_bytes(bytes), 42);
+/// }
+/// # Ok(())
+/// # }
+/// ```
+#[derive(Debug, Clone)]
+pub enum Value<'a> {
+    /// Bytes borrowed directly from the overlay cache.
+    Cached(&'a [u8]),
+    /// An owned backend value, without conversion to a `Vec<u8>`.
+    #[cfg(feature = "sled-backend")]
+    Backend(sled::IVec),
+    /// An owned backend value, without conversion to a `Vec<u8>`.
+    #[cfg(feature = "fjall-backend")]
+    Backend(fjall::UserValue),
+}
+
+impl Value<'_> {
+    /// Consume the handle and copy its bytes into an independently owned vector.
+    /// The returned vector no longer borrows the overlay or retains the backend handle.
+    #[inline]
+    pub fn into_vec(self) -> Vec<u8> {
+        self.as_ref().to_vec()
+    }
+}
+
+impl AsRef<[u8]> for Value<'_> {
+    #[inline]
+    fn as_ref(&self) -> &[u8] {
+        match self {
+            Self::Cached(value) => value,
+            Self::Backend(value) => value.as_ref(),
+        }
+    }
+}
+
 /// Struct representing the abstraction over the supported key-value
 /// embedded databases trees.
 #[derive(Clone)]
@@ -70,9 +128,11 @@ impl Tree {
         Ok(self.tree.contains_key(k)?)
     }
 
-    /// Retrieve a value from the Tree if it exists.    
-    pub fn get(&self, k: &[u8]) -> Result<Option<Vec<u8>>> {
-        Ok(self.tree.get(k)?.map(|v| v.to_vec()))
+    /// Retrieve a value without copying it into a new value buffer.
+    /// The returned handle owns its bytes and does not borrow the tree.
+    /// Backend reads may still allocate and retrieve the entire value.
+    pub fn get(&self, k: &[u8]) -> Result<Option<Value<'static>>> {
+        Ok(self.tree.get(k)?.map(Value::Backend))
     }
 
     /// Inserts a key-value pair into the tree and return its previous

+ 10 - 11
src/tree_overlay.rs

@@ -22,7 +22,7 @@ use std::{
     iter::{FusedIterator, Peekable},
 };
 
-use crate::{Batch, Error, Result, Tree, TreeOverlayState, TreeOverlayStateDiff};
+use crate::{Batch, Error, Result, Tree, TreeOverlayState, TreeOverlayStateDiff, Value};
 
 /// An overlay on top of a single [`Tree`] instance.
 #[derive(Debug, Clone)]
@@ -154,19 +154,18 @@ impl TreeOverlay {
         Ok(Some((cache_last.0.clone(), cache_last.1.clone())))
     }
 
-    /// Retrieve a value from the overlay if it exists.
-    pub fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
-        // First check if the key was removed in the overlay
+    /// Retrieve a value without copying it into a new value buffer.
+    /// Cached values borrow the overlay; other values own a backend handle.
+    /// Pending removals and writes take precedence over the underlying tree.
+    pub fn get(&self, key: &[u8]) -> Result<Option<Value<'_>>> {
         if self.state.removed.contains(key) {
             return Ok(None);
         }
 
-        // Then check the cache
-        if let Some(v) = self.state.cache.get(key) {
-            return Ok(Some(v.clone()));
+        if let Some(value) = self.state.cache.get(key) {
+            return Ok(Some(Value::Cached(value)));
         }
 
-        // And finally the main tree
         self.tree.get(key)
     }
 
@@ -189,7 +188,7 @@ impl TreeOverlay {
         // If cache didn't contain this key previously, and it wasn't
         // removed either, then check if it's in the main tree.
         if prev.is_none() {
-            prev = self.tree.get(key)?;
+            prev = self.tree.get(key)?.map(Value::into_vec);
         }
 
         Ok(prev)
@@ -206,7 +205,7 @@ impl TreeOverlay {
         // before, we have to get the previous value from the tree:
         let mut prev = self.state.cache.remove(key);
         if prev.is_none() {
-            prev = self.tree.get(key)?;
+            prev = self.tree.get(key)?.map(Value::into_vec);
         }
 
         // Previous value must existed
@@ -409,7 +408,7 @@ impl Iterator for TreeOverlayIter<'_> {
         // Grab the next key value from the overlay
         match self.overlay.get(&next_key) {
             Ok(next_value) => match next_value {
-                Some(next_value) => Some(Ok((next_key, next_value))),
+                Some(next_value) => Some(Ok((next_key, next_value.into_vec()))),
                 // If the value doesn't exist, it means it's in the
                 // removed set, so we advance the iterator.
                 None => self.next(),

+ 3 - 3
src/tree_overlay_state.rs

@@ -18,7 +18,7 @@
 
 use std::collections::{BTreeMap, BTreeSet};
 
-use crate::{Batch, Result, Tree};
+use crate::{Batch, Result, Tree, Value};
 
 /// Struct representing [`TreeOverlay`] cache state.
 #[derive(Debug, Default, Clone, PartialEq)]
@@ -140,7 +140,7 @@ impl TreeOverlayStateDiff {
         // Set inserted keys
         for (key, value) in state.cache.iter() {
             // Grab each key previous value, if it existed
-            let previous = tree.get(key)?;
+            let previous = tree.get(key)?.map(Value::into_vec);
             cache.insert(key.clone(), (previous, value.clone()));
         }
 
@@ -148,7 +148,7 @@ impl TreeOverlayStateDiff {
         for key in state.removed.iter() {
             // Grab each key last value, if it existed, otherwise
             // use an empty value as its previous.
-            let previous = tree.get(key)?.unwrap_or_default();
+            let previous = tree.get(key)?.map(Value::into_vec).unwrap_or_default();
             removed.insert(key.clone(), previous);
         }
 

+ 18 - 18
tests/database_overlay.rs

@@ -59,13 +59,13 @@ fn database_overlay() -> Result<()> {
     overlay.insert(TREE_2, b"key_f", b"val_f")?;
 
     // Verify they are in the overlay
-    assert_eq!(overlay.get(TREE_1, b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(overlay.get(TREE_1, b"key_b")?, Some(b"val_b".into()));
-    assert_eq!(overlay.get(TREE_1, b"key_c")?, Some(b"val_c".into()));
+    assert_eq!(overlay.get(TREE_1, b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(overlay.get(TREE_1, b"key_b")?.unwrap().as_ref(), b"val_b");
+    assert_eq!(overlay.get(TREE_1, b"key_c")?.unwrap().as_ref(), b"val_c");
 
-    assert_eq!(overlay.get(TREE_2, b"key_d")?, Some(b"val_d".into()));
-    assert_eq!(overlay.get(TREE_2, b"key_e")?, Some(b"val_e".into()));
-    assert_eq!(overlay.get(TREE_2, b"key_f")?, Some(b"val_f".into()));
+    assert_eq!(overlay.get(TREE_2, b"key_d")?.unwrap().as_ref(), b"val_d");
+    assert_eq!(overlay.get(TREE_2, b"key_e")?.unwrap().as_ref(), b"val_e");
+    assert_eq!(overlay.get(TREE_2, b"key_f")?.unwrap().as_ref(), b"val_f");
 
     // Check overlay trees are not empty
     assert!(!overlay.is_empty(TREE_1)?);
@@ -82,13 +82,13 @@ fn database_overlay() -> Result<()> {
     );
 
     // Verify they are not in the database
-    assert_eq!(tree_1.get(b"key_a")?, None);
-    assert_eq!(tree_1.get(b"key_b")?, None);
-    assert_eq!(tree_1.get(b"key_c")?, None);
+    assert!(tree_1.get(b"key_a")?.is_none());
+    assert!(tree_1.get(b"key_b")?.is_none());
+    assert!(tree_1.get(b"key_c")?.is_none());
 
-    assert_eq!(tree_2.get(b"key_d")?, None);
-    assert_eq!(tree_2.get(b"key_e")?, None);
-    assert_eq!(tree_2.get(b"key_f")?, None);
+    assert!(tree_2.get(b"key_d")?.is_none());
+    assert!(tree_2.get(b"key_e")?.is_none());
+    assert!(tree_2.get(b"key_f")?.is_none());
 
     // Now execute all tree batches in the overlay
     overlay.apply()?;
@@ -97,13 +97,13 @@ fn database_overlay() -> Result<()> {
     db.flush_default_mode()?;
 
     // Verify the database contains keys
-    assert_eq!(tree_1.get(b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(tree_1.get(b"key_b")?, Some(b"val_b".into()));
-    assert_eq!(tree_1.get(b"key_c")?, Some(b"val_c".into()));
+    assert_eq!(tree_1.get(b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(tree_1.get(b"key_b")?.unwrap().as_ref(), b"val_b");
+    assert_eq!(tree_1.get(b"key_c")?.unwrap().as_ref(), b"val_c");
 
-    assert_eq!(tree_2.get(b"key_d")?, Some(b"val_d".into()));
-    assert_eq!(tree_2.get(b"key_e")?, Some(b"val_e".into()));
-    assert_eq!(tree_2.get(b"key_f")?, Some(b"val_f".into()));
+    assert_eq!(tree_2.get(b"key_d")?.unwrap().as_ref(), b"val_d");
+    assert_eq!(tree_2.get(b"key_e")?.unwrap().as_ref(), b"val_e");
+    assert_eq!(tree_2.get(b"key_f")?.unwrap().as_ref(), b"val_f");
 
     Ok(())
 }

+ 18 - 18
tests/database_overlay_checkpoint.rs

@@ -45,14 +45,14 @@ fn database_overlay_checkpoint() -> Result<()> {
     overlay.insert(TREE, b"key_c", b"val_c")?;
 
     // Verify they are in the overlay
-    assert_eq!(overlay.get(TREE, b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(overlay.get(TREE, b"key_b")?, Some(b"val_b".into()));
-    assert_eq!(overlay.get(TREE, b"key_c")?, Some(b"val_c".into()));
+    assert_eq!(overlay.get(TREE, b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(overlay.get(TREE, b"key_b")?.unwrap().as_ref(), b"val_b");
+    assert_eq!(overlay.get(TREE, b"key_c")?.unwrap().as_ref(), b"val_c");
 
     // Verify they are not in the database
-    assert_eq!(tree.get(b"key_a")?, None);
-    assert_eq!(tree.get(b"key_b")?, None);
-    assert_eq!(tree.get(b"key_c")?, None);
+    assert!(tree.get(b"key_a")?.is_none());
+    assert!(tree.get(b"key_b")?.is_none());
+    assert!(tree.get(b"key_c")?.is_none());
 
     // Now we create an overlay checkpoint
     overlay.checkpoint();
@@ -63,14 +63,14 @@ fn database_overlay_checkpoint() -> Result<()> {
     overlay.insert(TREE, b"key_f", b"val_f")?;
 
     // Verify they are in the overlay
-    assert_eq!(overlay.get(TREE, b"key_d")?, Some(b"val_d".into()));
-    assert_eq!(overlay.get(TREE, b"key_e")?, Some(b"val_e".into()));
-    assert_eq!(overlay.get(TREE, b"key_f")?, Some(b"val_f".into()));
+    assert_eq!(overlay.get(TREE, b"key_d")?.unwrap().as_ref(), b"val_d");
+    assert_eq!(overlay.get(TREE, b"key_e")?.unwrap().as_ref(), b"val_e");
+    assert_eq!(overlay.get(TREE, b"key_f")?.unwrap().as_ref(), b"val_f");
 
     // Verify they are not in the database
-    assert_eq!(tree.get(b"key_d")?, None);
-    assert_eq!(tree.get(b"key_e")?, None);
-    assert_eq!(tree.get(b"key_f")?, None);
+    assert!(tree.get(b"key_d")?.is_none());
+    assert!(tree.get(b"key_e")?.is_none());
+    assert!(tree.get(b"key_f")?.is_none());
 
     // We also create a new tree
     overlay.open_tree_default(NEW_TREE, false)?;
@@ -88,14 +88,14 @@ fn database_overlay_checkpoint() -> Result<()> {
     db.flush_default_mode()?;
 
     // Verify the database contains pre-checkpoint keys
-    assert_eq!(tree.get(b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(tree.get(b"key_b")?, Some(b"val_b".into()));
-    assert_eq!(tree.get(b"key_c")?, Some(b"val_c".into()));
+    assert_eq!(tree.get(b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(tree.get(b"key_b")?.unwrap().as_ref(), b"val_b");
+    assert_eq!(tree.get(b"key_c")?.unwrap().as_ref(), b"val_c");
 
     // Verify the database doesn't contains keys after checkpoint
-    assert_eq!(tree.get(b"key_d")?, None);
-    assert_eq!(tree.get(b"key_e")?, None);
-    assert_eq!(tree.get(b"key_f")?, None);
+    assert!(tree.get(b"key_d")?.is_none());
+    assert!(tree.get(b"key_e")?.is_none());
+    assert!(tree.get(b"key_f")?.is_none());
 
     // Verify the database doesn't contain the new tree we created
     // after checkpoint

+ 39 - 21
tests/database_overlay_clone.rs

@@ -50,9 +50,9 @@ fn database_overlay_clone() -> Result<()> {
     overlay.insert(TREE, b"key_c", b"val_c")?;
 
     // Verify they are in the overlay
-    assert_eq!(overlay.get(TREE, b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(overlay.get(TREE, b"key_b")?, Some(b"val_b".into()));
-    assert_eq!(overlay.get(TREE, b"key_c")?, Some(b"val_c".into()));
+    assert_eq!(overlay.get(TREE, b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(overlay.get(TREE, b"key_b")?.unwrap().as_ref(), b"val_b");
+    assert_eq!(overlay.get(TREE, b"key_c")?.unwrap().as_ref(), b"val_c");
 
     // Check overlay tree is not empty
     assert!(!overlay.is_empty(TREE)?);
@@ -64,9 +64,9 @@ fn database_overlay_clone() -> Result<()> {
     );
 
     // Verify they are not in the database
-    assert_eq!(tree.get(b"key_a")?, None);
-    assert_eq!(tree.get(b"key_b")?, None);
-    assert_eq!(tree.get(b"key_c")?, None);
+    assert!(tree.get(b"key_a")?.is_none());
+    assert!(tree.get(b"key_b")?.is_none());
+    assert!(tree.get(b"key_c")?.is_none());
 
     // Clone the overlay
     let mut overlay_clone = overlay.clone();
@@ -86,12 +86,30 @@ fn database_overlay_clone() -> Result<()> {
     overlay_clone.insert(TREE, b"key_f", b"val_f")?;
 
     // Verify all records are in the cloned overlay
-    assert_eq!(overlay_clone.get(TREE, b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(overlay_clone.get(TREE, b"key_b")?, Some(b"val_b".into()));
-    assert_eq!(overlay_clone.get(TREE, b"key_c")?, Some(b"val_c".into()));
-    assert_eq!(overlay_clone.get(TREE, b"key_d")?, Some(b"val_d".into()));
-    assert_eq!(overlay_clone.get(TREE, b"key_e")?, Some(b"val_e".into()));
-    assert_eq!(overlay_clone.get(TREE, b"key_f")?, Some(b"val_f".into()));
+    assert_eq!(
+        overlay_clone.get(TREE, b"key_a")?.unwrap().as_ref(),
+        b"val_a"
+    );
+    assert_eq!(
+        overlay_clone.get(TREE, b"key_b")?.unwrap().as_ref(),
+        b"val_b"
+    );
+    assert_eq!(
+        overlay_clone.get(TREE, b"key_c")?.unwrap().as_ref(),
+        b"val_c"
+    );
+    assert_eq!(
+        overlay_clone.get(TREE, b"key_d")?.unwrap().as_ref(),
+        b"val_d"
+    );
+    assert_eq!(
+        overlay_clone.get(TREE, b"key_e")?.unwrap().as_ref(),
+        b"val_e"
+    );
+    assert_eq!(
+        overlay_clone.get(TREE, b"key_f")?.unwrap().as_ref(),
+        b"val_f"
+    );
 
     // Check its last values
     assert_eq!(
@@ -100,12 +118,12 @@ fn database_overlay_clone() -> Result<()> {
     );
 
     // Verify they are not in original overlay or the database
-    assert_eq!(tree.get(b"key_d")?, None);
-    assert_eq!(tree.get(b"key_e")?, None);
-    assert_eq!(tree.get(b"key_f")?, None);
-    assert_eq!(overlay.get(TREE, b"key_d")?, None);
-    assert_eq!(overlay.get(TREE, b"key_e")?, None);
-    assert_eq!(overlay.get(TREE, b"key_f")?, None);
+    assert!(tree.get(b"key_d")?.is_none());
+    assert!(tree.get(b"key_e")?.is_none());
+    assert!(tree.get(b"key_f")?.is_none());
+    assert!(overlay.get(TREE, b"key_d")?.is_none());
+    assert!(overlay.get(TREE, b"key_e")?.is_none());
+    assert!(overlay.get(TREE, b"key_f")?.is_none());
 
     // We finished processing the cloned overlay, so we can
     // discard it and apply all tree baches of the original
@@ -116,9 +134,9 @@ fn database_overlay_clone() -> Result<()> {
     db.flush_default_mode()?;
 
     // Verify the database contains keys
-    assert_eq!(tree.get(b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(tree.get(b"key_b")?, Some(b"val_b".into()));
-    assert_eq!(tree.get(b"key_c")?, Some(b"val_c".into()));
+    assert_eq!(tree.get(b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(tree.get(b"key_b")?.unwrap().as_ref(), b"val_b");
+    assert_eq!(tree.get(b"key_c")?.unwrap().as_ref(), b"val_c");
 
     Ok(())
 }

+ 3 - 3
tests/database_overlay_remove_tree.rs

@@ -46,9 +46,9 @@ fn database_overlay_remove_tree() -> Result<()> {
     overlay.open_tree_default(TREE_1, false)?;
 
     // Verify values are in the overlay
-    assert_eq!(overlay.get(TREE_1, b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(overlay.get(TREE_1, b"key_b")?, Some(b"val_b".into()));
-    assert_eq!(overlay.get(TREE_1, b"key_c")?, Some(b"val_c".into()));
+    assert_eq!(overlay.get(TREE_1, b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(overlay.get(TREE_1, b"key_b")?.unwrap().as_ref(), b"val_b");
+    assert_eq!(overlay.get(TREE_1, b"key_c")?.unwrap().as_ref(), b"val_c");
 
     // Drop tree
     overlay.drop_tree(TREE_1)?;

+ 82 - 82
tests/database_overlay_state.rs

@@ -309,17 +309,17 @@ fn database_overlay_state() -> Result<()> {
 
     let tree_1 = db.open_tree_default(TREE_1)?;
     assert_eq!(tree_1.len()?, 2);
-    assert_eq!(tree_1.get(b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(tree_1.get(b"key_b")?, Some(b"val_b".into()));
+    assert_eq!(tree_1.get(b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(tree_1.get(b"key_b")?.unwrap().as_ref(), b"val_b");
     let tree_2 = db.open_tree_default(TREE_2)?;
     assert!(tree_2.is_empty()?);
     let tree_3 = db.open_tree_default(TREE_3)?;
     assert_eq!(tree_3.len()?, 1);
-    assert_eq!(tree_3.get(b"key_i")?, Some(b"val_i".into()));
+    assert_eq!(tree_3.get(b"key_i")?.unwrap().as_ref(), b"val_i");
     let tree_4 = db.open_tree_default(TREE_4)?;
     assert_eq!(tree_4.len()?, 2);
-    assert_eq!(tree_4.get(b"key_j")?, Some(b"val_j".into()));
-    assert_eq!(tree_4.get(b"key_k")?, Some(b"val_k".into()));
+    assert_eq!(tree_4.get(b"key_j")?.unwrap().as_ref(), b"val_j");
+    assert_eq!(tree_4.get(b"key_k")?.unwrap().as_ref(), b"val_k");
     let tree_5 = db.open_tree_default(TREE_5)?;
     assert!(tree_5.is_empty()?);
     let tree_6 = db.open_tree_default(TREE_6)?;
@@ -404,18 +404,18 @@ fn database_overlay_state() -> Result<()> {
 
     let tree_1 = db.open_tree_default(TREE_1)?;
     assert_eq!(tree_1.len()?, 1);
-    assert_eq!(tree_1.get(b"key_b")?, Some(b"val_bb".into()));
+    assert_eq!(tree_1.get(b"key_b")?.unwrap().as_ref(), b"val_bb");
     let tree_2 = db.open_tree_default(TREE_2)?;
     assert_eq!(tree_2.len()?, 2);
-    assert_eq!(tree_2.get(b"key_d")?, Some(b"val_d".into()));
-    assert_eq!(tree_2.get(b"key_e")?, Some(b"val_e".into()));
+    assert_eq!(tree_2.get(b"key_d")?.unwrap().as_ref(), b"val_d");
+    assert_eq!(tree_2.get(b"key_e")?.unwrap().as_ref(), b"val_e");
     let tree_4 = db.open_tree_default(TREE_4)?;
     assert_eq!(tree_4.len()?, 2);
-    assert_eq!(tree_4.get(b"key_j")?, Some(b"val_j".into()));
-    assert_eq!(tree_4.get(b"key_k")?, Some(b"val_kk".into()));
+    assert_eq!(tree_4.get(b"key_j")?.unwrap().as_ref(), b"val_j");
+    assert_eq!(tree_4.get(b"key_k")?.unwrap().as_ref(), b"val_kk");
     let tree_5 = db.open_tree_default(TREE_5)?;
     assert_eq!(tree_5.len()?, 1);
-    assert_eq!(tree_5.get(b"key_h")?, Some(b"val_h".into()));
+    assert_eq!(tree_5.get(b"key_h")?.unwrap().as_ref(), b"val_h");
     let tree_6 = db.open_tree_default(TREE_6)?;
     assert!(tree_6.is_empty()?);
 
@@ -480,15 +480,15 @@ fn database_overlay_state() -> Result<()> {
 
     let tree_1 = db.open_tree_default(TREE_1)?;
     assert_eq!(tree_1.len()?, 2);
-    assert_eq!(tree_1.get(b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(tree_1.get(b"key_c")?, Some(b"val_c".into()));
+    assert_eq!(tree_1.get(b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(tree_1.get(b"key_c")?.unwrap().as_ref(), b"val_c");
     let tree_2 = db.open_tree_default(TREE_2)?;
     assert_eq!(tree_2.len()?, 2);
-    assert_eq!(tree_2.get(b"key_d")?, Some(b"val_d".into()));
-    assert_eq!(tree_2.get(b"key_f")?, Some(b"val_f".into()));
+    assert_eq!(tree_2.get(b"key_d")?.unwrap().as_ref(), b"val_d");
+    assert_eq!(tree_2.get(b"key_f")?.unwrap().as_ref(), b"val_f");
     let tree_5 = db.open_tree_default(TREE_5)?;
     assert_eq!(tree_5.len()?, 1);
-    assert_eq!(tree_5.get(b"key_h")?, Some(b"val_h".into()));
+    assert_eq!(tree_5.get(b"key_h")?.unwrap().as_ref(), b"val_h");
     let tree_6 = db.open_tree_default(TREE_6)?;
     assert!(tree_6.is_empty()?);
 
@@ -530,11 +530,11 @@ fn database_overlay_state() -> Result<()> {
 
     let tree_1 = db.open_tree_default(TREE_1)?;
     assert_eq!(tree_1.len()?, 2);
-    assert_eq!(tree_1.get(b"key_a")?, Some(b"val_aa".into()));
-    assert_eq!(tree_1.get(b"key_b")?, Some(b"val_b".into()));
+    assert_eq!(tree_1.get(b"key_a")?.unwrap().as_ref(), b"val_aa");
+    assert_eq!(tree_1.get(b"key_b")?.unwrap().as_ref(), b"val_b");
     let tree_2 = db.open_tree_default(TREE_2)?;
     assert_eq!(tree_2.len()?, 1);
-    assert_eq!(tree_2.get(b"key_f")?, Some(b"val_ff".into()));
+    assert_eq!(tree_2.get(b"key_f")?.unwrap().as_ref(), b"val_ff");
     let tree_6 = db.open_tree_default(TREE_6)?;
     assert!(tree_6.is_empty()?);
 
@@ -551,17 +551,17 @@ fn database_overlay_state() -> Result<()> {
 
     let tree_1 = db.open_tree_default(TREE_1)?;
     assert_eq!(tree_1.len()?, 2);
-    assert_eq!(tree_1.get(b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(tree_1.get(b"key_c")?, Some(b"val_c".into()));
+    assert_eq!(tree_1.get(b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(tree_1.get(b"key_c")?.unwrap().as_ref(), b"val_c");
     let tree_2 = db.open_tree_default(TREE_2)?;
     assert_eq!(tree_2.len()?, 2);
-    assert_eq!(tree_2.get(b"key_d")?, Some(b"val_d".into()));
-    assert_eq!(tree_2.get(b"key_f")?, Some(b"val_f".into()));
+    assert_eq!(tree_2.get(b"key_d")?.unwrap().as_ref(), b"val_d");
+    assert_eq!(tree_2.get(b"key_f")?.unwrap().as_ref(), b"val_f");
     let tree_6 = db.open_tree_default(TREE_6)?;
     assert!(tree_6.is_empty()?);
     let tree_5 = db.open_tree_default(TREE_5)?;
     assert_eq!(tree_5.len()?, 1);
-    assert_eq!(tree_5.get(b"key_h")?, Some(b"val_h".into()));
+    assert_eq!(tree_5.get(b"key_h")?.unwrap().as_ref(), b"val_h");
 
     // Now we are going to revert the diffs sequence going backwards
     // and verify the database state mutates accordingly
@@ -578,20 +578,20 @@ fn database_overlay_state() -> Result<()> {
 
     let tree_1 = db.open_tree_default(TREE_1)?;
     assert_eq!(tree_1.len()?, 1);
-    assert_eq!(tree_1.get(b"key_b")?, Some(b"val_bb".into()));
+    assert_eq!(tree_1.get(b"key_b")?.unwrap().as_ref(), b"val_bb");
     let tree_2 = db.open_tree_default(TREE_2)?;
     assert_eq!(tree_2.len()?, 2);
-    assert_eq!(tree_2.get(b"key_d")?, Some(b"val_d".into()));
-    assert_eq!(tree_2.get(b"key_e")?, Some(b"val_e".into()));
+    assert_eq!(tree_2.get(b"key_d")?.unwrap().as_ref(), b"val_d");
+    assert_eq!(tree_2.get(b"key_e")?.unwrap().as_ref(), b"val_e");
     let tree_6 = db.open_tree_default(TREE_6)?;
     assert!(tree_6.is_empty()?);
     let tree_4 = db.open_tree_default(TREE_4)?;
     assert_eq!(tree_4.len()?, 2);
-    assert_eq!(tree_4.get(b"key_j")?, Some(b"val_j".into()));
-    assert_eq!(tree_4.get(b"key_k")?, Some(b"val_kk".into()));
+    assert_eq!(tree_4.get(b"key_j")?.unwrap().as_ref(), b"val_j");
+    assert_eq!(tree_4.get(b"key_k")?.unwrap().as_ref(), b"val_kk");
     let tree_5 = db.open_tree_default(TREE_5)?;
     assert_eq!(tree_5.len()?, 1);
-    assert_eq!(tree_5.get(b"key_h")?, Some(b"val_h".into()));
+    assert_eq!(tree_5.get(b"key_h")?.unwrap().as_ref(), b"val_h");
 
     overlay.apply_diff(&sequence[1].inverse())?;
     db.flush_default_mode()?;
@@ -605,17 +605,17 @@ fn database_overlay_state() -> Result<()> {
 
     let tree_1 = db.open_tree_default(TREE_1)?;
     assert_eq!(tree_1.len()?, 2);
-    assert_eq!(tree_1.get(b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(tree_1.get(b"key_b")?, Some(b"val_b".into()));
+    assert_eq!(tree_1.get(b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(tree_1.get(b"key_b")?.unwrap().as_ref(), b"val_b");
     let tree_6 = db.open_tree_default(TREE_6)?;
     assert!(tree_6.is_empty()?);
     let tree_3 = db.open_tree_default(TREE_3)?;
     assert_eq!(tree_3.len()?, 1);
-    assert_eq!(tree_3.get(b"key_i")?, Some(b"val_i".into()));
+    assert_eq!(tree_3.get(b"key_i")?.unwrap().as_ref(), b"val_i");
     let tree_4 = db.open_tree_default(TREE_4)?;
     assert_eq!(tree_4.len()?, 2);
-    assert_eq!(tree_4.get(b"key_j")?, Some(b"val_j".into()));
-    assert_eq!(tree_4.get(b"key_k")?, Some(b"val_k".into()));
+    assert_eq!(tree_4.get(b"key_j")?.unwrap().as_ref(), b"val_j");
+    assert_eq!(tree_4.get(b"key_k")?.unwrap().as_ref(), b"val_k");
 
     overlay.apply_diff(&sequence[0].inverse())?;
     db.flush_default_mode()?;
@@ -641,11 +641,11 @@ fn database_overlay_state() -> Result<()> {
 
     let tree_1 = db.open_tree_default(TREE_1)?;
     assert_eq!(tree_1.len()?, 1);
-    assert_eq!(tree_1.get(b"key_a")?, Some(b"val_a".into()));
+    assert_eq!(tree_1.get(b"key_a")?.unwrap().as_ref(), b"val_a");
     let tree_4 = db.open_tree_default(TREE_4)?;
     assert_eq!(tree_4.len()?, 2);
-    assert_eq!(tree_4.get(b"key_g")?, Some(b"val_g".into()));
-    assert_eq!(tree_4.get(b"key_j")?, Some(b"val_j".into()));
+    assert_eq!(tree_4.get(b"key_g")?.unwrap().as_ref(), b"val_g");
+    assert_eq!(tree_4.get(b"key_j")?.unwrap().as_ref(), b"val_j");
 
     Ok(())
 }
@@ -848,16 +848,16 @@ fn database_overlay_rebuild_state() -> Result<()> {
 
     let tree_1 = db.open_tree_default(TREE_1)?;
     assert_eq!(tree_1.len()?, 2);
-    assert_eq!(tree_1.get(b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(tree_1.get(b"key_b")?, Some(b"val_b".into()));
+    assert_eq!(tree_1.get(b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(tree_1.get(b"key_b")?.unwrap().as_ref(), b"val_b");
     let tree_2 = db.open_tree_default(TREE_2)?;
     assert!(tree_2.is_empty()?);
     let tree_3 = db.open_tree_default(TREE_3)?;
     assert_eq!(tree_3.len()?, 1);
-    assert_eq!(tree_3.get(b"key_i")?, Some(b"val_i".into()));
+    assert_eq!(tree_3.get(b"key_i")?.unwrap().as_ref(), b"val_i");
     let tree_4 = db.open_tree_default(TREE_4)?;
     assert_eq!(tree_4.len()?, 1);
-    assert_eq!(tree_4.get(b"key_g")?, Some(b"val_g".into()));
+    assert_eq!(tree_4.get(b"key_g")?.unwrap().as_ref(), b"val_g");
     let tree_5 = db.open_tree_default(TREE_5)?;
     assert!(tree_5.is_empty()?);
     let tree_6 = db.open_tree_default(TREE_6)?;
@@ -988,14 +988,14 @@ fn database_overlay_rebuild_state() -> Result<()> {
 
     let tree_1 = db.open_tree_default(TREE_1)?;
     assert_eq!(tree_1.len()?, 1);
-    assert_eq!(tree_1.get(b"key_b")?, Some(b"val_bb".into()));
+    assert_eq!(tree_1.get(b"key_b")?.unwrap().as_ref(), b"val_bb");
     let tree_2 = db.open_tree_default(TREE_2)?;
     assert_eq!(tree_2.len()?, 2);
-    assert_eq!(tree_2.get(b"key_d")?, Some(b"val_d".into()));
-    assert_eq!(tree_2.get(b"key_e")?, Some(b"val_e".into()));
+    assert_eq!(tree_2.get(b"key_d")?.unwrap().as_ref(), b"val_d");
+    assert_eq!(tree_2.get(b"key_e")?.unwrap().as_ref(), b"val_e");
     let tree_4 = db.open_tree_default(TREE_4)?;
     assert_eq!(tree_4.len()?, 1);
-    assert_eq!(tree_4.get(b"key_g")?, Some(b"val_g".into()));
+    assert_eq!(tree_4.get(b"key_g")?.unwrap().as_ref(), b"val_g");
 
     assert_eq!(overlay.state.initial_tree_names.len(), 3);
     assert!(overlay.state.initial_tree_names.contains(&TREE_1.into()));
@@ -1095,15 +1095,15 @@ fn database_overlay_rebuild_state() -> Result<()> {
 
     let tree_1 = db.open_tree_default(TREE_1)?;
     assert_eq!(tree_1.len()?, 2);
-    assert_eq!(tree_1.get(b"key_b")?, Some(b"val_bb".into()));
-    assert_eq!(tree_1.get(b"key_c")?, Some(b"val_cc".into()));
+    assert_eq!(tree_1.get(b"key_b")?.unwrap().as_ref(), b"val_bb");
+    assert_eq!(tree_1.get(b"key_c")?.unwrap().as_ref(), b"val_cc");
     let tree_2 = db.open_tree_default(TREE_2)?;
     assert_eq!(tree_2.len()?, 1);
-    assert_eq!(tree_2.get(b"key_d")?, Some(b"val_d".into()));
+    assert_eq!(tree_2.get(b"key_d")?.unwrap().as_ref(), b"val_d");
     let tree_4 = db.open_tree_default(TREE_4)?;
     assert_eq!(tree_4.len()?, 2);
-    assert_eq!(tree_4.get(b"key_g")?, Some(b"val_g".into()));
-    assert_eq!(tree_4.get(b"key_f")?, Some(b"val_f".into()));
+    assert_eq!(tree_4.get(b"key_g")?.unwrap().as_ref(), b"val_g");
+    assert_eq!(tree_4.get(b"key_f")?.unwrap().as_ref(), b"val_f");
 
     // Since we removed everything, current overlay must not have
     // diffs over the tree, therefore its safe to keep using it
@@ -1278,10 +1278,10 @@ fn database_overlay_rebuild_state() -> Result<()> {
 
     let tree_1 = db.open_tree_default(TREE_1)?;
     assert_eq!(tree_1.len()?, 1);
-    assert_eq!(tree_1.get(b"key_a")?, Some(b"val_a".into()));
+    assert_eq!(tree_1.get(b"key_a")?.unwrap().as_ref(), b"val_a");
     let tree_4 = db.open_tree_default(TREE_4)?;
     assert_eq!(tree_4.len()?, 1);
-    assert_eq!(tree_4.get(b"key_g")?, Some(b"val_g".into()));
+    assert_eq!(tree_4.get(b"key_g")?.unwrap().as_ref(), b"val_g");
 
     Ok(())
 }
@@ -1472,16 +1472,16 @@ fn database_overlay_protected_trees() -> Result<()> {
 
     let tree_1 = db.open_tree_default(TREE_1)?;
     assert_eq!(tree_1.len()?, 2);
-    assert_eq!(tree_1.get(b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(tree_1.get(b"key_b")?, Some(b"val_b".into()));
+    assert_eq!(tree_1.get(b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(tree_1.get(b"key_b")?.unwrap().as_ref(), b"val_b");
     let tree_2 = db.open_tree_default(TREE_2)?;
     assert!(tree_2.is_empty()?);
     let tree_3 = db.open_tree_default(TREE_3)?;
     assert_eq!(tree_3.len()?, 1);
-    assert_eq!(tree_3.get(b"key_i")?, Some(b"val_i".into()));
+    assert_eq!(tree_3.get(b"key_i")?.unwrap().as_ref(), b"val_i");
     let tree_4 = db.open_tree_default(TREE_4)?;
     assert_eq!(tree_4.len()?, 1);
-    assert_eq!(tree_4.get(b"key_g")?, Some(b"val_g".into()));
+    assert_eq!(tree_4.get(b"key_g")?.unwrap().as_ref(), b"val_g");
     let tree_5 = db.open_tree_default(TREE_5)?;
     assert!(tree_5.is_empty()?);
 
@@ -1543,18 +1543,18 @@ fn database_overlay_protected_trees() -> Result<()> {
 
     let tree_1 = db.open_tree_default(TREE_1)?;
     assert_eq!(tree_1.len()?, 2);
-    assert_eq!(tree_1.get(b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(tree_1.get(b"key_b")?, Some(b"val_b".into()));
+    assert_eq!(tree_1.get(b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(tree_1.get(b"key_b")?.unwrap().as_ref(), b"val_b");
     let tree_2 = db.open_tree_default(TREE_2)?;
     assert_eq!(tree_2.len()?, 2);
-    assert_eq!(tree_2.get(b"key_d")?, Some(b"val_d".into()));
-    assert_eq!(tree_2.get(b"key_e")?, Some(b"val_e".into()));
+    assert_eq!(tree_2.get(b"key_d")?.unwrap().as_ref(), b"val_d");
+    assert_eq!(tree_2.get(b"key_e")?.unwrap().as_ref(), b"val_e");
     let tree_4 = db.open_tree_default(TREE_4)?;
     assert_eq!(tree_4.len()?, 1);
-    assert_eq!(tree_4.get(b"key_g")?, Some(b"val_g".into()));
+    assert_eq!(tree_4.get(b"key_g")?.unwrap().as_ref(), b"val_g");
     let tree_5 = db.open_tree_default(TREE_5)?;
     assert_eq!(tree_5.len()?, 1);
-    assert_eq!(tree_5.get(b"key_h")?, Some(b"val_h".into()));
+    assert_eq!(tree_5.get(b"key_h")?.unwrap().as_ref(), b"val_h");
 
     assert_eq!(overlay.state.initial_tree_names.len(), 5);
     assert!(overlay.state.initial_tree_names.contains(&TREE_1.into()));
@@ -1605,18 +1605,18 @@ fn database_overlay_protected_trees() -> Result<()> {
 
     let tree_1 = db.open_tree_default(TREE_1)?;
     assert_eq!(tree_1.len()?, 2);
-    assert_eq!(tree_1.get(b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(tree_1.get(b"key_b")?, Some(b"val_b".into()));
+    assert_eq!(tree_1.get(b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(tree_1.get(b"key_b")?.unwrap().as_ref(), b"val_b");
     let tree_2 = db.open_tree_default(TREE_2)?;
     assert_eq!(tree_2.len()?, 2);
-    assert_eq!(tree_2.get(b"key_d")?, Some(b"val_d".into()));
-    assert_eq!(tree_2.get(b"key_f")?, Some(b"val_f".into()));
+    assert_eq!(tree_2.get(b"key_d")?.unwrap().as_ref(), b"val_d");
+    assert_eq!(tree_2.get(b"key_f")?.unwrap().as_ref(), b"val_f");
     let tree_4 = db.open_tree_default(TREE_4)?;
     assert_eq!(tree_4.len()?, 1);
-    assert_eq!(tree_4.get(b"key_g")?, Some(b"val_g".into()));
+    assert_eq!(tree_4.get(b"key_g")?.unwrap().as_ref(), b"val_g");
     let tree_5 = db.open_tree_default(TREE_5)?;
     assert_eq!(tree_5.len()?, 1);
-    assert_eq!(tree_5.get(b"key_h")?, Some(b"val_h".into()));
+    assert_eq!(tree_5.get(b"key_h")?.unwrap().as_ref(), b"val_h");
 
     // Since we removed everything, current overlay must not have
     // diffs over the tree, just the protected opened references,
@@ -1701,21 +1701,21 @@ fn database_overlay_protected_trees() -> Result<()> {
 
     let tree_1 = db.open_tree_default(TREE_1)?;
     assert_eq!(tree_1.len()?, 2);
-    assert_eq!(tree_1.get(b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(tree_1.get(b"key_b")?, Some(b"val_b".into()));
+    assert_eq!(tree_1.get(b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(tree_1.get(b"key_b")?.unwrap().as_ref(), b"val_b");
     let tree_2 = db.open_tree_default(TREE_2)?;
     assert_eq!(tree_2.len()?, 2);
-    assert_eq!(tree_2.get(b"key_d")?, Some(b"val_d".into()));
-    assert_eq!(tree_2.get(b"key_e")?, Some(b"val_e".into()));
+    assert_eq!(tree_2.get(b"key_d")?.unwrap().as_ref(), b"val_d");
+    assert_eq!(tree_2.get(b"key_e")?.unwrap().as_ref(), b"val_e");
     let tree_3 = db.open_tree_default(TREE_3)?;
     assert_eq!(tree_3.len()?, 1);
-    assert_eq!(tree_3.get(b"key_i")?, Some(b"val_i".into()));
+    assert_eq!(tree_3.get(b"key_i")?.unwrap().as_ref(), b"val_i");
     let tree_4 = db.open_tree_default(TREE_4)?;
     assert_eq!(tree_4.len()?, 1);
-    assert_eq!(tree_4.get(b"key_g")?, Some(b"val_g".into()));
+    assert_eq!(tree_4.get(b"key_g")?.unwrap().as_ref(), b"val_g");
     let tree_5 = db.open_tree_default(TREE_5)?;
     assert_eq!(tree_5.len()?, 1);
-    assert_eq!(tree_5.get(b"key_h")?, Some(b"val_h".into()));
+    assert_eq!(tree_5.get(b"key_h")?.unwrap().as_ref(), b"val_h");
 
     overlay.apply_diff(&sequence[1].inverse())?;
     db.flush_default_mode()?;
@@ -1728,14 +1728,14 @@ fn database_overlay_protected_trees() -> Result<()> {
 
     let tree_1 = db.open_tree_default(TREE_1)?;
     assert_eq!(tree_1.len()?, 2);
-    assert_eq!(tree_1.get(b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(tree_1.get(b"key_b")?, Some(b"val_b".into()));
+    assert_eq!(tree_1.get(b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(tree_1.get(b"key_b")?.unwrap().as_ref(), b"val_b");
     let tree_3 = db.open_tree_default(TREE_3)?;
     assert_eq!(tree_3.len()?, 1);
-    assert_eq!(tree_3.get(b"key_i")?, Some(b"val_i".into()));
+    assert_eq!(tree_3.get(b"key_i")?.unwrap().as_ref(), b"val_i");
     let tree_4 = db.open_tree_default(TREE_4)?;
     assert_eq!(tree_4.len()?, 1);
-    assert_eq!(tree_4.get(b"key_g")?, Some(b"val_g".into()));
+    assert_eq!(tree_4.get(b"key_g")?.unwrap().as_ref(), b"val_g");
 
     overlay.apply_diff(&sequence[0].inverse())?;
     db.flush_default_mode()?;
@@ -1768,10 +1768,10 @@ fn database_overlay_protected_trees() -> Result<()> {
 
     let tree_1 = db.open_tree_default(TREE_1)?;
     assert_eq!(tree_1.len()?, 1);
-    assert_eq!(tree_1.get(b"key_a")?, Some(b"val_a".into()));
+    assert_eq!(tree_1.get(b"key_a")?.unwrap().as_ref(), b"val_a");
     let tree_4 = db.open_tree_default(TREE_4)?;
     assert_eq!(tree_4.len()?, 1);
-    assert_eq!(tree_4.get(b"key_g")?, Some(b"val_g".into()));
+    assert_eq!(tree_4.get(b"key_g")?.unwrap().as_ref(), b"val_g");
 
     Ok(())
 }

+ 18 - 18
tests/new_tree_remove.rs

@@ -46,14 +46,14 @@ fn new_tree_remove() -> Result<()> {
     overlay.insert(TREE, b"key_c", b"val_c")?;
 
     // Verify they are in the overlay
-    assert_eq!(overlay.get(TREE, b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(overlay.get(TREE, b"key_b")?, Some(b"val_b".into()));
-    assert_eq!(overlay.get(TREE, b"key_c")?, Some(b"val_c".into()));
+    assert_eq!(overlay.get(TREE, b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(overlay.get(TREE, b"key_b")?.unwrap().as_ref(), b"val_b");
+    assert_eq!(overlay.get(TREE, b"key_c")?.unwrap().as_ref(), b"val_c");
 
     // Verify they are not in the database
-    assert_eq!(tree.get(b"key_a")?, None);
-    assert_eq!(tree.get(b"key_b")?, None);
-    assert_eq!(tree.get(b"key_c")?, None);
+    assert!(tree.get(b"key_a")?.is_none());
+    assert!(tree.get(b"key_b")?.is_none());
+    assert!(tree.get(b"key_c")?.is_none());
 
     // Now we asume something happened and want to scratch everything
     overlay.purge_new_trees()?;
@@ -92,17 +92,17 @@ fn new_tree_remove_multiple_overlays() -> Result<()> {
     overlay1.insert(TREE, b"key_c", b"val_c")?;
 
     // Verify they are in the overlays
-    assert_eq!(overlay0.get(TREE, b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(overlay0.get(TREE, b"key_b")?, Some(b"val_b".into()));
-    assert_eq!(overlay0.get(TREE, b"key_c")?, Some(b"val_c".into()));
-    assert_eq!(overlay1.get(TREE, b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(overlay1.get(TREE, b"key_b")?, Some(b"val_b".into()));
-    assert_eq!(overlay1.get(TREE, b"key_c")?, Some(b"val_c".into()));
+    assert_eq!(overlay0.get(TREE, b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(overlay0.get(TREE, b"key_b")?.unwrap().as_ref(), b"val_b");
+    assert_eq!(overlay0.get(TREE, b"key_c")?.unwrap().as_ref(), b"val_c");
+    assert_eq!(overlay1.get(TREE, b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(overlay1.get(TREE, b"key_b")?.unwrap().as_ref(), b"val_b");
+    assert_eq!(overlay1.get(TREE, b"key_c")?.unwrap().as_ref(), b"val_c");
 
     // Verify they are not in the database
-    assert_eq!(tree.get(b"key_a")?, None);
-    assert_eq!(tree.get(b"key_b")?, None);
-    assert_eq!(tree.get(b"key_c")?, None);
+    assert!(tree.get(b"key_a")?.is_none());
+    assert!(tree.get(b"key_b")?.is_none());
+    assert!(tree.get(b"key_c")?.is_none());
 
     // Now we asume something happened and want to scratch everything
     // in overlay0
@@ -119,9 +119,9 @@ fn new_tree_remove_multiple_overlays() -> Result<()> {
     // We need to re-open the tree since we removed it when we
     // scratched overlay0 (overlay0.purge_new_trees())
     let tree = db.open_tree_default(TREE)?;
-    assert_eq!(tree.get(b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(tree.get(b"key_b")?, Some(b"val_b".into()));
-    assert_eq!(tree.get(b"key_c")?, Some(b"val_c".into()));
+    assert_eq!(tree.get(b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(tree.get(b"key_b")?.unwrap().as_ref(), b"val_b");
+    assert_eq!(tree.get(b"key_c")?.unwrap().as_ref(), b"val_c");
 
     Ok(())
 }

+ 18 - 18
tests/tree_overlay.rs

@@ -54,13 +54,13 @@ fn tree_overlay() -> Result<()> {
     overlay_2.insert(b"key_f", b"val_f")?;
 
     // Verify they are in the overlays
-    assert_eq!(overlay_1.get(b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(overlay_1.get(b"key_b")?, Some(b"val_b".into()));
-    assert_eq!(overlay_1.get(b"key_c")?, Some(b"val_c".into()));
+    assert_eq!(overlay_1.get(b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(overlay_1.get(b"key_b")?.unwrap().as_ref(), b"val_b");
+    assert_eq!(overlay_1.get(b"key_c")?.unwrap().as_ref(), b"val_c");
 
-    assert_eq!(overlay_2.get(b"key_d")?, Some(b"val_d".into()));
-    assert_eq!(overlay_2.get(b"key_e")?, Some(b"val_e".into()));
-    assert_eq!(overlay_2.get(b"key_f")?, Some(b"val_f".into()));
+    assert_eq!(overlay_2.get(b"key_d")?.unwrap().as_ref(), b"val_d");
+    assert_eq!(overlay_2.get(b"key_e")?.unwrap().as_ref(), b"val_e");
+    assert_eq!(overlay_2.get(b"key_f")?.unwrap().as_ref(), b"val_f");
 
     // Check overlays are not empty
     assert!(!overlay_1.is_empty()?);
@@ -71,26 +71,26 @@ fn tree_overlay() -> Result<()> {
     assert_eq!(overlay_2.last()?, Some((b"key_f".into(), b"val_f".into())));
 
     // Verify they are not in the database
-    assert_eq!(tree_1.get(b"key_a")?, None);
-    assert_eq!(tree_1.get(b"key_b")?, None);
-    assert_eq!(tree_1.get(b"key_c")?, None);
+    assert!(tree_1.get(b"key_a")?.is_none());
+    assert!(tree_1.get(b"key_b")?.is_none());
+    assert!(tree_1.get(b"key_c")?.is_none());
 
-    assert_eq!(tree_2.get(b"key_d")?, None);
-    assert_eq!(tree_2.get(b"key_e")?, None);
-    assert_eq!(tree_2.get(b"key_f")?, None);
+    assert!(tree_2.get(b"key_d")?.is_none());
+    assert!(tree_2.get(b"key_e")?.is_none());
+    assert!(tree_2.get(b"key_f")?.is_none());
 
     // Now we write all changes to the database
     db.write_tree_overlays_changes(&[&overlay_1, &overlay_2])?;
     db.flush_default_mode()?;
 
     // Verify database contains keys
-    assert_eq!(tree_1.get(b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(tree_1.get(b"key_b")?, Some(b"val_b".into()));
-    assert_eq!(tree_1.get(b"key_c")?, Some(b"val_c".into()));
+    assert_eq!(tree_1.get(b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(tree_1.get(b"key_b")?.unwrap().as_ref(), b"val_b");
+    assert_eq!(tree_1.get(b"key_c")?.unwrap().as_ref(), b"val_c");
 
-    assert_eq!(tree_2.get(b"key_d")?, Some(b"val_d".into()));
-    assert_eq!(tree_2.get(b"key_e")?, Some(b"val_e".into()));
-    assert_eq!(tree_2.get(b"key_f")?, Some(b"val_f".into()));
+    assert_eq!(tree_2.get(b"key_d")?.unwrap().as_ref(), b"val_d");
+    assert_eq!(tree_2.get(b"key_e")?.unwrap().as_ref(), b"val_e");
+    assert_eq!(tree_2.get(b"key_f")?.unwrap().as_ref(), b"val_f");
 
     Ok(())
 }

+ 18 - 18
tests/tree_overlay_checkpoint.rs

@@ -39,14 +39,14 @@ fn tree_overlay_checkpoint() -> Result<()> {
     overlay.insert(b"key_c", b"val_c")?;
 
     // Verify they are in the overlay
-    assert_eq!(overlay.get(b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(overlay.get(b"key_b")?, Some(b"val_b".into()));
-    assert_eq!(overlay.get(b"key_c")?, Some(b"val_c".into()));
+    assert_eq!(overlay.get(b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(overlay.get(b"key_b")?.unwrap().as_ref(), b"val_b");
+    assert_eq!(overlay.get(b"key_c")?.unwrap().as_ref(), b"val_c");
 
     // Verify they are not in the database
-    assert_eq!(tree.get(b"key_a")?, None);
-    assert_eq!(tree.get(b"key_b")?, None);
-    assert_eq!(tree.get(b"key_c")?, None);
+    assert!(tree.get(b"key_a")?.is_none());
+    assert!(tree.get(b"key_b")?.is_none());
+    assert!(tree.get(b"key_c")?.is_none());
 
     // Now we create an overlay checkpoint
     overlay.checkpoint();
@@ -57,14 +57,14 @@ fn tree_overlay_checkpoint() -> Result<()> {
     overlay.insert(b"key_f", b"val_f")?;
 
     // Verify they are in the overlay
-    assert_eq!(overlay.get(b"key_d")?, Some(b"val_d".into()));
-    assert_eq!(overlay.get(b"key_e")?, Some(b"val_e".into()));
-    assert_eq!(overlay.get(b"key_f")?, Some(b"val_f".into()));
+    assert_eq!(overlay.get(b"key_d")?.unwrap().as_ref(), b"val_d");
+    assert_eq!(overlay.get(b"key_e")?.unwrap().as_ref(), b"val_e");
+    assert_eq!(overlay.get(b"key_f")?.unwrap().as_ref(), b"val_f");
 
     // Verify they are not in the database
-    assert_eq!(tree.get(b"key_d")?, None);
-    assert_eq!(tree.get(b"key_e")?, None);
-    assert_eq!(tree.get(b"key_f")?, None);
+    assert!(tree.get(b"key_d")?.is_none());
+    assert!(tree.get(b"key_e")?.is_none());
+    assert!(tree.get(b"key_f")?.is_none());
 
     // We assume something went wrong, so we revert to last checkpoint
     overlay.revert_to_checkpoint();
@@ -74,14 +74,14 @@ fn tree_overlay_checkpoint() -> Result<()> {
     db.flush_default_mode()?;
 
     // Verify database contains pre-checkpoint keys
-    assert_eq!(tree.get(b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(tree.get(b"key_b")?, Some(b"val_b".into()));
-    assert_eq!(tree.get(b"key_c")?, Some(b"val_c".into()));
+    assert_eq!(tree.get(b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(tree.get(b"key_b")?.unwrap().as_ref(), b"val_b");
+    assert_eq!(tree.get(b"key_c")?.unwrap().as_ref(), b"val_c");
 
     // Verify database doesn't contains keys after checkpoint
-    assert_eq!(tree.get(b"key_d")?, None);
-    assert_eq!(tree.get(b"key_e")?, None);
-    assert_eq!(tree.get(b"key_f")?, None);
+    assert!(tree.get(b"key_d")?.is_none());
+    assert!(tree.get(b"key_e")?.is_none());
+    assert!(tree.get(b"key_f")?.is_none());
 
     Ok(())
 }

+ 37 - 37
tests/tree_overlay_state.rs

@@ -111,15 +111,15 @@ fn tree_overlay_state() -> Result<()> {
     db.write_tree_overlays_diff(&tree, &sequence[0], false)?;
     db.flush_default_mode()?;
     assert_eq!(tree.len()?, 2);
-    assert_eq!(tree.get(b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(tree.get(b"key_b")?, Some(b"val_b".into()));
+    assert_eq!(tree.get(b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(tree.get(b"key_b")?.unwrap().as_ref(), b"val_b");
     overlay.remove_diff(&sequence[0]);
 
     db.write_tree_overlays_diff(&tree, &sequence[1], false)?;
     db.flush_default_mode()?;
     assert_eq!(tree.len()?, 1);
-    assert_eq!(tree.get(b"key_a")?, None);
-    assert_eq!(tree.get(b"key_b")?, Some(b"val_bb".into()));
+    assert!(tree.get(b"key_a")?.is_none());
+    assert_eq!(tree.get(b"key_b")?.unwrap().as_ref(), b"val_bb");
     overlay.remove_diff(&sequence[1]);
 
     // Since we removed the diffs, current overlay diff must be
@@ -130,9 +130,9 @@ fn tree_overlay_state() -> Result<()> {
     db.write_tree_overlays_changes(&[&overlay])?;
     db.flush_default_mode()?;
     assert_eq!(tree.len()?, 2);
-    assert_eq!(tree.get(b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(tree.get(b"key_b")?, None);
-    assert_eq!(tree.get(b"key_c")?, Some(b"val_c".into()));
+    assert_eq!(tree.get(b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert!(tree.get(b"key_b")?.is_none());
+    assert_eq!(tree.get(b"key_c")?.unwrap().as_ref(), b"val_c");
     overlay.remove_diff(&sequence[2]);
 
     // Since we removed everything, current overlay must not have
@@ -152,38 +152,38 @@ fn tree_overlay_state() -> Result<()> {
     db.write_tree_overlays_diff(&tree, &diff, false)?;
     db.flush_default_mode()?;
     assert_eq!(tree.len()?, 2);
-    assert_eq!(tree.get(b"key_a")?, Some(b"val_aa".into()));
-    assert_eq!(tree.get(b"key_b")?, Some(b"val_b".into()));
-    assert_eq!(tree.get(b"key_c")?, None);
+    assert_eq!(tree.get(b"key_a")?.unwrap().as_ref(), b"val_aa");
+    assert_eq!(tree.get(b"key_b")?.unwrap().as_ref(), b"val_b");
+    assert!(tree.get(b"key_c")?.is_none());
 
     // Now we grab the diff revert batch, apply it and verity tree state
     db.write_tree_overlays_diff(&tree, &diff, true)?;
     db.flush_default_mode()?;
     assert_eq!(tree.len()?, 2);
-    assert_eq!(tree.get(b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(tree.get(b"key_b")?, None);
-    assert_eq!(tree.get(b"key_c")?, Some(b"val_c".into()));
+    assert_eq!(tree.get(b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert!(tree.get(b"key_b")?.is_none());
+    assert_eq!(tree.get(b"key_c")?.unwrap().as_ref(), b"val_c");
 
     // Now we are going to revert the diffs sequence going backwards
     // and verify tree state mutates accordingly
     db.write_tree_overlays_diff(&tree, &sequence[2], true)?;
     db.flush_default_mode()?;
     assert_eq!(tree.len()?, 1);
-    assert_eq!(tree.get(b"key_a")?, None);
-    assert_eq!(tree.get(b"key_b")?, Some(b"val_bb".into()));
+    assert!(tree.get(b"key_a")?.is_none());
+    assert_eq!(tree.get(b"key_b")?.unwrap().as_ref(), b"val_bb");
 
     db.write_tree_overlays_diff(&tree, &sequence[1], true)?;
     db.flush_default_mode()?;
     assert_eq!(tree.len()?, 2);
-    assert_eq!(tree.get(b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(tree.get(b"key_b")?, Some(b"val_b".into()));
+    assert_eq!(tree.get(b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(tree.get(b"key_b")?.unwrap().as_ref(), b"val_b");
 
     db.write_tree_overlays_diff(&tree, &sequence[0], true)?;
     db.flush_default_mode()?;
 
     // Tree has now reverted to its original state
     assert_eq!(tree.len()?, 1);
-    assert_eq!(tree.get(b"key_a")?, Some(b"val_a".into()));
+    assert_eq!(tree.get(b"key_a")?.unwrap().as_ref(), b"val_a");
 
     Ok(())
 }
@@ -269,15 +269,15 @@ fn tree_overlay_rebuild_state() -> Result<()> {
     db.write_tree_overlays_diff(&tree, &sequence[0], false)?;
     db.flush_default_mode()?;
     assert_eq!(tree.len()?, 2);
-    assert_eq!(tree.get(b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(tree.get(b"key_b")?, Some(b"val_b".into()));
+    assert_eq!(tree.get(b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(tree.get(b"key_b")?.unwrap().as_ref(), b"val_b");
     overlay.remove_diff(&sequence[0]);
 
     db.write_tree_overlays_diff(&tree, &sequence[1], false)?;
     db.flush_default_mode()?;
     assert_eq!(tree.len()?, 1);
-    assert_eq!(tree.get(b"key_a")?, None);
-    assert_eq!(tree.get(b"key_b")?, Some(b"val_bb".into()));
+    assert!(tree.get(b"key_a")?.is_none());
+    assert_eq!(tree.get(b"key_b")?.unwrap().as_ref(), b"val_bb");
     overlay.remove_diff(&sequence[1]);
 
     // Since we removed the diffs, current overlay diff must be
@@ -288,9 +288,9 @@ fn tree_overlay_rebuild_state() -> Result<()> {
     db.write_tree_overlays_changes(&[&overlay])?;
     db.flush_default_mode()?;
     assert_eq!(tree.len()?, 2);
-    assert_eq!(tree.get(b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(tree.get(b"key_b")?, None);
-    assert_eq!(tree.get(b"key_c")?, Some(b"val_c".into()));
+    assert_eq!(tree.get(b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert!(tree.get(b"key_b")?.is_none());
+    assert_eq!(tree.get(b"key_c")?.unwrap().as_ref(), b"val_c");
     overlay.remove_diff(&sequence[2]);
 
     // Since we removed everything, current overlay must not have
@@ -354,7 +354,7 @@ fn tree_overlay_rebuild_state() -> Result<()> {
     db.write_tree_overlays_changes(&[&overlay])?;
     db.flush_default_mode()?;
     assert_eq!(tree.len()?, 1);
-    assert_eq!(tree.get(b"key_a")?, Some(b"val_a".into()));
+    assert_eq!(tree.get(b"key_a")?.unwrap().as_ref(), b"val_a");
 
     Ok(())
 }
@@ -437,8 +437,8 @@ fn tree_overlay_clear_state() -> Result<()> {
     db.write_tree_overlays_diff(&tree, &sequence[0], false)?;
     db.flush_default_mode()?;
     assert_eq!(tree.len()?, 2);
-    assert_eq!(tree.get(b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(tree.get(b"key_b")?, Some(b"val_b".into()));
+    assert_eq!(tree.get(b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(tree.get(b"key_b")?.unwrap().as_ref(), b"val_b");
     overlay.remove_diff(&sequence[0]);
 
     db.write_tree_overlays_diff(&tree, &sequence[1], false)?;
@@ -454,9 +454,9 @@ fn tree_overlay_clear_state() -> Result<()> {
     db.write_tree_overlays_changes(&[&overlay])?;
     db.flush_default_mode()?;
     assert_eq!(tree.len()?, 2);
-    assert_eq!(tree.get(b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(tree.get(b"key_b")?, None);
-    assert_eq!(tree.get(b"key_c")?, Some(b"val_c".into()));
+    assert_eq!(tree.get(b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert!(tree.get(b"key_b")?.is_none());
+    assert_eq!(tree.get(b"key_c")?.unwrap().as_ref(), b"val_c");
     overlay.remove_diff(&sequence[2]);
 
     // Since we removed everything, current overlay must not have
@@ -478,9 +478,9 @@ fn tree_overlay_clear_state() -> Result<()> {
     // Now we grab the diff revert batch, apply it and verity tree state
     db.write_tree_overlays_diff(&tree, &diff, true)?;
     assert_eq!(tree.len()?, 2);
-    assert_eq!(tree.get(b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(tree.get(b"key_b")?, None);
-    assert_eq!(tree.get(b"key_c")?, Some(b"val_c".into()));
+    assert_eq!(tree.get(b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert!(tree.get(b"key_b")?.is_none());
+    assert_eq!(tree.get(b"key_c")?.unwrap().as_ref(), b"val_c");
 
     // Now we are going to revert the diffs sequence going backwards
     // and verify tree state mutates accordingly
@@ -491,15 +491,15 @@ fn tree_overlay_clear_state() -> Result<()> {
     db.write_tree_overlays_diff(&tree, &sequence[1], true)?;
     db.flush_default_mode()?;
     assert_eq!(tree.len()?, 2);
-    assert_eq!(tree.get(b"key_a")?, Some(b"val_a".into()));
-    assert_eq!(tree.get(b"key_b")?, Some(b"val_b".into()));
+    assert_eq!(tree.get(b"key_a")?.unwrap().as_ref(), b"val_a");
+    assert_eq!(tree.get(b"key_b")?.unwrap().as_ref(), b"val_b");
 
     db.write_tree_overlays_diff(&tree, &sequence[0], true)?;
     db.flush_default_mode()?;
 
     // Tree has now reverted to its original state
     assert_eq!(tree.len()?, 1);
-    assert_eq!(tree.get(b"key_a")?, Some(b"val_a".into()));
+    assert_eq!(tree.get(b"key_a")?.unwrap().as_ref(), b"val_a");
 
     Ok(())
 }

+ 145 - 0
tests/value.rs

@@ -0,0 +1,145 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2026-2026 Dyne.org foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+use std::io::{Cursor, Read};
+
+use kvdb_overlay::{Database, DatabaseOverlay, Error, Result, TreeOverlay, Value};
+
+#[test]
+fn tree_value_owns_bytes() -> Result<()> {
+    let (db, _folder) = Database::open_temp()?;
+    let value = {
+        let tree = db.open_tree_default("values")?;
+        assert!(tree.get(b"missing")?.is_none());
+        tree.insert(b"empty", b"")?;
+        assert_eq!(tree.get(b"empty")?.unwrap().as_ref(), b"");
+
+        tree.insert(b"key", &[42; 128])?;
+        let value = tree.get(b"key")?.unwrap();
+        assert!(matches!(value, Value::Backend(_)));
+        tree.insert(b"key", b"replacement")?;
+        tree.remove(b"key")?;
+        value
+    };
+    drop(db);
+
+    let mut reader = Cursor::new(value);
+    let mut bytes = [0; 128];
+    reader.read_exact(&mut bytes)?;
+    assert_eq!(bytes, [42; 128]);
+    assert_eq!(reader.read(&mut bytes)?, 0);
+    Ok(())
+}
+
+#[test]
+fn overlay_value_borrows_cache() -> Result<()> {
+    let (db, _folder) = Database::open_temp()?;
+    let tree = db.open_tree_default("values")?;
+    tree.insert(b"key", b"backend")?;
+    let mut overlay = TreeOverlay::new(&tree);
+    assert!(overlay.get(b"missing")?.is_none());
+    let value = overlay.get(b"key")?.unwrap();
+    assert!(matches!(value, Value::Backend(_)));
+    assert_eq!(value.as_ref(), b"backend");
+
+    overlay.insert(b"key", &[7; 128])?;
+    let value = overlay.get(b"key")?.unwrap();
+    assert!(matches!(value, Value::Cached(_)));
+    assert_eq!(
+        value.as_ref().as_ptr(),
+        overlay.state.cache.get(b"key".as_slice()).unwrap().as_ptr()
+    );
+    let mut reader = value.as_ref();
+    let mut bytes = [0; 128];
+    reader.read_exact(&mut bytes)?;
+    assert_eq!(bytes, [7; 128]);
+    assert_eq!(reader.read(&mut bytes)?, 0);
+
+    overlay.remove(b"key")?;
+    assert!(overlay.get(b"key")?.is_none());
+    assert!(tree.get(b"key")?.is_some());
+    overlay.insert(b"key", b"")?;
+    assert_eq!(overlay.get(b"key")?.unwrap().as_ref(), b"");
+
+    // Removals take precedence even if a public cache state contains both.
+    overlay.state.removed.insert(b"key".to_vec());
+    assert!(overlay.get(b"key")?.is_none());
+    Ok(())
+}
+
+#[test]
+fn database_overlay_value() -> Result<()> {
+    let (db, _folder) = Database::open_temp()?;
+    let tree = db.open_tree_default("values")?;
+    tree.insert(b"key", b"backend")?;
+    let mut overlay = DatabaseOverlay::new(&db, vec![])?;
+    assert!(matches!(
+        overlay.get("values", b"key"),
+        Err(Error::CollectionNotFound(_))
+    ));
+    overlay.open_tree_default("values", false)?;
+    assert!(overlay.get("values", b"missing")?.is_none());
+    assert_eq!(overlay.get("values", b"key")?.unwrap().as_ref(), b"backend");
+    overlay.insert("values", b"key", b"cached")?;
+    assert_eq!(overlay.get("values", b"key")?.unwrap().as_ref(), b"cached");
+    overlay.remove("values", b"key")?;
+    assert!(overlay.get("values", b"key")?.is_none());
+    overlay.drop_tree("values")?;
+    assert!(matches!(
+        overlay.get("values", b"key"),
+        Err(Error::CollectionNotFound(_))
+    ));
+    Ok(())
+}
+
+#[test]
+fn value_into_vec_copies_bytes() -> Result<()> {
+    for cached in [false, true] {
+        for original in [b"value".as_slice(), b""] {
+            let mut bytes = {
+                let (db, _folder) = Database::open_temp()?;
+                let tree = db.open_tree_default("values")?;
+                tree.insert(b"key", original)?;
+                let mut overlay = TreeOverlay::new(&tree);
+                if cached {
+                    overlay.insert(b"key", original)?;
+                }
+                let value = overlay.get(b"key")?.unwrap();
+                if cached {
+                    assert!(matches!(value, Value::Cached(_)));
+                } else {
+                    assert!(matches!(value, Value::Backend(_)));
+                }
+                let mut bytes = value.into_vec();
+                assert_eq!(bytes, original);
+                bytes.reverse();
+                bytes.push(b'!');
+                assert_eq!(overlay.get(b"key")?.unwrap().as_ref(), original);
+                assert_eq!(tree.get(b"key")?.unwrap().as_ref(), original);
+
+                overlay.insert(b"key", b"replacement")?;
+                overlay.remove(b"key")?;
+                bytes
+            };
+            assert_eq!(bytes.pop(), Some(b'!'));
+            bytes.reverse();
+            assert_eq!(bytes, original);
+        }
+    }
+    Ok(())
+}