mirror of
https://gitlab.com/fabinfra/fabaccess/bffh.git
synced 2024-11-11 01:53:23 +01:00
199 lines
5.0 KiB
Rust
199 lines
5.0 KiB
Rust
|
use std::{
|
||
|
marker::PhantomData,
|
||
|
};
|
||
|
|
||
|
use rkyv::{
|
||
|
Archived,
|
||
|
archived_root,
|
||
|
|
||
|
Serialize,
|
||
|
|
||
|
ser::{
|
||
|
Serializer,
|
||
|
serializers::AllocSerializer,
|
||
|
},
|
||
|
|
||
|
util::AlignedVec,
|
||
|
|
||
|
Fallible,
|
||
|
};
|
||
|
|
||
|
use lmdb::{
|
||
|
Environment,
|
||
|
DatabaseFlags,
|
||
|
WriteFlags,
|
||
|
|
||
|
Transaction,
|
||
|
RwTransaction,
|
||
|
};
|
||
|
|
||
|
use super::RawDB;
|
||
|
|
||
|
pub trait Adapter: Fallible {
|
||
|
type Serializer: rkyv::ser::Serializer;
|
||
|
type Value: Serialize<Self::Serializer>;
|
||
|
|
||
|
fn new_serializer() -> Self::Serializer;
|
||
|
|
||
|
fn from_ser_err(e: <Self::Serializer as Fallible>::Error) -> <Self as Fallible>::Error;
|
||
|
fn from_db_err(e: lmdb::Error) -> <Self as Fallible>::Error;
|
||
|
}
|
||
|
|
||
|
pub trait OutputBuffer {
|
||
|
type Buffer: AsRef<[u8]>;
|
||
|
fn into_slice(self) -> Self::Buffer;
|
||
|
}
|
||
|
|
||
|
impl<const N: usize> OutputBuffer for AllocSerializer<N> {
|
||
|
type Buffer = AlignedVec;
|
||
|
fn into_slice(self) -> Self::Buffer {
|
||
|
self.into_serializer().into_inner()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub trait OutputWriter: Fallible {
|
||
|
fn write_into(&mut self, buf: &mut [u8]) -> Result<(), Self::Error>;
|
||
|
}
|
||
|
|
||
|
pub struct DB<A> {
|
||
|
db: RawDB,
|
||
|
phantom: PhantomData<A>,
|
||
|
}
|
||
|
|
||
|
impl<A> DB<A> {
|
||
|
fn new(db: RawDB) -> Self {
|
||
|
Self { db, phantom: PhantomData }
|
||
|
}
|
||
|
|
||
|
/// Open the underlying DB, creating it if necessary
|
||
|
///
|
||
|
/// This function is unsafe since if the DB does not contain `A::Archived` we may end up doing
|
||
|
/// random memory reads or writes
|
||
|
pub unsafe fn create(env: &Environment, name: Option<&str>, flags: DatabaseFlags)
|
||
|
-> lmdb::Result<Self>
|
||
|
{
|
||
|
RawDB::create(env, name, flags).map(Self::new)
|
||
|
}
|
||
|
|
||
|
/// Open the underlying DB
|
||
|
///
|
||
|
/// This function is unsafe since if the DB does not contain `A::Archived` we may end up doing
|
||
|
/// random memory reads or writes
|
||
|
pub unsafe fn open(env: &Environment, name: Option<&str>) -> lmdb::Result<Self> {
|
||
|
RawDB::open(env, name).map(Self::new)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl<A: Adapter> DB<A>
|
||
|
{
|
||
|
pub fn del<K: AsRef<[u8]>>(&self, txn: &mut RwTransaction, key: &K) -> Result<(), A::Error> {
|
||
|
let v: Option<&Vec<u8>> = None;
|
||
|
self.db.del(txn, key, v).map_err(A::from_db_err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl<A: Adapter> DB<A>
|
||
|
{
|
||
|
pub fn get<'txn, T: Transaction, K: AsRef<[u8]>>(&self, txn: &'txn T, key: &K)
|
||
|
-> Result<Option<&'txn Archived<A::Value>>, A::Error>
|
||
|
{
|
||
|
if let Some(buf) = self.db.get(txn, key).map_err(A::from_db_err)? {
|
||
|
Ok(Some(unsafe { archived_root::<A::Value>(buf.as_ref()) }))
|
||
|
} else {
|
||
|
Ok(None)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn open_ro_cursor<'txn, T: Transaction>(&self, txn: &'txn T)
|
||
|
-> Result<Cursor<lmdb::RoCursor<'txn>, A>, A::Error>
|
||
|
{
|
||
|
let c = self.db.open_ro_cursor(txn)
|
||
|
.map_err(A::from_db_err)?;
|
||
|
// Safe because we are providing both Adapter and cursor and know it matches
|
||
|
Ok(unsafe { Cursor::new(c) })
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl<'a, A> DB<A>
|
||
|
where A: Adapter,
|
||
|
A::Serializer: OutputBuffer,
|
||
|
{
|
||
|
pub fn put<K: AsRef<[u8]>>(&self, txn: &mut RwTransaction, key: &K, val: &A::Value, flags: WriteFlags)
|
||
|
-> Result<usize, A::Error>
|
||
|
{
|
||
|
let mut serializer = A::new_serializer();
|
||
|
let pos = serializer.serialize_value(val)
|
||
|
.map_err(A::from_ser_err)?;
|
||
|
|
||
|
let buf = serializer.into_slice();
|
||
|
self.db.put(txn, key, &buf, flags)
|
||
|
.map_err(A::from_db_err)?;
|
||
|
|
||
|
Ok(pos)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl<'a, A> DB<A>
|
||
|
where A: Adapter,
|
||
|
A::Serializer: OutputWriter,
|
||
|
{
|
||
|
pub fn put_nocopy<K: AsRef<[u8]>>(&self, txn: &mut RwTransaction, key: &K, val: &A::Value, flags: WriteFlags)
|
||
|
-> Result<usize, A::Error>
|
||
|
{
|
||
|
let mut serializer = A::new_serializer();
|
||
|
let pos = serializer.serialize_value(val)
|
||
|
.map_err(A::from_ser_err)?;
|
||
|
|
||
|
let mut buf = self.db.reserve(txn, &key.as_ref(), pos, flags)
|
||
|
.map_err(A::from_db_err)?;
|
||
|
serializer.write_into(&mut buf)
|
||
|
.map_err(A::from_ser_err)?;
|
||
|
|
||
|
Ok(pos)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub struct Cursor<C, A> {
|
||
|
cursor: C,
|
||
|
phantom: PhantomData<A>,
|
||
|
}
|
||
|
|
||
|
impl<'txn, C, A> Cursor<C, A>
|
||
|
where C: lmdb::Cursor<'txn>,
|
||
|
A: Adapter,
|
||
|
{
|
||
|
// Unsafe because we don't know if the given adapter matches the given cursor
|
||
|
pub unsafe fn new(cursor: C) -> Self {
|
||
|
Self { cursor, phantom: PhantomData }
|
||
|
}
|
||
|
|
||
|
pub fn iter_dup_of<K: AsRef<[u8]>>(&mut self, key: &K) -> Iter<'txn, A> {
|
||
|
let iter = self.cursor.iter_dup_of(key);
|
||
|
// Safe because `new` isn't :P
|
||
|
unsafe { Iter::new(iter) }
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub struct Iter<'txn, A> {
|
||
|
iter: lmdb::Iter<'txn>,
|
||
|
phantom: PhantomData<A>,
|
||
|
}
|
||
|
|
||
|
impl<'txn, A: Adapter> Iter<'txn, A> {
|
||
|
pub unsafe fn new(iter: lmdb::Iter<'txn>) -> Self {
|
||
|
Self { iter, phantom: PhantomData }
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl<'txn, A: Adapter> Iterator for Iter<'txn, A>
|
||
|
where Archived<A::Value>: 'txn
|
||
|
{
|
||
|
type Item = Result<(&'txn [u8], &'txn Archived<A::Value>), A::Error>;
|
||
|
|
||
|
fn next(&mut self) -> Option<Self::Item> {
|
||
|
self.iter.next().map(|r| r
|
||
|
.map_err(A::from_db_err)
|
||
|
.map(|(key, buf)| { (key, unsafe { archived_root::<A::Value>(buf) }) }))
|
||
|
}
|
||
|
}
|