Lay down some more structs

This commit is contained in:
Olivier 'reivilibre' 2022-03-24 19:12:45 +00:00
parent 900122d131
commit fdd82b2fef
6 changed files with 163 additions and 24 deletions

2
src/database.rs Normal file
View File

@ -0,0 +1,2 @@
pub(crate) mod raw;
pub(crate) mod wrapped;

16
src/database/raw.rs Normal file
View File

@ -0,0 +1,16 @@
use libmdbx::{Database, Environment, WriteMap};
use ouroboros::self_referencing;
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>,
}

7
src/database/wrapped.rs Normal file
View File

@ -0,0 +1,7 @@
use crate::database::raw::RawTable;
pub struct WrappedTable<K, V> {
pub raw: RawTable<[u8], [u8]>,
pub k_wrapper: K,
pub v_wrapper: V,
}

View File

@ -1,30 +1,60 @@
use crate::database::raw::{RawTable, RawTableTryBuilder};
use crate::database::wrapped::WrappedTable;
use crate::wrapper::{ByteWrapper, ZeroCopyByteWrapper};
use anyhow::{ensure, Context};
use libmdbx::{DatabaseFlags, Environment, WriteMap};
use std::marker::PhantomData;
use std::sync::Arc;
use libmdbx::{Database, Environment, EnvironmentKind, WriteMap};
pub struct Env {
pub mdbx_env: Environment<WriteMap>,
pub mdbx_env: Arc<Environment<WriteMap>>,
}
#[self_referencing]
pub struct Table {
pub mdbx_env: Arc<Env>,
#[borrows(mdbx_env)]
#[covariant]
pub mdbx_db: Database<'this>
}
// TODO:
// - raw if wanted
// - serde if wanted
// - compression if wanted
// - ??? does it make sense to offer ALL these things in one interface; could the raw stuff
// be done slightly differently?
impl Env {
pub fn open_table(&self, name: &str, flags: ()) -> anyhow::Result<Table> {
todo!()
pub fn open_raw_table<K: ZeroCopyByteWrapper + ?Sized, V: ZeroCopyByteWrapper + ?Sized>(
&self,
name: Option<&str>,
_flags: (),
) -> anyhow::Result<RawTable<K, V>> {
Ok(RawTableTryBuilder {
mdbx_env: self.mdbx_env.clone(),
mdbx_db_builder: |mdbx_env: &Arc<Environment<WriteMap>>| {
let txn = mdbx_env
.begin_rw_txn()
.context("Can't start RW transaction")?;
// TODO database flags
let db = txn
.create_db(name, DatabaseFlags::empty())
.context("Can't open database")?;
txn.prime_for_permaopen(db);
let (_bool, mut dbs) = txn.commit_and_rebind_open_dbs()?;
ensure!(dbs.len() == 1);
let mdbx_db = dbs.pop().context("Expected database!")?;
Ok(mdbx_db)
},
phantom_k: PhantomData::default(),
phantom_v: PhantomData::default(),
}
.try_build()?)
}
pub fn open_
}
pub fn open_wrapped_table<K: ByteWrapper, V: ByteWrapper>(
&self,
name: Option<&str>,
flags: (),
k_wrapper: K,
v_wrapper: V,
) -> anyhow::Result<WrappedTable<K, V>> {
let raw_table = self.open_raw_table(name, flags)?;
Ok(WrappedTable {
raw: raw_table,
k_wrapper,
v_wrapper,
})
}
}

View File

@ -1,5 +1,6 @@
mod environment;
pub(crate) mod database;
pub(crate) mod environment;
pub(crate) mod wrapper;
#[cfg(test)]
mod tests {

83
src/wrapper.rs Normal file
View File

@ -0,0 +1,83 @@
use anyhow::anyhow;
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::marker::PhantomData;
use std::sync::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: 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)?)
}
}
pub trait ZeroCopyByteWrapper {
fn as_byte_slice(&self) -> &[u8];
fn from_byte_slice(bytes: &[u8]) -> anyhow::Result<&Self>;
}
impl ZeroCopyByteWrapper for [u8] {
fn as_byte_slice(&self) -> &[u8] {
self
}
fn from_byte_slice(bytes: &[u8]) -> anyhow::Result<&Self> {
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)?)
}
}