From 3656a1ada7639e146536bbab2d19bb82f6b1079e Mon Sep 17 00:00:00 2001 From: hinto-janai Date: Fri, 22 Mar 2024 17:11:48 -0400 Subject: [PATCH] database: return owned `T` in `Storable` (#92) * add `storable_slice.rs` * storable: `from_bytes(&[u8]) -> Self` * heed: return `T` in `Storable`, use `StorableSlice` in tests * remove `value_guard.rs` * redb: update `Storable` to return owned `T` * database: return `T` directly, remove `'a` lifetime * replace `[]` with `StorableSlice` * tables: add lifetime to `tables!()` and generated structs * backend: fix tests * heed: update fn signatures * storable: add `StorableVec`, remove `Storable::ALIGN` * tables/types: remove slice lifetimes, use `StorableVec` * backend: use `StorableVec`, fix tests * types: fix tests to use owned returned value * heed: fix `Storable` impl and tests * storable: `StorableVec` docs + tests * backend: assert values are detached from `get_range()` iterator * database: add docs * udpate docs, remove unneeded clippy `allow` * replace `ToOwnedDebug` with `std::fmt::Debug` * impl `StorableBytes` * backend: add `StorableBytes` to tests * readme: remove `to_owned_debug.rs` reference --- Cargo.lock | 1 + database/Cargo.toml | 5 +- database/README.md | 2 - database/src/backend/heed/database.rs | 37 ++--- database/src/backend/heed/storable.rs | 19 +-- database/src/backend/redb/database.rs | 105 ++----------- database/src/backend/redb/storable.rs | 71 ++++----- database/src/backend/tests.rs | 84 ++++++----- database/src/database.rs | 17 +-- database/src/key.rs | 5 +- database/src/lib.rs | 10 +- database/src/storable.rs | 210 ++++++++++++++++---------- database/src/table.rs | 6 +- database/src/to_owned_debug.rs | 51 ------- database/src/types.rs | 47 +++--- database/src/value_guard.rs | 47 ------ 16 files changed, 278 insertions(+), 439 deletions(-) delete mode 100644 database/src/to_owned_debug.rs delete mode 100644 database/src/value_guard.rs diff --git a/Cargo.lock b/Cargo.lock index fa2d27f..b57f1e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -592,6 +592,7 @@ name = "cuprate-database" version = "0.0.0" dependencies = [ "bytemuck", + "bytes", "cfg-if", "crossbeam", "cuprate-helper", diff --git a/database/Cargo.toml b/database/Cargo.toml index 070d2d1..ac01584 100644 --- a/database/Cargo.toml +++ b/database/Cargo.toml @@ -9,14 +9,15 @@ repository = "https://github.com/Cuprate/cuprate/tree/main/database" keywords = ["cuprate", "database"] [features] -# default = ["heed", "redb", "service"] -default = ["redb", "service"] +default = ["heed", "redb", "service"] +# default = ["redb", "service"] heed = ["dep:heed"] redb = ["dep:redb"] service = ["dep:crossbeam", "dep:futures", "dep:tokio", "dep:tokio-util", "dep:tower", "dep:rayon"] [dependencies] bytemuck = { version = "1.14.3", features = ["must_cast", "derive", "min_const_generics", "extern_crate_alloc"] } +bytes = { workspace = true } cfg-if = { workspace = true } # FIXME: # We only need the `thread` feature if `service` is enabled. diff --git a/database/README.md b/database/README.md index 9b91bc7..e45f973 100644 --- a/database/README.md +++ b/database/README.md @@ -79,10 +79,8 @@ The top-level `src/` files. | `storable.rs` | Data (de)serialization; `trait Storable` | `table.rs` | Database table abstraction; `trait Table` | `tables.rs` | All the table definitions used by `cuprate-database` -| `to_owned_debug.rs` | Borrowed/owned data abstraction; `trait ToOwnedDebug` | `transaction.rs` | Database transaction abstraction; `trait TxR{o,w}` | `types.rs` | Database table schema types -| `value_guard.rs` | Database value "guard" abstraction; `trait ValueGuard` ## `src/ops/` This folder contains the `cupate_database::ops` module. diff --git a/database/src/backend/heed/database.rs b/database/src/backend/heed/database.rs index 8070c83..d54958f 100644 --- a/database/src/backend/heed/database.rs +++ b/database/src/backend/heed/database.rs @@ -13,7 +13,6 @@ use crate::{ database::{DatabaseRo, DatabaseRw}, error::RuntimeError, table::Table, - value_guard::ValueGuard, }; //---------------------------------------------------------------------------------------------------- Heed Database Wrappers @@ -56,14 +55,12 @@ pub(super) struct HeedTableRw<'env, 'tx, T: Table> { /// Shared generic `get()` between `HeedTableR{o,w}`. #[inline] -fn get<'a, T: Table>( - db: &'_ HeedDb, - tx_ro: &'a heed::RoTxn<'_>, +fn get( + db: &HeedDb, + tx_ro: &heed::RoTxn<'_>, key: &T::Key, -) -> Result + 'a, RuntimeError> { - db.get(tx_ro, key)? - .map(Cow::Borrowed) - .ok_or(RuntimeError::KeyNotFound) +) -> Result { + db.get(tx_ro, key)?.ok_or(RuntimeError::KeyNotFound) } /// Shared generic `get_range()` between `HeedTableR{o,w}`. @@ -71,29 +68,26 @@ fn get<'a, T: Table>( fn get_range<'a, T: Table, Range>( db: &'a HeedDb, tx_ro: &'a heed::RoTxn<'_>, - range: &'a Range, -) -> Result + 'a, RuntimeError>>, RuntimeError> + range: Range, +) -> Result> + 'a, RuntimeError> where Range: RangeBounds + 'a, { - Ok(db.range(tx_ro, range)?.map(|res| Ok(Cow::Borrowed(res?.1)))) + Ok(db.range(tx_ro, &range)?.map(|res| Ok(res?.1))) } //---------------------------------------------------------------------------------------------------- DatabaseRo Impl impl<'tx, T: Table> DatabaseRo<'tx, T> for HeedTableRo<'tx, T> { #[inline] - fn get<'a>(&'a self, key: &'a T::Key) -> Result + 'a, RuntimeError> { + fn get(&self, key: &T::Key) -> Result { get::(&self.db, self.tx_ro, key) } #[inline] fn get_range<'a, Range>( &'a self, - range: &'a Range, - ) -> Result< - impl Iterator + 'a, RuntimeError>>, - RuntimeError, - > + range: Range, + ) -> Result> + 'a, RuntimeError> where Range: RangeBounds + 'a, { @@ -104,18 +98,15 @@ impl<'tx, T: Table> DatabaseRo<'tx, T> for HeedTableRo<'tx, T> { //---------------------------------------------------------------------------------------------------- DatabaseRw Impl impl<'tx, T: Table> DatabaseRo<'tx, T> for HeedTableRw<'_, 'tx, T> { #[inline] - fn get<'a>(&'a self, key: &'a T::Key) -> Result + 'a, RuntimeError> { + fn get(&self, key: &T::Key) -> Result { get::(&self.db, self.tx_rw, key) } #[inline] fn get_range<'a, Range>( &'a self, - range: &'a Range, - ) -> Result< - impl Iterator + 'a, RuntimeError>>, - RuntimeError, - > + range: Range, + ) -> Result> + 'a, RuntimeError> where Range: RangeBounds + 'a, { diff --git a/database/src/backend/heed/storable.rs b/database/src/backend/heed/storable.rs index 9014a28..0d180c2 100644 --- a/database/src/backend/heed/storable.rs +++ b/database/src/backend/heed/storable.rs @@ -5,7 +5,7 @@ use std::{borrow::Cow, fmt::Debug, marker::PhantomData}; use heed::{types::Bytes, BoxedError, BytesDecode, BytesEncode, Database}; -use crate::storable::Storable; +use crate::{storable::Storable, storable::StorableVec}; //---------------------------------------------------------------------------------------------------- StorableHeed /// The glue struct that implements `heed`'s (de)serialization @@ -19,9 +19,9 @@ where //---------------------------------------------------------------------------------------------------- BytesDecode impl<'a, T> BytesDecode<'a> for StorableHeed where - T: Storable + ?Sized + 'a, + T: Storable + 'static, { - type DItem = &'a T; + type DItem = T; #[inline] /// This function is infallible (will always return `Ok`). @@ -47,9 +47,8 @@ where //---------------------------------------------------------------------------------------------------- Tests #[cfg(test)] mod test { - use std::fmt::Debug; - use super::*; + use crate::{StorableBytes, StorableVec}; // Each `#[test]` function has a `test()` to: // - log @@ -79,7 +78,8 @@ mod test { test::(&-2, &[254, 255]); test::(&-3, &[253, 255, 255, 255]); test::(&-4, &[252, 255, 255, 255, 255, 255, 255, 255]); - test::<[u8]>(&[1, 2], &[1, 2]); + test::>(&StorableVec(vec![1, 2]), &[1, 2]); + test::(&StorableBytes(bytes::Bytes::from_static(&[1, 2])), &[1, 2]); test::<[u8; 0]>(&[], &[]); test::<[u8; 1]>(&[255], &[255]); test::<[u8; 2]>(&[111, 0], &[111, 0]); @@ -91,12 +91,12 @@ mod test { fn bytes_decode() { fn test(bytes: &[u8], expected: &T) where - T: Storable + ?Sized + PartialEq + ToOwned + Debug, + T: Storable + PartialEq + ToOwned + Debug + 'static, T::Owned: Debug, { println!("bytes: {bytes:?}, expected: {expected:?}"); assert_eq!( - as BytesDecode>::bytes_decode(bytes).unwrap(), + & as BytesDecode>::bytes_decode(bytes).unwrap(), expected ); } @@ -110,7 +110,8 @@ mod test { test::([254, 255].as_slice(), &-2); test::([253, 255, 255, 255].as_slice(), &-3); test::([252, 255, 255, 255, 255, 255, 255, 255].as_slice(), &-4); - test::<[u8]>([1, 2].as_slice(), &[1, 2]); + test::>(&[1, 2], &StorableVec(vec![1, 2])); + test::(&[1, 2], &StorableBytes(bytes::Bytes::from_static(&[1, 2]))); test::<[u8; 0]>([].as_slice(), &[]); test::<[u8; 1]>([255].as_slice(), &[255]); test::<[u8; 2]>([111, 0].as_slice(), &[111, 0]); diff --git a/database/src/backend/redb/database.rs b/database/src/backend/redb/database.rs index cb50d26..ab65f3f 100644 --- a/database/src/backend/redb/database.rs +++ b/database/src/backend/redb/database.rs @@ -17,8 +17,6 @@ use crate::{ error::RuntimeError, storable::Storable, table::Table, - value_guard::ValueGuard, - ToOwnedDebug, }; //---------------------------------------------------------------------------------------------------- Shared functions @@ -28,108 +26,40 @@ use crate::{ /// Shared generic `get()` between `RedbTableR{o,w}`. #[inline] -fn get<'a, T: Table + 'static>( - db: &'a impl redb::ReadableTable, StorableRedb>, - key: &'a T::Key, -) -> Result + 'a, RuntimeError> { - db.get(Cow::Borrowed(key))?.ok_or(RuntimeError::KeyNotFound) +fn get( + db: &impl redb::ReadableTable, StorableRedb>, + key: &T::Key, +) -> Result { + Ok(db.get(key)?.ok_or(RuntimeError::KeyNotFound)?.value()) } /// Shared generic `get_range()` between `RedbTableR{o,w}`. #[inline] fn get_range<'a, T: Table, Range>( db: &'a impl redb::ReadableTable, StorableRedb>, - range: &'a Range, -) -> Result< - impl Iterator>, RuntimeError>> + 'a, - RuntimeError, -> + range: Range, +) -> Result> + 'a, RuntimeError> where Range: RangeBounds + 'a, { - /// HACK: `redb` sees the database's key type as `Cow<'_, T::Key>`, - /// not `T::Key` directly like `heed` does. As such, it wants the - /// range to be over `Cow<'_, T::Key>`, not `T::Key` directly. - /// - /// If `DatabaseRo` were to want `Cow<'_, T::Key>` as input in `get()`, - /// `get_range()`, it would complicate the API: - /// ```rust,ignore - /// // This would be needed... - /// let range = Cow::Owned(0)..Cow::Owned(1); - /// // ...instead of the more obvious - /// let range = 0..1; - /// ``` - /// - /// As such, `DatabaseRo` only wants `RangeBounds` and - /// we create a compatibility struct here, essentially converting - /// this functions input: - /// ```rust,ignore - /// RangeBound - /// ``` - /// into `redb`'s desired: - /// ```rust,ignore - /// RangeBound> - /// ``` - struct CowRange<'a, K> - where - K: ToOwnedDebug, - { - /// The start bound of `Range`. - start_bound: Bound>, - /// The end bound of `Range`. - end_bound: Bound>, - } - - /// This impl forwards our `T::Key` to be wrapped in a Cow. - impl<'a, K> RangeBounds> for CowRange<'a, K> - where - K: ToOwnedDebug, - { - fn start_bound(&self) -> Bound<&Cow<'a, K>> { - self.start_bound.as_ref() - } - - fn end_bound(&self) -> Bound<&Cow<'a, K>> { - self.end_bound.as_ref() - } - } - - let start_bound = match range.start_bound() { - Bound::Included(t) => Bound::Included(Cow::Borrowed(t)), - Bound::Excluded(t) => Bound::Excluded(Cow::Borrowed(t)), - Bound::Unbounded => Bound::Unbounded, - }; - let end_bound = match range.end_bound() { - Bound::Included(t) => Bound::Included(Cow::Borrowed(t)), - Bound::Excluded(t) => Bound::Excluded(Cow::Borrowed(t)), - Bound::Unbounded => Bound::Unbounded, - }; - let range = CowRange { - start_bound, - end_bound, - }; - Ok(db.range(range)?.map(|result| { let (_key, value_guard) = result?; - Ok(value_guard) + Ok(value_guard.value()) })) } //---------------------------------------------------------------------------------------------------- DatabaseRo impl<'tx, T: Table + 'static> DatabaseRo<'tx, T> for RedbTableRo<'tx, T::Key, T::Value> { #[inline] - fn get<'a>(&'a self, key: &'a T::Key) -> Result + 'a, RuntimeError> { + fn get(&self, key: &T::Key) -> Result { get::(self, key) } #[inline] fn get_range<'a, Range>( &'a self, - range: &'a Range, - ) -> Result< - impl Iterator, RuntimeError>> + 'a, - RuntimeError, - > + range: Range, + ) -> Result> + 'a, RuntimeError> where Range: RangeBounds + 'a, { @@ -140,18 +70,15 @@ impl<'tx, T: Table + 'static> DatabaseRo<'tx, T> for RedbTableRo<'tx, T::Key, T: //---------------------------------------------------------------------------------------------------- DatabaseRw impl<'tx, T: Table + 'static> DatabaseRo<'tx, T> for RedbTableRw<'_, 'tx, T::Key, T::Value> { #[inline] - fn get<'a>(&'a self, key: &'a T::Key) -> Result + 'a, RuntimeError> { + fn get(&self, key: &T::Key) -> Result { get::(self, key) } #[inline] fn get_range<'a, Range>( &'a self, - range: &'a Range, - ) -> Result< - impl Iterator, RuntimeError>> + 'a, - RuntimeError, - > + range: Range, + ) -> Result> + 'a, RuntimeError> where Range: RangeBounds + 'a, { @@ -167,13 +94,13 @@ impl<'env, 'tx, T: Table + 'static> DatabaseRw<'env, 'tx, T> #[inline] fn put(&mut self, key: &T::Key, value: &T::Value) -> Result<(), RuntimeError> { - self.insert(Cow::Borrowed(key), Cow::Borrowed(value))?; + self.insert(key, value)?; Ok(()) } #[inline] fn delete(&mut self, key: &T::Key) -> Result<(), RuntimeError> { - self.remove(Cow::Borrowed(key))?; + self.remove(key)?; Ok(()) } } diff --git a/database/src/backend/redb/storable.rs b/database/src/backend/redb/storable.rs index 077db25..4a6b8f9 100644 --- a/database/src/backend/redb/storable.rs +++ b/database/src/backend/redb/storable.rs @@ -5,7 +5,7 @@ use std::{any::Any, borrow::Cow, cmp::Ordering, fmt::Debug, marker::PhantomData} use redb::{RedbKey, RedbValue, TypeName}; -use crate::{key::Key, storable::Storable, value_guard::ValueGuard}; +use crate::{key::Key, storable::Storable}; //---------------------------------------------------------------------------------------------------- StorableRedb /// The glue structs that implements `redb`'s (de)serialization @@ -15,27 +15,13 @@ use crate::{key::Key, storable::Storable, value_guard::ValueGuard}; #[derive(Debug)] pub(super) struct StorableRedb(PhantomData) where - T: Storable + ?Sized; - -impl ValueGuard for redb::AccessGuard<'_, StorableRedb> { - #[inline] - fn unguard(&self) -> Cow<'_, T> { - self.value() - } -} - -impl ValueGuard for &redb::AccessGuard<'_, StorableRedb> { - #[inline] - fn unguard(&self) -> Cow<'_, T> { - self.value() - } -} + T: Storable; //---------------------------------------------------------------------------------------------------- RedbKey // If `Key` is also implemented, this can act as a `RedbKey`. impl RedbKey for StorableRedb where - T: Key + ?Sized, + T: Key + 'static, { #[inline] fn compare(left: &[u8], right: &[u8]) -> Ordering { @@ -46,9 +32,9 @@ where //---------------------------------------------------------------------------------------------------- RedbValue impl RedbValue for StorableRedb where - T: Storable + ?Sized, + T: Storable + 'static, { - type SelfType<'a> = Cow<'a, T> where Self: 'a; + type SelfType<'a> = T where Self: 'a; type AsBytes<'a> = &'a [u8] where Self: 'a; #[inline] @@ -57,18 +43,11 @@ where } #[inline] - fn from_bytes<'a>(data: &'a [u8]) -> Self::SelfType<'a> + fn from_bytes<'a>(data: &'a [u8]) -> Self::SelfType<'static> where Self: 'a, { - // Use the bytes directly if possible... - if T::ALIGN == 1 { - Cow::Borrowed(::from_bytes(data)) - // ...else, make sure the bytes are aligned - // when casting by allocating a new buffer. - } else { - ::from_bytes_unaligned(data) - } + ::from_bytes(data) } #[inline] @@ -76,7 +55,7 @@ where where Self: 'a + 'b, { - ::as_bytes(value.as_ref()) + ::as_bytes(value) } #[inline] @@ -90,6 +69,7 @@ where #[allow(clippy::needless_pass_by_value)] mod test { use super::*; + use crate::{StorableBytes, StorableVec}; // Each `#[test]` function has a `test()` to: // - log @@ -101,13 +81,13 @@ mod test { fn compare() { fn test(left: T, right: T, expected: Ordering) where - T: Key, + T: Key + 'static, { println!("left: {left:?}, right: {right:?}, expected: {expected:?}"); assert_eq!( as RedbKey>::compare( - as RedbValue>::as_bytes(&Cow::Borrowed(&left)), - as RedbValue>::as_bytes(&Cow::Borrowed(&right)) + as RedbValue>::as_bytes(&left), + as RedbValue>::as_bytes(&right) ), expected ); @@ -124,7 +104,7 @@ mod test { fn fixed_width() { fn test(expected: Option) where - T: Storable + ?Sized, + T: Storable + 'static, { assert_eq!( as RedbValue>::fixed_width(), expected); } @@ -138,7 +118,8 @@ mod test { test::(Some(2)); test::(Some(4)); test::(Some(8)); - test::<[u8]>(None); + test::>(None); + test::(None); test::<[u8; 0]>(Some(0)); test::<[u8; 1]>(Some(1)); test::<[u8; 2]>(Some(2)); @@ -150,13 +131,10 @@ mod test { fn as_bytes() { fn test(t: &T, expected: &[u8]) where - T: Storable + ?Sized, + T: Storable + 'static, { println!("t: {t:?}, expected: {expected:?}"); - assert_eq!( - as RedbValue>::as_bytes(&Cow::Borrowed(t)), - expected - ); + assert_eq!( as RedbValue>::as_bytes(t), expected); } test::<()>(&(), &[]); @@ -168,7 +146,8 @@ mod test { test::(&-2, &[254, 255]); test::(&-3, &[253, 255, 255, 255]); test::(&-4, &[252, 255, 255, 255, 255, 255, 255, 255]); - test::<[u8]>(&[1, 2], &[1, 2]); + test::>(&StorableVec([1, 2].to_vec()), &[1, 2]); + test::(&StorableBytes(bytes::Bytes::from_static(&[1, 2])), &[1, 2]); test::<[u8; 0]>(&[], &[]); test::<[u8; 1]>(&[255], &[255]); test::<[u8; 2]>(&[111, 0], &[111, 0]); @@ -180,12 +159,12 @@ mod test { fn from_bytes() { fn test(bytes: &[u8], expected: &T) where - T: Storable + PartialEq + ?Sized, + T: Storable + PartialEq + 'static, { println!("bytes: {bytes:?}, expected: {expected:?}"); assert_eq!( - as RedbValue>::from_bytes(bytes), - Cow::Borrowed(expected) + & as RedbValue>::from_bytes(bytes), + expected ); } @@ -198,7 +177,8 @@ mod test { test::([254, 255].as_slice(), &-2); test::([253, 255, 255, 255].as_slice(), &-3); test::([252, 255, 255, 255, 255, 255, 255, 255].as_slice(), &-4); - test::<[u8]>([1, 2].as_slice(), &[1, 2]); + test::>(&[1, 2], &StorableVec(vec![1, 2])); + test::(&[1, 2], &StorableBytes(bytes::Bytes::from_static(&[1, 2]))); test::<[u8; 0]>([].as_slice(), &[]); test::<[u8; 1]>([255].as_slice(), &[255]); test::<[u8; 2]>([111, 0].as_slice(), &[111, 0]); @@ -221,7 +201,8 @@ mod test { as RedbValue>::type_name(), as RedbValue>::type_name(), as RedbValue>::type_name(), - as RedbValue>::type_name(), + > as RedbValue>::type_name(), + as RedbValue>::type_name(), as RedbValue>::type_name(), as RedbValue>::type_name(), as RedbValue>::type_name(), diff --git a/database/src/backend/tests.rs b/database/src/backend/tests.rs index 83ddaf9..960d44d 100644 --- a/database/src/backend/tests.rs +++ b/database/src/backend/tests.rs @@ -24,6 +24,7 @@ use crate::{ env::{Env, EnvInner}, error::{InitError, RuntimeError}, resize::ResizeAlgorithm, + storable::StorableVec, table::Table, tables::{ BlockBlobs, BlockHeights, BlockInfoV1s, BlockInfoV2s, BlockInfoV3s, KeyImages, NumOutputs, @@ -36,7 +37,6 @@ use crate::{ BlockInfoV2, BlockInfoV3, KeyImage, Output, PreRctOutputId, PrunableBlob, PrunableHash, PrunedBlob, RctOutput, TxHash, TxId, UnlockTime, }, - value_guard::ValueGuard, ConcreteEnv, }; @@ -181,10 +181,10 @@ fn db_read_write() { tx_idx: 2_353_487, }; - /// Assert a passed `Output` is equal to the const value. - fn assert_eq(output: &Output) { - assert_eq!(output, &VALUE); - // Make sure all field accesses are aligned. + /// Assert 2 `Output`'s are equal, and that accessing + /// their fields don't result in an unaligned panic. + fn assert_same(output: Output) { + assert_eq!(output, VALUE); assert_eq!(output.key, VALUE.key); assert_eq!(output.height, VALUE.height); assert_eq!(output.output_flags, VALUE.output_flags); @@ -200,31 +200,47 @@ fn db_read_write() { // Assert the 1st key is there. { - let guard = table.get(&KEY).unwrap(); - let cow: Cow<'_, Output> = guard.unguard(); - let value: &Output = cow.as_ref(); - assert_eq(value); + let value: Output = table.get(&KEY).unwrap(); + assert_same(value); } // Assert the whole range is there. { - let range = table.get_range(&..).unwrap(); + let range = table.get_range(..).unwrap(); let mut i = 0; for result in range { - let guard = result.unwrap(); - let cow: Cow<'_, Output> = guard.unguard(); - let value: &Output = cow.as_ref(); - assert_eq(value); + let value: Output = result.unwrap(); + assert_same(value); + i += 1; } assert_eq!(i, 100); } - // Assert `get_range()` works. + // `get_range()` tests. let mut key = KEY; key.amount += 100; let range = KEY..key; - assert_eq!(100, table.get_range(&range).unwrap().count()); + + // Assert count is correct. + assert_eq!(100, table.get_range(range.clone()).unwrap().count()); + + // Assert each returned value from the iterator is owned. + { + let mut iter = table.get_range(range.clone()).unwrap(); + let value: Output = iter.next().unwrap().unwrap(); // 1. take value out + drop(iter); // 2. drop the `impl Iterator + 'a` + assert_same(value); // 3. assert even without the iterator, the value is alive + } + + // Assert each value is the same. + { + let mut iter = table.get_range(range).unwrap(); + for _ in 0..100 { + let value: Output = iter.next().unwrap().unwrap(); + assert_same(value); + } + } // Assert deleting works. table.delete(&KEY).unwrap(); @@ -261,33 +277,29 @@ macro_rules! test_tables { /// The expected key. const KEY: $key_type = $key; - /// The expected value. - const VALUE: &$value_type = &$value; + // The expected value. + let value: $value_type = $value; - /// Assert a passed value is equal to the const value. - fn assert_eq(value: &$value_type) { - assert_eq!(value, VALUE); - } + // Assert a passed value is equal to the const value. + let assert_eq = |v: &$value_type| { + assert_eq!(v, &value); + }; // Insert the key. - table.put(&KEY, VALUE).unwrap(); + table.put(&KEY, &value).unwrap(); // Assert key is there. { - let guard = table.get(&KEY).unwrap(); - let cow: Cow<'_, $value_type> = guard.unguard(); - let value: &$value_type = cow.as_ref(); - assert_eq(value); + let value: $value_type = table.get(&KEY).unwrap(); + assert_eq(&value); } // Assert `get_range()` works. { let range = KEY..; - assert_eq!(1, table.get_range(&range).unwrap().count()); - let mut iter = table.get_range(&range).unwrap(); - let guard = iter.next().unwrap().unwrap(); - let cow = guard.unguard(); - let value = cow.as_ref(); - assert_eq(value); + assert_eq!(1, table.get_range(range.clone()).unwrap().count()); + let mut iter = table.get_range(range).unwrap(); + let value = iter.next().unwrap().unwrap(); + assert_eq(&value); } // Assert deleting works. @@ -303,7 +315,7 @@ macro_rules! test_tables { test_tables! { BlockBlobs, // Table type BlockHeight => BlockBlob, // Key type => Value type - 123 => [1,2,3,4,5,6,7,8].as_slice(), // Actual key => Actual value + 123 => StorableVec(vec![1,2,3,4,5,6,7,8]), // Actual key => Actual value BlockHeights, BlockHash => BlockHeight, @@ -377,11 +389,11 @@ test_tables! { PrunedTxBlobs, TxId => PrunedBlob, - 123 => [1,2,3,4,5,6,7,8].as_slice(), + 123 => StorableVec(vec![1,2,3,4,5,6,7,8]), PrunableTxBlobs, TxId => PrunableBlob, - 123 => [1,2,3,4,5,6,7,8].as_slice(), + 123 => StorableVec(vec![1,2,3,4,5,6,7,8]), PrunableHashes, TxId => PrunableHash, diff --git a/database/src/database.rs b/database/src/database.rs index 80eae7d..67fbaaa 100644 --- a/database/src/database.rs +++ b/database/src/database.rs @@ -11,7 +11,6 @@ use crate::{ error::RuntimeError, table::Table, transaction::{TxRo, TxRw}, - value_guard::ValueGuard, }; //---------------------------------------------------------------------------------------------------- DatabaseRo @@ -22,19 +21,18 @@ use crate::{ pub trait DatabaseRo<'tx, T: Table> { /// Get the value corresponding to a key. /// - /// This returns a guard to the value, not the value itself. - /// See [`ValueGuard`] for more info. + /// The returned value is _owned_. /// /// # Errors /// This will return [`RuntimeError::KeyNotFound`] wrapped in [`Err`] if `key` does not exist. /// /// It will return other [`RuntimeError`]'s on things like IO errors as well. - fn get<'a>(&'a self, key: &'a T::Key) -> Result + 'a, RuntimeError>; + fn get(&self, key: &T::Key) -> Result; /// Get an iterator of values corresponding to a range of keys. /// - /// This returns guards to the values, not the values themselves. - /// See [`ValueGuard`] for more info. + /// Although the returned iterator itself is tied to the lifetime + /// of `&'a self`, the returned values from the iterator are _owned_. /// /// # Errors /// Each key in the `range` has the potential to error, for example, @@ -43,11 +41,8 @@ pub trait DatabaseRo<'tx, T: Table> { /// from the iterator. fn get_range<'a, Range>( &'a self, - range: &'a Range, - ) -> Result< - impl Iterator, RuntimeError>> + 'a, - RuntimeError, - > + range: Range, + ) -> Result> + 'a, RuntimeError> where Range: RangeBounds + 'a; } diff --git a/database/src/key.rs b/database/src/key.rs index 2e4d616..f88455c 100644 --- a/database/src/key.rs +++ b/database/src/key.rs @@ -5,10 +5,7 @@ use std::{cmp::Ordering, fmt::Debug}; use bytemuck::Pod; -use crate::{ - storable::{self, Storable}, - ToOwnedDebug, -}; +use crate::storable::{self, Storable}; //---------------------------------------------------------------------------------------------------- Table /// Database [`Table`](crate::table::Table) key metadata. diff --git a/database/src/lib.rs b/database/src/lib.rs index e42254b..51315f2 100644 --- a/database/src/lib.rs +++ b/database/src/lib.rs @@ -129,7 +129,6 @@ redundant_semicolons, unused_allocation, coherence_leak_check, - single_use_lifetimes, while_true, clippy::missing_docs_in_private_items, @@ -142,6 +141,7 @@ keyword_idents, non_ascii_idents, variant_size_differences, + single_use_lifetimes, // Probably can be put into `#[deny]`. future_incompatible, @@ -240,7 +240,7 @@ pub use key::Key; mod macros; mod storable; -pub use storable::Storable; +pub use storable::{Storable, StorableBytes, StorableVec}; pub mod ops; @@ -254,12 +254,6 @@ pub mod types; mod transaction; pub use transaction::{TxRo, TxRw}; -mod to_owned_debug; -pub use to_owned_debug::ToOwnedDebug; - -mod value_guard; -pub use value_guard::ValueGuard; - //---------------------------------------------------------------------------------------------------- Feature-gated #[cfg(feature = "service")] pub mod service; diff --git a/database/src/storable.rs b/database/src/storable.rs index 6f04af1..ac3f263 100644 --- a/database/src/storable.rs +++ b/database/src/storable.rs @@ -2,16 +2,15 @@ //---------------------------------------------------------------------------------------------------- Import use std::{ - borrow::Cow, + borrow::{Borrow, Cow}, char::ToLowercase, fmt::Debug, io::{Read, Write}, sync::Arc, }; -use bytemuck::Pod; - -use crate::ToOwnedDebug; +use bytemuck::{Pod, Zeroable}; +use bytes::Bytes; //---------------------------------------------------------------------------------------------------- Storable /// A type that can be stored in the database. @@ -35,6 +34,8 @@ use crate::ToOwnedDebug; /// - All types in [`tables`](crate::tables) /// - Slices, e.g, `[T] where T: Storable` /// +/// See [`StorableVec`] for storing slices of `T: Storable`. +/// /// ```rust /// # use cuprate_database::*; /// # use std::borrow::*; @@ -45,7 +46,7 @@ use crate::ToOwnedDebug; /// assert_eq!(into, &[0; 8]); /// /// // From bytes. -/// let from: u64 = *Storable::from_bytes(&into); +/// let from: u64 = Storable::from_bytes(&into); /// assert_eq!(from, number); /// ``` /// @@ -62,36 +63,7 @@ use crate::ToOwnedDebug; /// /// Most likely, the bytes are little-endian, however /// that cannot be relied upon when using this trait. -pub trait Storable: ToOwnedDebug { - /// What is the alignment of `Self`? - /// - /// For `[T]` types, this is set to the alignment of `T`. - /// - /// This is used to prevent copying when unneeded, e.g. - /// `[u8] -> [u8]` does not need to account for unaligned bytes, - /// since no cast needs to occur. - /// - /// # Examples - /// ```rust - /// # use cuprate_database::Storable; - /// assert_eq!(<()>::ALIGN, 1); - /// assert_eq!(u8::ALIGN, 1); - /// assert_eq!(u16::ALIGN, 2); - /// assert_eq!(u32::ALIGN, 4); - /// assert_eq!(u64::ALIGN, 8); - /// assert_eq!(i8::ALIGN, 1); - /// assert_eq!(i16::ALIGN, 2); - /// assert_eq!(i32::ALIGN, 4); - /// assert_eq!(i64::ALIGN, 8); - /// assert_eq!(<[u8]>::ALIGN, 1); - /// assert_eq!(<[u64]>::ALIGN, 8); - /// assert_eq!(<[u8; 0]>::ALIGN, 1); - /// assert_eq!(<[u8; 1]>::ALIGN, 1); - /// assert_eq!(<[u8; 2]>::ALIGN, 1); - /// assert_eq!(<[u64; 2]>::ALIGN, 8); - /// ``` - const ALIGN: usize; - +pub trait Storable: Debug { /// Is this type fixed width in byte length? /// /// I.e., when converting `Self` to bytes, is it @@ -113,7 +85,7 @@ pub trait Storable: ToOwnedDebug { /// /// # Examples /// ```rust - /// # use cuprate_database::Storable; + /// # use cuprate_database::*; /// assert_eq!(<()>::BYTE_LENGTH, Some(0)); /// assert_eq!(u8::BYTE_LENGTH, Some(1)); /// assert_eq!(u16::BYTE_LENGTH, Some(2)); @@ -123,46 +95,29 @@ pub trait Storable: ToOwnedDebug { /// assert_eq!(i16::BYTE_LENGTH, Some(2)); /// assert_eq!(i32::BYTE_LENGTH, Some(4)); /// assert_eq!(i64::BYTE_LENGTH, Some(8)); - /// assert_eq!(<[u8]>::BYTE_LENGTH, None); - /// assert_eq!(<[u8; 0]>::BYTE_LENGTH, Some(0)); - /// assert_eq!(<[u8; 1]>::BYTE_LENGTH, Some(1)); - /// assert_eq!(<[u8; 2]>::BYTE_LENGTH, Some(2)); - /// assert_eq!(<[u8; 3]>::BYTE_LENGTH, Some(3)); + /// assert_eq!(StorableVec::::BYTE_LENGTH, None); + /// assert_eq!(StorableVec::::BYTE_LENGTH, None); /// ``` const BYTE_LENGTH: Option; /// Return `self` in byte form. fn as_bytes(&self) -> &[u8]; - /// Create a borrowed [`Self`] from bytes. - /// - /// # Invariant - /// `bytes` must be perfectly aligned for `Self` - /// or else this function may cause UB. - /// - /// This function _may_ panic if `bytes` isn't aligned. + /// Create an owned [`Self`] from bytes. /// /// # Blanket implementation /// The blanket implementation that covers all types used - /// by `cuprate_database` will simply cast `bytes` into `Self`, - /// with no copying. - fn from_bytes(bytes: &[u8]) -> &Self; - - /// Create a [`Self`] from potentially unaligned bytes. + /// by `cuprate_database` will simply bitwise copy `bytes` + /// into `Self`. /// - /// # Blanket implementation - /// The blanket implementation that covers all types used - /// by `cuprate_database` will **always** allocate a new buffer - /// or create a new `Self`. - fn from_bytes_unaligned(bytes: &[u8]) -> Cow<'_, Self>; + /// The bytes do not have be correctly aligned. + fn from_bytes(bytes: &[u8]) -> Self; } -//---------------------------------------------------------------------------------------------------- Impl impl Storable for T where - Self: Pod + ToOwnedDebug, + Self: Pod + Debug, { - const ALIGN: usize = std::mem::align_of::(); const BYTE_LENGTH: Option = Some(std::mem::size_of::()); #[inline] @@ -171,37 +126,132 @@ where } #[inline] - fn from_bytes(bytes: &[u8]) -> &T { - bytemuck::from_bytes(bytes) - } - - #[inline] - fn from_bytes_unaligned(bytes: &[u8]) -> Cow<'static, Self> { - Cow::Owned(bytemuck::pod_read_unaligned(bytes)) + fn from_bytes(bytes: &[u8]) -> T { + bytemuck::pod_read_unaligned(bytes) } } -impl Storable for [T] +//---------------------------------------------------------------------------------------------------- StorableVec +/// A [`Storable`] vector of `T: Storable`. +/// +/// This is a wrapper around `Vec where T: Storable`. +/// +/// Slice types are owned both: +/// - when returned from the database +/// - in `put()` +/// +/// This is needed as `impl Storable for Vec` runs into impl conflicts. +/// +/// ```rust +/// # use cuprate_database::*; +/// //---------------------------------------------------- u8 +/// let vec: StorableVec = StorableVec(vec![0,1]); +/// +/// // Into bytes. +/// let into = Storable::as_bytes(&vec); +/// assert_eq!(into, &[0,1]); +/// +/// // From bytes. +/// let from: StorableVec = Storable::from_bytes(&into); +/// assert_eq!(from, vec); +/// +/// //---------------------------------------------------- u64 +/// let vec: StorableVec = StorableVec(vec![0,1]); +/// +/// // Into bytes. +/// let into = Storable::as_bytes(&vec); +/// assert_eq!(into, &[0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0]); +/// +/// // From bytes. +/// let from: StorableVec = Storable::from_bytes(&into); +/// assert_eq!(from, vec); +/// ``` +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, bytemuck::TransparentWrapper)] +#[repr(transparent)] +pub struct StorableVec(pub Vec); + +impl Storable for StorableVec where - T: Pod + ToOwnedDebug, - Self: ToOwnedDebug>, + T: Pod + Debug, { - const ALIGN: usize = std::mem::align_of::(); + const BYTE_LENGTH: Option = None; + + /// Casts the inner `Vec` directly as bytes. + #[inline] + fn as_bytes(&self) -> &[u8] { + bytemuck::must_cast_slice(&self.0) + } + + /// This always allocates a new `Vec`, + /// casting `bytes` into a vector of type `T`. + #[inline] + fn from_bytes(bytes: &[u8]) -> Self { + Self(bytemuck::pod_collect_to_vec(bytes)) + } +} + +impl std::ops::Deref for StorableVec { + type Target = [T]; + #[inline] + fn deref(&self) -> &[T] { + &self.0 + } +} + +impl Borrow<[T]> for StorableVec { + #[inline] + fn borrow(&self) -> &[T] { + &self.0 + } +} + +//---------------------------------------------------------------------------------------------------- StorableBytes +/// A [`Storable`] version of [`Bytes`]. +/// +/// ```rust +/// # use cuprate_database::*; +/// # use bytes::Bytes; +/// let bytes: StorableBytes = StorableBytes(Bytes::from_static(&[0,1])); +/// +/// // Into bytes. +/// let into = Storable::as_bytes(&bytes); +/// assert_eq!(into, &[0,1]); +/// +/// // From bytes. +/// let from: StorableBytes = Storable::from_bytes(&into); +/// assert_eq!(from, bytes); +/// ``` +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +pub struct StorableBytes(pub Bytes); + +impl Storable for StorableBytes { const BYTE_LENGTH: Option = None; #[inline] fn as_bytes(&self) -> &[u8] { - bytemuck::must_cast_slice(self) + &self.0 } + /// This always allocates a new `Bytes`. #[inline] - fn from_bytes(bytes: &[u8]) -> &[T] { - bytemuck::cast_slice(bytes) + fn from_bytes(bytes: &[u8]) -> Self { + Self(Bytes::copy_from_slice(bytes)) } +} +impl std::ops::Deref for StorableBytes { + type Target = [u8]; #[inline] - fn from_bytes_unaligned(bytes: &[u8]) -> Cow<'static, Self> { - Cow::Owned(bytemuck::pod_collect_to_vec(bytes)) + fn deref(&self) -> &[u8] { + &self.0 + } +} + +impl Borrow<[u8]> for StorableBytes { + #[inline] + fn borrow(&self) -> &[u8] { + &self.0 } } @@ -220,7 +270,7 @@ mod test { // A `Vec` of the numbers to test. t: Vec, ) where - T: Storable + Copy + PartialEq, + T: Storable + Debug + Copy + PartialEq, { for t in t { let expected_bytes = to_le_bytes(t); @@ -229,7 +279,7 @@ mod test { // (De)serialize. let se: &[u8] = Storable::as_bytes(&t); - let de: &T = Storable::from_bytes(se); + let de = ::from_bytes(se); println!("serialized: {se:?}, deserialized: {de:?}\n"); @@ -238,7 +288,7 @@ mod test { assert_eq!(se.len(), expected_bytes.len()); } // Assert the data is the same. - assert_eq!(de, &t); + assert_eq!(de, t); } } diff --git a/database/src/table.rs b/database/src/table.rs index 723d883..e117dc1 100644 --- a/database/src/table.rs +++ b/database/src/table.rs @@ -3,7 +3,7 @@ //---------------------------------------------------------------------------------------------------- Import use std::fmt::Debug; -use crate::{key::Key, storable::Storable, to_owned_debug::ToOwnedDebug}; +use crate::{key::Key, storable::Storable}; //---------------------------------------------------------------------------------------------------- Table /// Database table metadata. @@ -19,10 +19,10 @@ pub trait Table: crate::tables::private::Sealed + 'static { const NAME: &'static str; /// Primary key type. - type Key: Key + ?Sized + 'static; + type Key: Key + 'static; /// Value type. - type Value: Storable + ?Sized + 'static; + type Value: Storable + 'static; } //---------------------------------------------------------------------------------------------------- Tests diff --git a/database/src/to_owned_debug.rs b/database/src/to_owned_debug.rs deleted file mode 100644 index e8c67cf..0000000 --- a/database/src/to_owned_debug.rs +++ /dev/null @@ -1,51 +0,0 @@ -//! Borrowed/owned data abstraction; `trait ToOwnedDebug`. - -//---------------------------------------------------------------------------------------------------- Import -use std::fmt::Debug; - -use crate::{key::Key, storable::Storable}; - -//---------------------------------------------------------------------------------------------------- Table -/// `T: Debug` and `T::Owned: Debug`. -/// -/// This trait simply combines [`Debug`] and [`ToOwned`] -/// such that the `Owned` version must also be [`Debug`]. -/// -/// An example is `[u8]` which is [`Debug`], and -/// its owned version `Vec` is also [`Debug`]. -/// -/// # Explanation (not needed for practical use) -/// This trait solely exists due to the `redb` backend -/// requiring [`Debug`] bounds on keys and values. -/// -/// As we have `?Sized` types like `[u8]`, and due to `redb` requiring -/// allocation upon deserialization, we must make our values `ToOwned`. -/// -/// However, this requires that the `Owned` version is also `Debug`. -/// Combined with: -/// - [`Table::Key`](crate::Table::Key) -/// - [`Table::Value`](crate::Table::Value) -/// - [`Key::Primary`] -/// -/// this quickly permutates into many many many `where` bounds on -/// each function that touchs any data that must be deserialized. -/// -/// This trait and the blanket impl it provides get applied all these types -/// automatically, which means we don't have to write these bounds everywhere. -pub trait ToOwnedDebug: Debug + ToOwned { - /// The owned version of [`Self`]. - /// - /// Should be equal to `::Owned`. - type OwnedDebug: Debug; -} - -// The blanket impl that covers all our types. -impl + Debug + ?Sized> ToOwnedDebug for T { - type OwnedDebug = O; -} - -//---------------------------------------------------------------------------------------------------- Tests -#[cfg(test)] -mod test { - // use super::*; -} diff --git a/database/src/types.rs b/database/src/types.rs index 89ffba2..9ca9c59 100644 --- a/database/src/types.rs +++ b/database/src/types.rs @@ -37,7 +37,6 @@ */ // actually i still don't trust you. no unsafe. #![forbid(unsafe_code)] // if you remove this line i will steal your monero -#![allow(missing_docs)] // bytemuck auto-generates some non-documented structs //---------------------------------------------------------------------------------------------------- Import use bytemuck::{AnyBitPattern, NoUninit, Pod, Zeroable}; @@ -45,6 +44,8 @@ use bytemuck::{AnyBitPattern, NoUninit, Pod, Zeroable}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +use crate::storable::StorableVec; + //---------------------------------------------------------------------------------------------------- Aliases // TODO: document these, why they exist, and their purpose. // @@ -58,10 +59,10 @@ pub type Amount = u64; pub type AmountIndex = u64; /// TODO -pub type AmountIndices = [AmountIndex]; +pub type AmountIndices = StorableVec; /// TODO -pub type BlockBlob = [u8]; +pub type BlockBlob = StorableVec; /// TODO pub type BlockHash = [u8; 32]; @@ -73,10 +74,10 @@ pub type BlockHeight = u64; pub type KeyImage = [u8; 32]; /// TODO -pub type PrunedBlob = [u8]; +pub type PrunedBlob = StorableVec; /// TODO -pub type PrunableBlob = [u8]; +pub type PrunableBlob = StorableVec; /// TODO pub type PrunableHash = [u8; 32]; @@ -102,10 +103,8 @@ pub type UnlockTime = u64; /// amount_index: 123, /// }; /// let b = Storable::as_bytes(&a); -/// let c: &PreRctOutputId = Storable::from_bytes(b); -/// let c2: Cow<'_, PreRctOutputId> = Storable::from_bytes_unaligned(b); -/// assert_eq!(&a, c); -/// assert_eq!(c, c2.as_ref()); +/// let c: PreRctOutputId = Storable::from_bytes(b); +/// assert_eq!(a, c); /// ``` /// /// # Size & Alignment @@ -140,10 +139,8 @@ pub struct PreRctOutputId { /// block_hash: [54; 32], /// }; /// let b = Storable::as_bytes(&a); -/// let c: &BlockInfoV1 = Storable::from_bytes(b); -/// let c2: Cow<'_, BlockInfoV1> = Storable::from_bytes_unaligned(b); -/// assert_eq!(&a, c); -/// assert_eq!(c, c2.as_ref()); +/// let c: BlockInfoV1 = Storable::from_bytes(b); +/// assert_eq!(a, c); /// ``` /// /// # Size & Alignment @@ -185,10 +182,8 @@ pub struct BlockInfoV1 { /// cumulative_rct_outs: 2389, /// }; /// let b = Storable::as_bytes(&a); -/// let c: &BlockInfoV2 = Storable::from_bytes(b); -/// let c2: Cow<'_, BlockInfoV2> = Storable::from_bytes_unaligned(b); -/// assert_eq!(&a, c); -/// assert_eq!(c, c2.as_ref()); +/// let c: BlockInfoV2 = Storable::from_bytes(b); +/// assert_eq!(a, c); /// ``` /// /// # Size & Alignment @@ -237,10 +232,8 @@ pub struct BlockInfoV2 { /// long_term_weight: 2389, /// }; /// let b = Storable::as_bytes(&a); -/// let c: &BlockInfoV3 = Storable::from_bytes(b); -/// let c2: Cow<'_, BlockInfoV3> = Storable::from_bytes_unaligned(b); -/// assert_eq!(&a, c); -/// assert_eq!(c, c2.as_ref()); +/// let c: BlockInfoV3 = Storable::from_bytes(b); +/// assert_eq!(a, c); /// ``` /// /// # Size & Alignment @@ -287,10 +280,8 @@ pub struct BlockInfoV3 { /// tx_idx: 3, /// }; /// let b = Storable::as_bytes(&a); -/// let c: &Output = Storable::from_bytes(b); -/// let c2: Cow<'_, Output> = Storable::from_bytes_unaligned(b); -/// assert_eq!(&a, c); -/// assert_eq!(c, c2.as_ref()); +/// let c: Output = Storable::from_bytes(b); +/// assert_eq!(a, c); /// ``` /// /// # Size & Alignment @@ -329,10 +320,8 @@ pub struct Output { /// commitment: [3; 32], /// }; /// let b = Storable::as_bytes(&a); -/// let c: &RctOutput = Storable::from_bytes(b); -/// let c2: Cow<'_, RctOutput> = Storable::from_bytes_unaligned(b); -/// assert_eq!(&a, c); -/// assert_eq!(c, c2.as_ref()); +/// let c: RctOutput = Storable::from_bytes(b); +/// assert_eq!(a, c); /// ``` /// /// # Size & Alignment diff --git a/database/src/value_guard.rs b/database/src/value_guard.rs deleted file mode 100644 index 8dda43b..0000000 --- a/database/src/value_guard.rs +++ /dev/null @@ -1,47 +0,0 @@ -//! Database table value "guard" abstraction; `trait ValueGuard`. - -//---------------------------------------------------------------------------------------------------- Import -use std::borrow::{Borrow, Cow}; - -use crate::{table::Table, Storable, ToOwnedDebug}; - -//---------------------------------------------------------------------------------------------------- Table -/// A guard that allows you to access a value. -/// -/// This trait acts as an object that must be kept alive, -/// and will give you access to a [`Table`]'s value. -/// -/// # Explanation (not needed for practical use) -/// This trait solely exists due to the `redb` backend -/// not _directly_ returning the value, but a -/// [guard object](https://docs.rs/redb/1.5.0/redb/struct.AccessGuard.html) -/// that has a lifetime attached to the key. -/// It does not implement `Deref` or `Borrow` and such. -/// -/// Also, due to `redb` requiring `Cow`, this object builds on that. -/// -/// - `heed` will always be `Cow::Borrowed` -/// - `redb` will always be `Cow::Borrowed` for `[u8]` -/// or any type where `Storable::ALIGN == 1` -/// - `redb` will always be `Cow::Owned` for everything else -pub trait ValueGuard { - /// Retrieve the data from the guard. - fn unguard(&self) -> Cow<'_, T>; -} - -impl ValueGuard for Cow<'_, T> { - #[inline] - fn unguard(&self) -> Cow<'_, T> { - Cow::Borrowed(self.borrow()) - } -} - -// HACK: -// This is implemented for `redb::AccessGuard<'_>` in -// `src/backend/redb/storable.rs` due to struct privacy. - -//---------------------------------------------------------------------------------------------------- Tests -#[cfg(test)] -mod test { - // use super::*; -}