Skip to content

Commit

Permalink
Add wrapper type to handle keys and values based on bincode serializa…
Browse files Browse the repository at this point in the history
…tion
  • Loading branch information
adamreichold committed May 8, 2024
1 parent cae8038 commit 8d5f237
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 4 deletions.
13 changes: 9 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ crate-type = ["cdylib", "rlib"]
pyo3-build-config = { version = "0.20.0", optional = true }

[dependencies]
log = {version = "0.4.17", optional = true }
pyo3 = {version = "0.20.0", features=["extension-module", "abi3-py37"], optional = true }
log = { version = "0.4.17", optional = true }
pyo3 = { version = "0.20.0", features=["extension-module", "abi3-py37"], optional = true }
bincode = { version = "1.3.3", optional = true }
serde = { version = "1.0", optional = true }

[target.'cfg(unix)'.dependencies]
libc = "0.2.104"
Expand All @@ -32,6 +34,7 @@ tempfile = "3.5.0"
redb1 = { version = "=1.0.0", package = "redb" }
# for backwards compatibility testing - pin at 2.0.0
redb2 = { version = "=2.0.0", package = "redb" }
serde = { version = "1.0", features = ["derive"] }

# Just benchmarking dependencies
[target.'cfg(not(target_os = "wasi"))'.dev-dependencies]
Expand All @@ -51,11 +54,13 @@ io-uring = "0.6.2"

[features]
# This feature is still experimental, and is not considered stable
python = [ "pyo3", "pyo3-build-config" ]
python = ["dep:pyo3", "dep:pyo3-build-config"]
# Enables log messages
logging = ["log"]
logging = ["dep:log"]
# Enable cache hit metrics
cache_metrics = []
# Support keys and values based bincode serialization
bincode = ["dep:bincode", "dep:serde"]

[profile.bench]
debug = true
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ pub use table::{
};
pub use transactions::{DatabaseStats, Durability, ReadTransaction, WriteTransaction};
pub use tree_store::{AccessGuard, AccessGuardMut, Savepoint};
#[cfg(feature = "bincode")]
pub use types::Bincode;
pub use types::{Key, MutInPlaceValue, TypeName, Value};

pub type Result<T = (), E = StorageError> = std::result::Result<T, E>;
Expand Down
52 changes: 52 additions & 0 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -622,3 +622,55 @@ le_impl!(i64);
le_impl!(i128);
le_value!(f32);
le_value!(f64);

#[cfg(feature = "bincode")]
/// Wrapper type to handle keys and values using bincode serialization
#[derive(Debug)]
pub struct Bincode<T>(pub T);

#[cfg(feature = "bincode")]
impl<T> Value for Bincode<T>
where
T: Debug + serde::Serialize + for<'a> serde::Deserialize<'a>,
{
type SelfType<'a> = T
where
Self: 'a;

type AsBytes<'a> = Vec<u8>
where
Self: 'a;

fn fixed_width() -> Option<usize> {
None
}

fn from_bytes<'a>(data: &'a [u8]) -> Self::SelfType<'a>
where
Self: 'a,
{
bincode::deserialize(data).unwrap()
}

fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> Self::AsBytes<'a>
where
Self: 'a,
Self: 'b,
{
bincode::serialize(value).unwrap()
}

fn type_name() -> TypeName {
TypeName::internal(&format!("Bincode<{}>", std::any::type_name::<T>()))
}
}

#[cfg(feature = "bincode")]
impl<T> Key for Bincode<T>
where
T: Debug + serde::Serialize + for<'a> serde::Deserialize<'a> + Ord,
{
fn compare(data1: &[u8], data2: &[u8]) -> Ordering {
Self::from_bytes(data1).cmp(&Self::from_bytes(data2))
}
}
62 changes: 62 additions & 0 deletions tests/basic_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1739,3 +1739,65 @@ fn u8_array_serialization() {
assert_eq!(ref_order, generic_order);
}
}

#[cfg(feature = "bincode")]
#[test]
fn bincode_type() {
use redb::Bincode;
use serde::{Deserialize, Serialize};

let tmpfile = create_tempfile();
let db = Database::create(tmpfile.path()).unwrap();

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
struct SomeKey {
foo: String,
bar: i32,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct SomeValue {
foo: [f64; 3],
bar: bool,
}

let definition: TableDefinition<Bincode<SomeKey>, Bincode<SomeValue>> =
TableDefinition::new("x");

let some_key = SomeKey {
foo: "hello world".to_string(),
bar: 42,
};
let some_value = SomeValue {
foo: [1., 2., 3.],
bar: true,
};

let write_txn = db.begin_write().unwrap();
{
let mut table = write_txn.open_table(definition).unwrap();
table.insert(&some_key, &some_value).unwrap();
}
write_txn.commit().unwrap();

let read_txn = db.begin_read().unwrap();
let table = read_txn.open_table(definition).unwrap();
assert_eq!(some_value, table.get(&some_key).unwrap().unwrap().value());

let mut iter = table.iter().unwrap();
assert_eq!(iter.next().unwrap().unwrap().1.value(), some_value);
assert!(iter.next().is_none());

let lower = SomeKey {
foo: "a".to_string(),
bar: 42,
};
let upper = SomeKey {
foo: "z".to_string(),
bar: 42,
};

let mut iter: Range<Bincode<SomeKey>, Bincode<SomeValue>> = table.range(lower..upper).unwrap();
assert_eq!(iter.next().unwrap().unwrap().1.value(), some_value);
assert!(iter.next().is_none());
}

0 comments on commit 8d5f237

Please sign in to comment.