mirror of
https://gitlab.com/fabinfra/fabaccess/bffh.git
synced 2024-11-25 16:17:56 +01:00
184 lines
4.7 KiB
Rust
184 lines
4.7 KiB
Rust
use std::{
|
|
marker::PhantomData,
|
|
hash::{
|
|
Hash,
|
|
Hasher,
|
|
BuildHasher,
|
|
},
|
|
collections::hash_map::RandomState,
|
|
fmt,
|
|
fmt::Debug,
|
|
};
|
|
use std::fmt::Formatter;
|
|
|
|
use rkyv::{
|
|
Archive,
|
|
Archived,
|
|
Serialize,
|
|
Deserialize,
|
|
Fallible,
|
|
};
|
|
|
|
use super::{
|
|
DB,
|
|
Adapter,
|
|
OutputBuffer,
|
|
|
|
Environment,
|
|
|
|
DatabaseFlags,
|
|
WriteFlags,
|
|
|
|
Transaction,
|
|
RwTransaction,
|
|
};
|
|
|
|
|
|
#[derive(Archive, Serialize, Deserialize, Debug)]
|
|
/// The entry as it is stored inside the database.
|
|
pub struct Entry<K: Archive, V: Archive> {
|
|
pub key: K,
|
|
pub val: V,
|
|
}
|
|
|
|
#[derive(Clone, Copy)]
|
|
pub struct HashAdapter<K, A> {
|
|
k: PhantomData<K>,
|
|
a: PhantomData<A>,
|
|
}
|
|
impl<K, A> HashAdapter<K, A> {
|
|
pub fn new() -> Self {
|
|
Self { k: PhantomData, a: PhantomData }
|
|
}
|
|
}
|
|
impl<K, A> Debug for HashAdapter<K, A> {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
use core::any::type_name;
|
|
write!(f, "HashAdapter<{}, {}>", type_name::<K>(), type_name::<A>())
|
|
}
|
|
}
|
|
|
|
impl<K, A: Fallible> Fallible for HashAdapter<K, A> { type Error = <A as Fallible>::Error; }
|
|
impl<K, A: Adapter> Adapter for HashAdapter<K, A>
|
|
where K: Archive,
|
|
Entry<K, A::Value>: Serialize<A::Serializer>,
|
|
{
|
|
type Serializer = A::Serializer;
|
|
type Value = Entry<K, A::Value>;
|
|
|
|
fn new_serializer() -> Self::Serializer
|
|
{ A::new_serializer() }
|
|
|
|
fn from_ser_err(e: <Self::Serializer as Fallible>::Error) -> <A as Fallible>::Error
|
|
{ A::from_ser_err(e) }
|
|
|
|
fn from_db_err(e: lmdb::Error) -> <A as Fallible>::Error
|
|
{ A::from_db_err(e) }
|
|
}
|
|
|
|
|
|
const DEFAULT_HASH_FLAGS: libc::c_uint =
|
|
DatabaseFlags::INTEGER_KEY.bits() + DatabaseFlags::DUP_SORT.bits();
|
|
|
|
pub struct HashDB<A, K, H = RandomState>
|
|
{
|
|
db: DB<HashAdapter<K, A>>,
|
|
hash_builder: H,
|
|
}
|
|
|
|
impl<A: Adapter, K, H: Debug> fmt::Debug for HashDB<A, K, H> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let adapter = HashAdapter::<A,K>::new();
|
|
f.debug_struct("HashDB")
|
|
.field("db", &adapter)
|
|
.field("hasher", &self.hash_builder)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
impl<A, K> HashDB<A, K>
|
|
{
|
|
pub unsafe fn create(env: &Environment, name: Option<&str>) -> lmdb::Result<Self> {
|
|
Self::create_with_hasher(env, name, RandomState::new())
|
|
}
|
|
pub unsafe fn open(env: &Environment, name: Option<&str>) -> lmdb::Result<Self> {
|
|
Self::open_with_hasher(env, name, RandomState::new())
|
|
}
|
|
}
|
|
|
|
impl<A, K, H: BuildHasher> HashDB<A, K, H>
|
|
{
|
|
fn new(db: DB<HashAdapter<K, A>>, hash_builder: H) -> Self {
|
|
Self { db, hash_builder }
|
|
}
|
|
|
|
pub unsafe fn create_with_hasher(env: &Environment, name: Option<&str>, hash_builder: H)
|
|
-> lmdb::Result<Self>
|
|
{
|
|
let flags = DatabaseFlags::from_bits(DEFAULT_HASH_FLAGS).unwrap();
|
|
DB::create(env, name, flags).map(|db| Self::new(db, hash_builder))
|
|
}
|
|
pub unsafe fn open_with_hasher(env: &Environment, name: Option<&str>, hash_builder: H)
|
|
-> lmdb::Result<Self>
|
|
{
|
|
DB::open(env, name).map(|db| Self::new(db, hash_builder))
|
|
}
|
|
|
|
}
|
|
|
|
impl<A, K, H> HashDB<A, K, H>
|
|
where A: Adapter,
|
|
HashAdapter<K, A>: Adapter<Value=Entry<K, A::Value>>,
|
|
H: BuildHasher,
|
|
K: Hash + Archive,
|
|
K::Archived: PartialEq<K>,
|
|
{
|
|
/// Retrieve an entry from the hashdb
|
|
///
|
|
/// The result is a view pinned to the lifetime of the transaction. You can get owned Values
|
|
/// using [`Deserialize`].
|
|
pub fn get<'txn, T: Transaction>(&self, txn: &'txn T, key: &K)
|
|
-> Result<
|
|
Option<&'txn Archived<<HashAdapter<K, A> as Adapter>::Value>>,
|
|
<HashAdapter<K, A> as Fallible>::Error
|
|
>
|
|
{
|
|
let mut hasher = self.hash_builder.build_hasher();
|
|
key.hash(&mut hasher);
|
|
let hash = hasher.finish();
|
|
|
|
let mut cursor = self.db.open_ro_cursor(txn)?;
|
|
let i = cursor
|
|
.iter_dup_of(&hash.to_ne_bytes()).filter_map(|r| r.ok())
|
|
.map(|(_keybuf, entry)| entry);
|
|
for entry in i {
|
|
let entry: &Archived<Entry<K, A::Value>> = entry;
|
|
if entry.key == *key {
|
|
return Ok(Some(entry));
|
|
}
|
|
}
|
|
|
|
Ok(None)
|
|
}
|
|
}
|
|
|
|
impl<'a, A, K, H> HashDB<A, K, H>
|
|
where A: Adapter,
|
|
A::Serializer: OutputBuffer,
|
|
H: BuildHasher,
|
|
K: Hash + Serialize<A::Serializer>,
|
|
K::Archived: PartialEq<K>,
|
|
{
|
|
pub fn insert_entry(&self, txn: &mut RwTransaction, entry: &Entry<K, A::Value>)
|
|
-> Result<(), A::Error>
|
|
{
|
|
let mut hasher = self.hash_builder.build_hasher();
|
|
entry.key.hash(&mut hasher);
|
|
let hash = hasher.finish();
|
|
|
|
self.db.put(txn, &hash.to_ne_bytes(), entry, WriteFlags::empty())?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|