Overhaul a bit to make it more ergonomic and use newer libMDBX
This commit is contained in:
parent
2554674a47
commit
b64bc5eb09
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@
|
|||||||
Cargo.lock
|
Cargo.lock
|
||||||
|
|
||||||
.idea
|
.idea
|
||||||
|
.direnv
|
12
Cargo.toml
12
Cargo.toml
@ -4,7 +4,7 @@ description = "Fancy wrapper for libMDBX"
|
|||||||
repository = "https://bics.ga/reivilibre/fancy_mdbx.git"
|
repository = "https://bics.ga/reivilibre/fancy_mdbx.git"
|
||||||
authors = ["Olivier 'reivilibre'"]
|
authors = ["Olivier 'reivilibre'"]
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
version = "0.1.1"
|
version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
@ -16,16 +16,12 @@ log = "0.4.16"
|
|||||||
|
|
||||||
thiserror = "1.0.30"
|
thiserror = "1.0.30"
|
||||||
|
|
||||||
libmdbx = "0.1.1"
|
libmdbx = "0.3.3"
|
||||||
|
|
||||||
# 0.11.1+zstd.1.5.2
|
|
||||||
zstd = "0.11.1"
|
|
||||||
|
|
||||||
serde = "1.0.136"
|
|
||||||
serde_bare = "0.5.0"
|
|
||||||
|
|
||||||
ouroboros = "0.14.2"
|
ouroboros = "0.14.2"
|
||||||
|
|
||||||
|
byte_lamination = "0.1.1"
|
||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3.3.0"
|
tempfile = "3.3.0"
|
||||||
|
58
flake.lock
generated
Normal file
58
flake.lock
generated
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1677075010,
|
||||||
|
"narHash": "sha256-X+UmR1AkdR//lPVcShmLy8p1n857IGf7y+cyCArp8bU=",
|
||||||
|
"path": "/nix/store/b1vy558z7lxph5mbg7n50b5njp393ia9-source",
|
||||||
|
"rev": "c95bf18beba4290af25c60cbaaceea1110d0f727",
|
||||||
|
"type": "path"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "nixpkgs",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"utils": "utils"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681202837,
|
||||||
|
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
45
flake.nix
Normal file
45
flake.nix
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
description = "fancy_mdbx";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
utils.url = "github:numtide/flake-utils";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, utils }:
|
||||||
|
utils.lib.eachDefaultSystem (system: let
|
||||||
|
pkgs = nixpkgs.legacyPackages."${system}";
|
||||||
|
in rec {
|
||||||
|
# `nix develop`
|
||||||
|
devShell = pkgs.mkShell {
|
||||||
|
nativeBuildInputs =
|
||||||
|
let
|
||||||
|
rust-toolchain = pkgs.symlinkJoin {
|
||||||
|
name = "rust-toolchain";
|
||||||
|
paths = with pkgs; [rustc cargo rustfmt rustPlatform.rustcSrc];
|
||||||
|
};
|
||||||
|
in
|
||||||
|
[
|
||||||
|
rust-toolchain
|
||||||
|
];
|
||||||
|
|
||||||
|
# Needed for bindgen when binding to mdbx
|
||||||
|
LIBCLANG_PATH="${pkgs.llvmPackages_latest.libclang.lib}/lib";
|
||||||
|
|
||||||
|
# Cargo culted:
|
||||||
|
# Add to rustc search path
|
||||||
|
RUSTFLAGS = (builtins.map (a: ''-L ${a}/lib'') [
|
||||||
|
]);
|
||||||
|
# Add to bindgen search path
|
||||||
|
BINDGEN_EXTRA_CLANG_ARGS =
|
||||||
|
# Includes with normal include path
|
||||||
|
(builtins.map (a: ''-I"${a}/include"'') [
|
||||||
|
# C standard library
|
||||||
|
pkgs.glibc.dev
|
||||||
|
])
|
||||||
|
# Includes with special directory paths
|
||||||
|
++ [
|
||||||
|
''-I"${pkgs.llvmPackages_latest.libclang.lib}/lib/clang/${pkgs.llvmPackages_latest.libclang.version}/include"''
|
||||||
|
];
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
@ -1,58 +0,0 @@
|
|||||||
use crate::environment::Txn;
|
|
||||||
use crate::wrapper::ZeroCopyByteWrapper;
|
|
||||||
use libmdbx::{Database, Environment, TransactionKind, WriteFlags, WriteMap, RW};
|
|
||||||
use ouroboros::self_referencing;
|
|
||||||
use std::borrow::Cow;
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
#[self_referencing(pub_extras)]
|
|
||||||
pub struct RawTable<K: ?Sized, V: ?Sized> {
|
|
||||||
pub mdbx_env: Arc<Environment<WriteMap>>,
|
|
||||||
|
|
||||||
#[borrows(mdbx_env)]
|
|
||||||
#[covariant]
|
|
||||||
pub mdbx_db: Database<'this>,
|
|
||||||
|
|
||||||
pub(crate) phantom_k: PhantomData<K>,
|
|
||||||
pub(crate) phantom_v: PhantomData<V>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K: ?Sized + ZeroCopyByteWrapper, V: ?Sized + ZeroCopyByteWrapper> RawTable<K, V> {
|
|
||||||
pub fn get<'txn, TK: TransactionKind>(
|
|
||||||
&'txn self,
|
|
||||||
txn: &'txn Txn<'txn, TK>,
|
|
||||||
k: impl AsRef<K>,
|
|
||||||
) -> anyhow::Result<Option<Cow<'txn, V>>> {
|
|
||||||
let bytes = txn
|
|
||||||
.mdbx_txn
|
|
||||||
.get::<Cow<'txn, [u8]>>(self.borrow_mdbx_db(), k.as_ref().as_byte_slice())?;
|
|
||||||
Ok(match bytes {
|
|
||||||
None => None,
|
|
||||||
Some(Cow::Owned(owned)) => Some(Cow::Owned(V::from_owned_bytes(owned)?)),
|
|
||||||
Some(Cow::Borrowed(borrowed)) => Some(Cow::Borrowed(V::from_byte_slice(borrowed)?)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn put<'txn>(
|
|
||||||
&self,
|
|
||||||
txn: &Txn<'txn, RW>,
|
|
||||||
k: impl AsRef<K>,
|
|
||||||
v: impl AsRef<V>,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
txn.mdbx_txn.put(
|
|
||||||
self.borrow_mdbx_db(),
|
|
||||||
k.as_ref().as_byte_slice(),
|
|
||||||
v.as_ref().as_byte_slice(),
|
|
||||||
WriteFlags::empty(),
|
|
||||||
)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the key was present.
|
|
||||||
pub fn delete<'txn>(&self, txn: &Txn<'txn, RW>, k: impl AsRef<K>) -> anyhow::Result<bool> {
|
|
||||||
Ok(txn
|
|
||||||
.mdbx_txn
|
|
||||||
.del(self.borrow_mdbx_db(), k.as_ref().as_byte_slice(), None)?)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
use crate::database::raw::RawTable;
|
|
||||||
use crate::environment::Txn;
|
|
||||||
use crate::wrapper::ByteWrapper;
|
|
||||||
use anyhow::Context;
|
|
||||||
use libmdbx::{TransactionKind, RW};
|
|
||||||
use std::borrow::Borrow;
|
|
||||||
|
|
||||||
pub struct WrappedTable<K, V> {
|
|
||||||
pub raw: RawTable<[u8], [u8]>,
|
|
||||||
pub k_wrapper: K,
|
|
||||||
pub v_wrapper: V,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K: ByteWrapper, V: ByteWrapper> WrappedTable<K, V> {
|
|
||||||
pub fn get<'txn, TK: TransactionKind>(
|
|
||||||
&self,
|
|
||||||
txn: &Txn<'txn, TK>,
|
|
||||||
k: impl Borrow<K::Item>,
|
|
||||||
) -> anyhow::Result<Option<V::Item>> {
|
|
||||||
let k_bytes = self
|
|
||||||
.k_wrapper
|
|
||||||
.dump_to_db_bytes(k.borrow())
|
|
||||||
.context("whilst converting key to bytes")?;
|
|
||||||
|
|
||||||
self.raw
|
|
||||||
.get(txn, k_bytes)?
|
|
||||||
.map(|v_bytes| self.v_wrapper.load_from_db_bytes(&v_bytes))
|
|
||||||
.transpose()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn put<'txn>(
|
|
||||||
&self,
|
|
||||||
txn: &Txn<'txn, RW>,
|
|
||||||
k: impl Borrow<K::Item>,
|
|
||||||
v: impl Borrow<V::Item>,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
let k_bytes = self
|
|
||||||
.k_wrapper
|
|
||||||
.dump_to_db_bytes(k.borrow())
|
|
||||||
.context("whilst converting key to bytes")?;
|
|
||||||
let v_bytes = self
|
|
||||||
.v_wrapper
|
|
||||||
.dump_to_db_bytes(v.borrow())
|
|
||||||
.context("whilst converting value to bytes")?;
|
|
||||||
|
|
||||||
self.raw.put(txn, k_bytes, v_bytes)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the key was present.
|
|
||||||
pub fn delete<'txn>(
|
|
||||||
&self,
|
|
||||||
txn: &Txn<'txn, RW>,
|
|
||||||
k: impl Borrow<K::Item>,
|
|
||||||
) -> anyhow::Result<bool> {
|
|
||||||
let k_bytes = self
|
|
||||||
.k_wrapper
|
|
||||||
.dump_to_db_bytes(k.borrow())
|
|
||||||
.context("whilst converting key to bytes")?;
|
|
||||||
|
|
||||||
self.raw.delete(txn, k_bytes)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +1,20 @@
|
|||||||
use crate::database::raw::{RawTable, RawTableTryBuilder};
|
|
||||||
use crate::database::wrapped::WrappedTable;
|
|
||||||
use crate::wrapper::{ByteWrapper, ZeroCopyByteWrapper};
|
|
||||||
use anyhow::{ensure, Context};
|
use anyhow::{ensure, Context};
|
||||||
use libmdbx::{
|
use libmdbx::{
|
||||||
DatabaseFlags, Environment, EnvironmentFlags, Geometry, Transaction, TransactionKind, WriteMap,
|
Database, DatabaseFlags, Geometry, TableFlags, Transaction, TransactionKind, WriteMap, RO, RW,
|
||||||
RO, RW,
|
|
||||||
};
|
};
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
use crate::table::{raw::RawTableTryBuilder, RawTable, Table};
|
||||||
|
use byte_lamination::ByteLamination;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub struct Env {
|
pub struct Db {
|
||||||
pub mdbx_env: Arc<Environment<WriteMap>>,
|
pub mdbx_env: Arc<Database<WriteMap>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Env {
|
impl Db {
|
||||||
pub fn open(path: &Path) -> anyhow::Result<Env> {
|
pub fn open(path: &Path) -> anyhow::Result<Self> {
|
||||||
let mut flags = EnvironmentFlags::default();
|
let mut flags = DatabaseFlags::default();
|
||||||
flags.no_sub_dir = true;
|
flags.no_sub_dir = true;
|
||||||
|
|
||||||
// TODO make the geometry more configurable.
|
// TODO make the geometry more configurable.
|
||||||
@ -31,31 +29,27 @@ impl Env {
|
|||||||
geom.shrink_threshold = Some(16 * 1024 * 1024);
|
geom.shrink_threshold = Some(16 * 1024 * 1024);
|
||||||
// (Yes these numbers represent a large database).
|
// (Yes these numbers represent a large database).
|
||||||
|
|
||||||
let environment = Environment::new()
|
let environment = Database::new()
|
||||||
.set_max_dbs(256)
|
.set_max_tables(256)
|
||||||
.set_geometry(geom)
|
.set_geometry(geom)
|
||||||
.set_flags(flags)
|
.set_flags(flags)
|
||||||
.open(path)?;
|
.open(path)?;
|
||||||
|
|
||||||
Ok(Env {
|
Ok(Db {
|
||||||
mdbx_env: Arc::new(environment),
|
mdbx_env: Arc::new(environment),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_raw_table<K: ZeroCopyByteWrapper + ?Sized, V: ZeroCopyByteWrapper + ?Sized>(
|
pub fn open_raw_table(&self, name: Option<&str>, _flags: ()) -> anyhow::Result<RawTable> {
|
||||||
&self,
|
|
||||||
name: Option<&str>,
|
|
||||||
_flags: (),
|
|
||||||
) -> anyhow::Result<RawTable<K, V>> {
|
|
||||||
Ok(RawTableTryBuilder {
|
Ok(RawTableTryBuilder {
|
||||||
mdbx_env: self.mdbx_env.clone(),
|
mdbx_env: self.mdbx_env.clone(),
|
||||||
mdbx_db_builder: |mdbx_env: &Arc<Environment<WriteMap>>| {
|
mdbx_db_builder: |mdbx_env: &Arc<Database<WriteMap>>| {
|
||||||
let txn = mdbx_env
|
let txn = mdbx_env
|
||||||
.begin_rw_txn()
|
.begin_rw_txn()
|
||||||
.context("Can't start RW transaction")?;
|
.context("Can't start RW transaction")?;
|
||||||
// TODO database flags
|
// TODO database flags
|
||||||
let db = txn
|
let db = txn
|
||||||
.create_db(name, DatabaseFlags::empty())
|
.create_table(name, TableFlags::empty())
|
||||||
.context("Can't open database")?;
|
.context("Can't open database")?;
|
||||||
txn.prime_for_permaopen(db);
|
txn.prime_for_permaopen(db);
|
||||||
|
|
||||||
@ -67,25 +61,20 @@ impl Env {
|
|||||||
|
|
||||||
Ok(mdbx_db)
|
Ok(mdbx_db)
|
||||||
},
|
},
|
||||||
phantom_k: PhantomData::default(),
|
|
||||||
phantom_v: PhantomData::default(),
|
|
||||||
}
|
}
|
||||||
.try_build()?)
|
.try_build()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_wrapped_table<K: ByteWrapper, V: ByteWrapper>(
|
pub fn open_wrapped_table<K: for<'a> ByteLamination<'a>, V: for<'a> ByteLamination<'a>>(
|
||||||
&self,
|
&self,
|
||||||
name: Option<&str>,
|
name: Option<&str>,
|
||||||
flags: (),
|
flags: (),
|
||||||
k_wrapper: K,
|
) -> anyhow::Result<Table<K, V>> {
|
||||||
v_wrapper: V,
|
|
||||||
) -> anyhow::Result<WrappedTable<K, V>> {
|
|
||||||
let raw_table = self.open_raw_table(name, flags)?;
|
let raw_table = self.open_raw_table(name, flags)?;
|
||||||
|
|
||||||
Ok(WrappedTable {
|
Ok(Table {
|
||||||
raw: raw_table,
|
raw: raw_table,
|
||||||
k_wrapper,
|
_marker: Default::default(),
|
||||||
v_wrapper,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
pub mod database;
|
|
||||||
pub mod environment;
|
pub mod environment;
|
||||||
pub mod wrapper;
|
pub mod table;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
@ -2,4 +2,4 @@ pub(crate) mod raw;
|
|||||||
pub(crate) mod wrapped;
|
pub(crate) mod wrapped;
|
||||||
|
|
||||||
pub use raw::RawTable;
|
pub use raw::RawTable;
|
||||||
pub use wrapped::WrappedTable;
|
pub use wrapped::Table;
|
47
src/table/raw.rs
Normal file
47
src/table/raw.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
use libmdbx::{Database, Table, TransactionKind, WriteFlags, WriteMap, RW};
|
||||||
|
use ouroboros::self_referencing;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
use crate::environment::Txn;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
#[self_referencing(pub_extras)]
|
||||||
|
pub struct RawTable {
|
||||||
|
pub mdbx_env: Arc<Database<WriteMap>>,
|
||||||
|
|
||||||
|
#[borrows(mdbx_env)]
|
||||||
|
#[covariant]
|
||||||
|
pub mdbx_db: Table<'this>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RawTable {
|
||||||
|
pub fn get<'txn, TK: TransactionKind>(
|
||||||
|
&'txn self,
|
||||||
|
txn: &'txn Txn<'txn, TK>,
|
||||||
|
k: &[u8],
|
||||||
|
) -> anyhow::Result<Option<Cow<'txn, [u8]>>> {
|
||||||
|
txn.mdbx_txn
|
||||||
|
.get::<Cow<'txn, [u8]>>(self.borrow_mdbx_db(), k.as_ref())
|
||||||
|
.map_err(|e| e.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put<'txn>(
|
||||||
|
&self,
|
||||||
|
txn: &Txn<'txn, RW>,
|
||||||
|
k: impl AsRef<[u8]>,
|
||||||
|
v: impl AsRef<[u8]>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
txn.mdbx_txn.put(
|
||||||
|
self.borrow_mdbx_db(),
|
||||||
|
k.as_ref(),
|
||||||
|
v.as_ref(),
|
||||||
|
WriteFlags::empty(),
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the key was present.
|
||||||
|
pub fn delete<'txn>(&self, txn: &Txn<'txn, RW>, k: impl AsRef<[u8]>) -> anyhow::Result<bool> {
|
||||||
|
Ok(txn.mdbx_txn.del(self.borrow_mdbx_db(), k.as_ref(), None)?)
|
||||||
|
}
|
||||||
|
}
|
114
src/table/wrapped.rs
Normal file
114
src/table/wrapped.rs
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
use crate::table::raw::RawTable;
|
||||||
|
|
||||||
|
use crate::environment::Txn;
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use byte_lamination::ByteLamination;
|
||||||
|
use libmdbx::{TransactionKind, RW};
|
||||||
|
use std::borrow::{Borrow, Cow};
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
pub struct Table<K, V> {
|
||||||
|
pub raw: RawTable,
|
||||||
|
pub _marker: PhantomData<(K, V)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: for<'a> ByteLamination<'a>, V: for<'a> ByteLamination<'a>> Table<K, V> {
|
||||||
|
pub fn get<'txn, 'qk, QK: ByteLaminationHelper<'qk, K>, TK: TransactionKind>(
|
||||||
|
&self,
|
||||||
|
txn: &Txn<'txn, TK>,
|
||||||
|
k: QK,
|
||||||
|
) -> anyhow::Result<Option<V>> {
|
||||||
|
let k_bytes = k.as_cow_bytes_helper();
|
||||||
|
|
||||||
|
let val_opt = self.raw.get(txn, &k_bytes)?;
|
||||||
|
|
||||||
|
match val_opt {
|
||||||
|
Some(cow_bytes) => {
|
||||||
|
let value = V::try_from_bytes(cow_bytes)
|
||||||
|
.map_err(|e| anyhow!("failed to convert value bytes to value: {e:?}"))?;
|
||||||
|
Ok(Some(value))
|
||||||
|
}
|
||||||
|
None => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put<
|
||||||
|
'txn,
|
||||||
|
'qk,
|
||||||
|
'qv,
|
||||||
|
QK: ByteLaminationHelper<'qk, K>,
|
||||||
|
QV: ByteLaminationHelper<'qv, V>,
|
||||||
|
>(
|
||||||
|
&self,
|
||||||
|
txn: &Txn<'txn, RW>,
|
||||||
|
k: QK,
|
||||||
|
v: QV,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let k_bytes = k.as_cow_bytes_helper();
|
||||||
|
let v_bytes = v.as_cow_bytes_helper();
|
||||||
|
self.raw.put(txn, &k_bytes, &v_bytes)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the key was present.
|
||||||
|
pub fn delete<'txn>(
|
||||||
|
&self,
|
||||||
|
txn: &Txn<'txn, RW>,
|
||||||
|
k: &(impl Borrow<K> + ?Sized),
|
||||||
|
) -> anyhow::Result<bool> {
|
||||||
|
let k_bytes = k.borrow().as_cow_bytes();
|
||||||
|
|
||||||
|
self.raw.delete(txn, &k_bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `T`: a ByteLamination that this type is equivalent to.
|
||||||
|
pub trait ByteLaminationHelper<'a, T: ByteLamination<'a>> {
|
||||||
|
fn as_cow_bytes_helper(&self) -> Cow<'_, [u8]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: ByteLamination<'a>> ByteLaminationHelper<'a, T> for T {
|
||||||
|
fn as_cow_bytes_helper(&self) -> Cow<'_, [u8]> {
|
||||||
|
self.as_cow_bytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b, T: ByteLamination<'a>> ByteLaminationHelper<'a, T> for &'b T {
|
||||||
|
fn as_cow_bytes_helper(&self) -> Cow<'_, [u8]> {
|
||||||
|
self.as_cow_bytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// can't get rust happy with this...
|
||||||
|
// impl<'a, T: ?Sized + ToOwned> ByteLaminationHelper<'a, Cow<'a, T>> for &'a T
|
||||||
|
// where for<'b> Cow<'b, T>: ByteLamination<'b> {
|
||||||
|
// fn as_cow_bytes_helper(&self) -> Cow<'_, [u8]> {
|
||||||
|
// let x: Cow<'a, T> = Cow::Borrowed(self);
|
||||||
|
// let y: &'a Cow<'a, T> = &x;
|
||||||
|
// Cow::<'_, T>::as_cow_bytes(y)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
impl<'a> ByteLaminationHelper<'a, Cow<'a, str>> for &'a str {
|
||||||
|
fn as_cow_bytes_helper(&self) -> Cow<'_, [u8]> {
|
||||||
|
Cow::Borrowed(self.as_bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ByteLaminationHelper<'a, String> for &'a str {
|
||||||
|
fn as_cow_bytes_helper(&self) -> Cow<'_, [u8]> {
|
||||||
|
Cow::Borrowed(self.as_bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ByteLaminationHelper<'a, Cow<'a, [u8]>> for &'a [u8] {
|
||||||
|
fn as_cow_bytes_helper(&self) -> Cow<'_, [u8]> {
|
||||||
|
Cow::Borrowed(&self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ByteLaminationHelper<'a, Vec<u8>> for &'a [u8] {
|
||||||
|
fn as_cow_bytes_helper(&self) -> Cow<'_, [u8]> {
|
||||||
|
Cow::Borrowed(&self)
|
||||||
|
}
|
||||||
|
}
|
16
src/tests.rs
16
src/tests.rs
@ -1,15 +1,15 @@
|
|||||||
use crate::environment::Env;
|
use crate::environment::Db;
|
||||||
use tempfile::NamedTempFile;
|
use tempfile::NamedTempFile;
|
||||||
|
|
||||||
fn temp_env() -> Env {
|
fn temp_env() -> Db {
|
||||||
let tempfile = NamedTempFile::new().unwrap();
|
let tempfile = NamedTempFile::new().unwrap();
|
||||||
Env::open(tempfile.path()).unwrap()
|
Db::open(tempfile.path()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_raw_bytes_basics() {
|
fn test_raw_bytes_basics() {
|
||||||
let env = temp_env();
|
let env = temp_env();
|
||||||
let db = env.open_raw_table::<[u8], [u8]>(Some("rawt"), ()).unwrap();
|
let db = env.open_raw_table(Some("rawt"), ()).unwrap();
|
||||||
|
|
||||||
let k: [u8; 4] = [0, 1, 2, 3];
|
let k: [u8; 4] = [0, 1, 2, 3];
|
||||||
|
|
||||||
@ -30,9 +30,11 @@ fn test_raw_bytes_basics() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_raw_str_basics() {
|
fn test_wrapped_str_basics() {
|
||||||
let env = temp_env();
|
let env = temp_env();
|
||||||
let db = env.open_raw_table::<str, str>(Some("rawt"), ()).unwrap();
|
let db = env
|
||||||
|
.open_wrapped_table::<String, String>(Some("rawt"), ())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
{
|
{
|
||||||
let txn = env.ro_txn().unwrap();
|
let txn = env.ro_txn().unwrap();
|
||||||
@ -43,6 +45,6 @@ fn test_raw_str_basics() {
|
|||||||
|
|
||||||
{
|
{
|
||||||
let txn = env.ro_txn().unwrap();
|
let txn = env.ro_txn().unwrap();
|
||||||
assert_eq!(db.get(&txn, "ooh").unwrap().unwrap().as_ref(), "cool!");
|
assert_eq!(db.get(&txn, "ooh").unwrap().unwrap().as_str(), "cool!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
147
src/wrapper.rs
147
src/wrapper.rs
@ -1,147 +0,0 @@
|
|||||||
use anyhow::anyhow;
|
|
||||||
use serde::de::DeserializeOwned;
|
|
||||||
use serde::Serialize;
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
use zstd::bulk::{Compressor, Decompressor};
|
|
||||||
|
|
||||||
pub trait ByteWrapper {
|
|
||||||
type Item;
|
|
||||||
|
|
||||||
fn load_from_db_bytes(&self, bytes: &[u8]) -> anyhow::Result<Self::Item>;
|
|
||||||
|
|
||||||
fn dump_to_db_bytes(&self, item: &Self::Item) -> anyhow::Result<Vec<u8>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SerdeBareWrapper<T> {
|
|
||||||
phantom: PhantomData<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Default for SerdeBareWrapper<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
SerdeBareWrapper {
|
|
||||||
phantom: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Serialize + DeserializeOwned> ByteWrapper for SerdeBareWrapper<T> {
|
|
||||||
type Item = T;
|
|
||||||
|
|
||||||
fn load_from_db_bytes(&self, bytes: &[u8]) -> anyhow::Result<Self::Item> {
|
|
||||||
Ok(serde_bare::from_slice(bytes)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dump_to_db_bytes(&self, item: &Self::Item) -> anyhow::Result<Vec<u8>> {
|
|
||||||
Ok(serde_bare::to_vec(item)?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CompressorWrapper<T> {
|
|
||||||
inner: T,
|
|
||||||
compressor: Mutex<Compressor<'static>>,
|
|
||||||
decompressor: Mutex<Decompressor<'static>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ByteWrapper> ByteWrapper for CompressorWrapper<T> {
|
|
||||||
type Item = T::Item;
|
|
||||||
|
|
||||||
fn load_from_db_bytes(&self, bytes: &[u8]) -> anyhow::Result<Self::Item> {
|
|
||||||
let mut decompressor = self
|
|
||||||
.decompressor
|
|
||||||
.lock()
|
|
||||||
.map_err(|_| anyhow!("Can't lock decompressor"))?;
|
|
||||||
// TODO be more sensible about capacity. 8× should be good though.
|
|
||||||
let raw_bytes = decompressor.decompress(bytes, bytes.len() * 8)?;
|
|
||||||
Ok(self.inner.load_from_db_bytes(&raw_bytes)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dump_to_db_bytes(&self, item: &Self::Item) -> anyhow::Result<Vec<u8>> {
|
|
||||||
let raw_bytes = self.inner.dump_to_db_bytes(item)?;
|
|
||||||
let mut compressor = self
|
|
||||||
.compressor
|
|
||||||
.lock()
|
|
||||||
.map_err(|_| anyhow!("Can't lock compressor"))?;
|
|
||||||
Ok(compressor.compress(&raw_bytes)?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> CompressorWrapper<T> {
|
|
||||||
pub fn new_with(inner: T) -> anyhow::Result<Self> {
|
|
||||||
let compressor = Compressor::new(13)?;
|
|
||||||
let decompressor = Decompressor::new()?;
|
|
||||||
|
|
||||||
Ok(CompressorWrapper {
|
|
||||||
inner,
|
|
||||||
compressor: Mutex::new(compressor),
|
|
||||||
decompressor: Mutex::new(decompressor),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new() -> anyhow::Result<Self>
|
|
||||||
where
|
|
||||||
T: Default,
|
|
||||||
{
|
|
||||||
let inner: T = Default::default();
|
|
||||||
|
|
||||||
Self::new_with(inner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ByteWrapper for String {
|
|
||||||
type Item = String;
|
|
||||||
|
|
||||||
fn load_from_db_bytes(&self, bytes: &[u8]) -> anyhow::Result<Self::Item> {
|
|
||||||
Ok(std::str::from_utf8(bytes)?.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dump_to_db_bytes(&self, item: &Self::Item) -> anyhow::Result<Vec<u8>> {
|
|
||||||
Ok(item.as_bytes().to_vec())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ByteWrapper> ByteWrapper for Arc<T> {
|
|
||||||
type Item = T::Item;
|
|
||||||
|
|
||||||
fn load_from_db_bytes(&self, bytes: &[u8]) -> anyhow::Result<Self::Item> {
|
|
||||||
(&self as &T).load_from_db_bytes(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dump_to_db_bytes(&self, item: &Self::Item) -> anyhow::Result<Vec<u8>> {
|
|
||||||
(&self as &T).dump_to_db_bytes(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ZeroCopyByteWrapper: ToOwned {
|
|
||||||
fn as_byte_slice(&self) -> &[u8];
|
|
||||||
fn from_byte_slice(bytes: &[u8]) -> anyhow::Result<&Self>;
|
|
||||||
fn from_owned_bytes(bytes: Vec<u8>) -> anyhow::Result<Self::Owned>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ZeroCopyByteWrapper for [u8] {
|
|
||||||
fn as_byte_slice(&self) -> &[u8] {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_byte_slice(bytes: &[u8]) -> anyhow::Result<&Self> {
|
|
||||||
Ok(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_owned_bytes(bytes: Vec<u8>) -> anyhow::Result<Self::Owned> {
|
|
||||||
Ok(bytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ZeroCopyByteWrapper for str {
|
|
||||||
fn as_byte_slice(&self) -> &[u8] {
|
|
||||||
self.as_bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_byte_slice(bytes: &[u8]) -> anyhow::Result<&Self> {
|
|
||||||
Ok(std::str::from_utf8(bytes)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_owned_bytes(bytes: Vec<u8>) -> anyhow::Result<Self::Owned> {
|
|
||||||
Ok(String::from_utf8(bytes)?)
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user