Restructure

This commit is contained in:
Nadja Reitzenstein
2021-10-27 23:20:35 +02:00
parent a336f83e75
commit 0cca818cc1
29 changed files with 79 additions and 19 deletions

58
bffhd/db/fix.rs Normal file
View File

@ -0,0 +1,58 @@
use std::{
ptr::NonNull,
ops::Deref,
};
use crate::db::Transaction;
use std::fmt::{Debug, Formatter};
/// Memory Fixpoint for a value in the DB
///
/// LMDB binds lifetimes of buffers to the transaction that returned the buffer. As long as this
/// transaction is not `commit()`ed, `abort()`ed or `reset()`ed the pages containing these values
/// are not returned into circulation.
/// This struct encodes this by binding a live reference to the Transaction to the returned
/// and interpreted buffer. The placeholder `T` is the container for the transaction. This may be a
/// plain `RoTransaction<'env>`, a `Rc<RoTxn>` (meaning Fix is !Send) or an `Arc<RoTxn>`, depending
/// on your needs.
pub struct LMDBorrow<T, V> {
ptr: NonNull<V>,
txn: T,
}
impl<'env, T, V> LMDBorrow<T, V>
where T: Transaction,
{
pub unsafe fn new(ptr: NonNull<V>, txn: T) -> Self {
Self { ptr: ptr.into(), txn }
}
pub fn unwrap_txn(self) -> T {
self.txn
}
}
impl<'env, T, V> Deref for LMDBorrow<T, V>
{
type Target = V;
fn deref(&self) -> &Self::Target {
// As long as the transaction is kept alive (which it is, because it's in self) state is a
// valid pointer so this is safe.
unsafe { self.ptr.as_ref() }
}
}
impl<'env, T, V: Debug> Debug for LMDBorrow<T, V> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.deref())
}
}
impl<'env, T, V: serde::Serialize> serde::Serialize for LMDBorrow<T, V> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: serde::Serializer
{
self.deref().serialize(serializer)
}
}

164
bffhd/db/hash.rs Normal file
View File

@ -0,0 +1,164 @@
use std::{
marker::PhantomData,
hash::{
Hash,
Hasher,
BuildHasher,
},
collections::hash_map::RandomState,
};
use rkyv::{
Archive,
Archived,
Serialize,
Deserialize,
Fallible,
};
use super::{
DB,
Adapter,
OutputBuffer,
Environment,
DatabaseFlags,
WriteFlags,
Transaction,
RwTransaction,
};
#[derive(Archive, Serialize, Deserialize)]
/// 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: 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, 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(())
}
}

73
bffhd/db/pass.rs Normal file
View File

@ -0,0 +1,73 @@
use std::sync::Arc;
use super::Environment;
use super::AllocAdapter;
use super::DB;
use super::raw::RawDB;
use super::{DatabaseFlags, WriteFlags};
use crate::db::Result;
use super::Transaction;
use argon2;
type Adapter = AllocAdapter<String>;
#[derive(Clone)]
pub struct PassDB {
env: Arc<Environment>,
db: DB<Adapter>,
}
impl PassDB {
pub unsafe fn new(env: Arc<Environment>, db: RawDB) -> Self {
let db = DB::new_unchecked(db);
Self { env, db }
}
pub unsafe fn open(env: Arc<Environment>) -> Result<Self> {
let db = RawDB::open(&env, Some("pass"))?;
Ok(Self::new(env, db))
}
pub unsafe fn create(env: Arc<Environment>) -> Result<Self> {
let flags = DatabaseFlags::empty();
let db = RawDB::create(&env, Some("pass"), flags)?;
Ok(Self::new(env, db))
}
pub fn check_pw<P: AsRef<[u8]>>(&self, uid: &str, inpass: P) -> Result<Option<bool>> {
let txn = self.env.begin_ro_txn()?;
if let Some(pass) = self.db.get(&txn, &uid.as_bytes())? {
Ok(argon2::verify_encoded(pass.as_str(), inpass.as_ref())
.ok())
} else {
Ok(None)
}
}
pub fn set_password<P: AsRef<[u8]>>(&self, uid: &str, password: P) -> Result<()> {
let cfg = argon2::Config::default();
let salt: [u8; 10] = rand::random();
let enc = argon2::hash_encoded(password.as_ref(), &salt, &cfg)
.expect("Hashing password failed for static valid config");
let flags = WriteFlags::empty();
let mut txn = self.env.begin_rw_txn()?;
self.db.put(&mut txn, &uid.as_bytes(), &enc, flags)?;
txn.commit()?;
Ok(())
}
pub fn get_all(&self) -> Result<Vec<(String, String)>> {
let txn = self.env.begin_ro_txn()?;
let mut cursor = self.db.open_ro_cursor(&txn)?;
let iter = cursor.iter_start();
let mut out = Vec::new();
for pass in iter {
let (uid, pass) = pass?;
let uid = unsafe { std::str::from_utf8_unchecked(uid).to_string() };
let pass = pass.as_str().to_string();
out.push((uid, pass));
}
Ok(out)
}
}

62
bffhd/db/raw.rs Normal file
View File

@ -0,0 +1,62 @@
use lmdb::{
Transaction,
RwTransaction,
Environment,
DatabaseFlags,
WriteFlags,
};
#[derive(Debug, Clone)]
pub struct RawDB {
db: lmdb::Database,
}
impl RawDB {
pub fn open(env: &Environment, name: Option<&str>) -> lmdb::Result<Self> {
env.open_db(name).map(|db| Self { db })
}
pub fn create(env: &Environment, name: Option<&str>, flags: DatabaseFlags) -> lmdb::Result<Self> {
env.create_db(name, flags).map(|db| Self { db })
}
pub fn get<'txn, T: Transaction, K>(&self, txn: &'txn T, key: &K) -> lmdb::Result<Option<&'txn [u8]>>
where K: AsRef<[u8]>
{
match txn.get(self.db, key) {
Ok(buf) => Ok(Some(buf)),
Err(lmdb::Error::NotFound) => Ok(None),
Err(e) => Err(e),
}
}
pub fn put<K, V>(&self, txn: &mut RwTransaction, key: &K, value: &V, flags: WriteFlags)
-> lmdb::Result<()>
where K: AsRef<[u8]>,
V: AsRef<[u8]>,
{
txn.put(self.db, key, value, flags)
}
pub fn reserve<'txn, K>(&self, txn: &'txn mut RwTransaction, key: &K, size: usize, flags: WriteFlags)
-> lmdb::Result<&'txn mut [u8]>
where K: AsRef<[u8]>
{
txn.reserve(self.db, key, size, flags)
}
pub fn del<K, V>(&self, txn: &mut RwTransaction, key: &K, value: Option<&V>) -> lmdb::Result<()>
where K: AsRef<[u8]>,
V: AsRef<[u8]>,
{
txn.del(self.db, key, value.map(AsRef::as_ref))
}
pub fn iter<'txn, C: lmdb::Cursor<'txn>>(&self, cursor: &'txn mut C) -> lmdb::Iter<'txn> {
cursor.iter_start()
}
pub fn open_ro_cursor<'txn, T: Transaction>(&self, txn: &'txn T) -> lmdb::Result<lmdb::RoCursor<'txn>> {
txn.open_ro_cursor(self.db)
}
}

72
bffhd/db/resources.rs Normal file
View File

@ -0,0 +1,72 @@
use rkyv::{Archive, Serialize, Deserialize};
use super::{
DB,
};
use crate::db::{AlignedAdapter, AllocAdapter};
use crate::db::raw::RawDB;
use std::sync::Arc;
use crate::db::{Environment, DatabaseFlags};
use crate::db::Result;
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Archive, Serialize, Deserialize)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Resource {
uuid: u128,
id: String,
name_idx: u64,
description_idx: u64,
}
#[derive(Clone)]
pub struct ResourceDB {
env: Arc<Environment>,
db: DB<AllocAdapter<Resource>>,
id_index: DB<AlignedAdapter<u64>>,
}
impl ResourceDB {
pub unsafe fn new(env: Arc<Environment>, db: RawDB, id_index: RawDB) -> Self {
let db = DB::new_unchecked(db);
let id_index = DB::new_unchecked(id_index);
Self { env, db, id_index }
}
pub unsafe fn open(env: Arc<Environment>) -> Result<Self> {
let db = RawDB::open(&env, Some("resources"))?;
let idx = RawDB::open(&env, Some("resources-idx"))?;
Ok(Self::new(env, db, idx))
}
pub unsafe fn create(env: Arc<Environment>) -> Result<Self> {
let flags = DatabaseFlags::empty();
let db = RawDB::create(&env, Some("resources"), flags)?;
let idx = RawDB::create(&env, Some("resources-idx"), flags)?;
Ok(Self::new(env, db, idx))
}
pub fn lookup_id<S: AsRef<str>>(&self, id: S) -> Result<Option<u64>> {
let txn = self.env.begin_ro_txn()?;
let id = self.id_index.get(&txn, &id.as_ref().as_bytes()).map(|ok| {
ok.map(|num| *num)
})?;
Ok(id)
}
pub fn get_all(&self) -> Result<Vec<(String, u64)>> {
let txn = self.env.begin_ro_txn()?;
let mut cursor = self.id_index.open_ro_cursor(&txn)?;
let iter = cursor.iter_start();
let mut out = Vec::new();
for id in iter {
let (name, id) = id?;
let name = unsafe { std::str::from_utf8_unchecked(name).to_string() };
out.push((name, *id));
}
Ok(out)
}
}

195
bffhd/db/state.rs Normal file
View File

@ -0,0 +1,195 @@
use std::{
sync::Arc,
path::Path,
};
use rkyv::{Archived};
use super::{
DB,
Environment,
EnvironmentFlags,
DatabaseFlags,
WriteFlags,
Adapter,
AllocAdapter,
DBError,
Transaction,
RoTransaction,
RwTransaction,
LMDBorrow,
};
use crate::state::State;
type StateAdapter = AllocAdapter<State>;
/// State Database containing the currently set state
#[derive(Clone, Debug)]
pub struct StateDB {
/// The environment for all the databases below
env: Arc<Environment>,
input: DB<StateAdapter>,
output: DB<StateAdapter>,
// TODO: Index resource name/id/uuid -> u64
}
impl StateDB {
fn open_env<P: AsRef<Path>>(path: P) -> lmdb::Result<Environment> {
Environment::new()
.set_flags( EnvironmentFlags::WRITE_MAP
| EnvironmentFlags::NO_SUB_DIR
| EnvironmentFlags::NO_TLS
| EnvironmentFlags::NO_READAHEAD)
.set_max_dbs(2)
.open(path.as_ref())
}
fn new(env: Environment, input: DB<StateAdapter>, output: DB<StateAdapter>) -> Self {
Self { env: Arc::new(env), input, output }
}
pub fn init<P: AsRef<Path>>(path: P) -> lmdb::Result<Self> {
let env = Self::open_env(path)?;
let input = unsafe {
DB::create(&env, Some("input"), DatabaseFlags::INTEGER_KEY)?
};
let output = unsafe {
DB::create(&env, Some("output"), DatabaseFlags::INTEGER_KEY)?
};
Ok(Self::new(env, input, output))
}
pub fn open<P: AsRef<Path>>(path: P) -> lmdb::Result<Self> {
let env = Self::open_env(path)?;
let input = unsafe { DB::open(&env, Some("input"))? };
let output = unsafe { DB::open(&env, Some("output"))? };
Ok(Self::new(env, input, output))
}
pub fn create<P: AsRef<Path>>(path: P) -> lmdb::Result<Self> {
let flags = DatabaseFlags::empty();
let env = Self::open_env(path)?;
let input = unsafe { DB::create(&env, Some("input"), flags)? };
let output = unsafe { DB::create(&env, Some("output"), flags)? };
Ok(Self::new(env, input, output))
}
fn update_txn(&self, txn: &mut RwTransaction, key: u64, input: &State, output: &State)
-> Result<(), DBError>
{
let flags = WriteFlags::empty();
let k = key.to_ne_bytes();
self.input.put(txn, &k, input, flags)?;
self.output.put(txn, &k, output, flags)?;
Ok(())
}
pub fn update(&self, key: u64, input: &State, output: &State)
-> Result<(), DBError>
{
let mut txn = self.env.begin_rw_txn().map_err(StateAdapter::from_db_err)?;
self.update_txn(&mut txn, key, input, output)?;
txn.commit().map_err(StateAdapter::from_db_err)
}
fn get(&self, db: &DB<StateAdapter>, key: u64)
-> Result<Option<LMDBorrow<RoTransaction, Archived<State>>>, DBError>
{
let txn = self.env.begin_ro_txn().map_err(StateAdapter::from_db_err)?;
if let Some(state) = db.get(&txn, &key.to_ne_bytes())? {
let ptr = state.into();
Ok(Some(unsafe { LMDBorrow::new(ptr, txn) }))
} else {
Ok(None)
}
}
#[inline(always)]
pub fn get_input(&self, key: u64)
-> Result<Option<LMDBorrow<RoTransaction, Archived<State>>>, DBError>
{ self.get(&self.input, key) }
#[inline(always)]
pub fn get_output(&self, key: u64)
-> Result<Option<LMDBorrow<RoTransaction, Archived<State>>>, DBError>
{ self.get(&self.output, key) }
pub fn accessor(&self, key: u64) -> StateAccessor {
StateAccessor::new(key, self.clone())
}
}
#[derive(Debug)]
pub struct StateAccessor {
key: u64,
db: StateDB
}
impl StateAccessor {
pub fn new(key: u64, db: StateDB) -> Self {
Self { key, db }
}
pub fn get_input(&self)
-> Result<Option<LMDBorrow<RoTransaction, Archived<State>>>, DBError>
{
self.db.get_input(self.key)
}
pub fn get_output(&self)
-> Result<Option<LMDBorrow<RoTransaction, Archived<State>>>, DBError>
{
self.db.get_output(self.key)
}
pub fn set(&self, input: &State, output: &State) -> Result<(), DBError> {
self.db.update(self.key, input, output)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::state::value::Vec3u8;
use crate::state::value::{OID_COLOUR, OID_POWERED, OID_INTENSITY};
use std::ops::Deref;
#[test]
fn construct_state() {
let tmpdir = tempfile::tempdir().unwrap();
let mut tmppath = tmpdir.path().to_owned();
tmppath.push("db");
let db = StateDB::init(tmppath).unwrap();
let b = State::build()
.add(OID_COLOUR.clone(), Box::new(Vec3u8 { a: 1, b: 2, c: 3}))
.add(OID_POWERED.clone(), Box::new(true))
.add(OID_INTENSITY.clone(), Box::new(1023))
.finish();
println!("({}) {:?}", b.hash(), b);
let c = State::build()
.add(OID_COLOUR.clone(), Box::new(Vec3u8 { a: 1, b: 2, c: 3}))
.add(OID_POWERED.clone(), Box::new(true))
.add(OID_INTENSITY.clone(), Box::new(1023))
.finish();
let key = rand::random();
db.update(key, &b, &c).unwrap();
let d = db.get_input(key).unwrap().unwrap();
let e = db.get_output(key).unwrap().unwrap();
assert_eq!(&b, d.deref());
assert_eq!(&c, e.deref());
}
}

242
bffhd/db/typed.rs Normal file
View File

@ -0,0 +1,242 @@
use std::{
fmt,
any::type_name,
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;
}
struct AdapterPrettyPrinter<A: Adapter>(PhantomData<A>);
impl<A: Adapter> AdapterPrettyPrinter<A> {
pub fn new() -> Self { Self(PhantomData) }
}
impl<A: Adapter> fmt::Debug for AdapterPrettyPrinter<A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct(&type_name::<A>())
.field("serializer", &type_name::<A::Serializer>())
.field("value", &type_name::<A::Value>())
.finish()
}
}
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()
}
}
// TODO: This should be possible to autoimplement for Sized Serializers
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> Clone for DB<A> {
fn clone(&self) -> Self {
Self {
db: self.db.clone(),
phantom: PhantomData,
}
}
}
impl<A: Adapter> fmt::Debug for DB<A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DB")
.field("db", &self.db)
.field("adapter", &AdapterPrettyPrinter::<A>::new())
.finish()
}
}
impl<A> DB<A> {
pub unsafe fn new_unchecked(db: RawDB) -> Self {
Self { db, phantom: PhantomData }
}
fn new(db: RawDB) -> Self {
unsafe { Self::new_unchecked(db) }
}
/// 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<TypedCursor<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 { TypedCursor::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 TypedCursor<C, A> {
cursor: C,
phantom: PhantomData<A>,
}
impl<'txn, C, A> TypedCursor<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_start(&mut self) -> Iter<'txn, A> {
let iter = self.cursor.iter_start();
// Safe because `new` isn't :P
unsafe { Iter::new(iter) }
}
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) }) }))
}
}

71
bffhd/db/user.rs Normal file
View File

@ -0,0 +1,71 @@
use std::sync::Arc;
use super::{DB, AllocAdapter, Environment, Result};
use crate::db::raw::RawDB;
use crate::db::{DatabaseFlags, LMDBorrow, RoTransaction, WriteFlags, };
use rkyv::{Archive, Serialize, Deserialize, Archived};
type Adapter = AllocAdapter<User>;
#[derive(Clone)]
pub struct UserDB {
env: Arc<Environment>,
db: DB<Adapter>,
}
#[derive(Debug, Clone, Archive, Serialize, Deserialize, serde::Serialize, serde::Deserialize)]
pub struct User {
id: u128,
username: String,
roles: Vec<String>,
}
impl UserDB {
pub unsafe fn new(env: Arc<Environment>, db: RawDB) -> Self {
let db = DB::new_unchecked(db);
Self { env, db }
}
pub unsafe fn open(env: Arc<Environment>) -> Result<Self> {
let db = RawDB::open(&env, Some("user"))?;
Ok(Self::new(env, db))
}
pub unsafe fn create(env: Arc<Environment>) -> Result<Self> {
let flags = DatabaseFlags::empty();
let db = RawDB::create(&env, Some("user"), flags)?;
Ok(Self::new(env, db))
}
pub fn get(&self, uid: &str) -> Result<Option<LMDBorrow<RoTransaction, Archived<User>>>> {
let txn = self.env.begin_ro_txn()?;
if let Some(state) = self.db.get(&txn, &uid.as_bytes())? {
let ptr = state.into();
Ok(Some(unsafe { LMDBorrow::new(ptr, txn) }))
} else {
Ok(None)
}
}
pub fn put(&self, uid: &str, user: &User) -> Result<()> {
let mut txn = self.env.begin_rw_txn()?;
let flags = WriteFlags::empty();
self.db.put(&mut txn, &uid.as_bytes(), user, flags)?;
Ok(())
}
pub fn get_all(&self) -> Result<Vec<(String, User)>> {
let txn = self.env.begin_ro_txn()?;
let mut cursor = self.db.open_ro_cursor(&txn)?;
let iter = cursor.iter_start();
let mut out = Vec::new();
let mut deserializer = rkyv::Infallible;
for user in iter {
let (uid, user) = user?;
let uid = unsafe { std::str::from_utf8_unchecked(uid).to_string() };
let user: User = user.deserialize(&mut deserializer).unwrap();
out.push((uid, user));
}
Ok(out)
}
}