use std::fmt; use std::rc::Rc; use std::sync::Arc; use std::any::Any; use std::collections::{HashMap, hash_map::DefaultHasher}; use std::hash::{Hash, Hasher}; use std::default::Default; use std::ptr::NonNull; use std::alloc::Layout; use std::ops::Deref; use rkyv::{ Archive, Archived, Serialize, Deserialize, Fallible, ser::{ Serializer, ScratchSpace, serializers::*, }, string::{ StringResolver, ArchivedString, }, out_field, archived_root, }; use rkyv_dyn::{ archive_dyn, }; use rkyv_typename::TypeName; use crate::error::Error; use crate::db::{DB, Environment, WriteFlags, Transaction, RoTransaction}; #[archive_dyn(deserialize)] /// Trait to be implemented by any value in the state map. /// /// A value can be any type not having dangling references (with the added restriction that it has /// to implement `Debug` for debugger QoL). /// In fact Value *also* needs to implement Hash since BFFH checks if the state is different to /// before on input and output before updating the resource re. notifying actors and notifys. This /// dependency is not expressable via supertraits since it is not possible to make Hash into a /// trait object. /// To solve this [`State`] uses the [`StateBuilder`] which adds an `Hash` requirement for inputs /// on [`add`](struct::StateBuilder::add). The hash is being created over all inserted values and /// then used to check for equality. Note that in addition to collisions, Hash is not guaranteed /// stable over ordering and will additionally not track overwrites, so if the order of insertions /// changes or values are set and later overwritten then two equal States can and are likely to /// have different hashes. pub trait Value: Any + fmt::Debug { } #[repr(transparent)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Archive, Serialize, Deserialize)] #[archive_attr(derive(TypeName, Debug))] pub struct Bool(bool); #[archive_dyn(deserialize)] impl Value for Bool { } impl Value for Archived { } #[repr(transparent)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Archive, Serialize, Deserialize)] #[archive_attr(derive(TypeName, Debug))] pub struct UInt32(u32); #[archive_dyn(deserialize)] impl Value for UInt32 { } impl Value for Archived { } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Archive, Serialize, Deserialize)] #[archive_attr(derive(TypeName, Debug))] pub struct Vec3u8 { a: u8, b: u8, c: u8, } #[archive_dyn(deserialize)] impl Value for Vec3u8 { } impl Value for Archived { } #[derive(Archive, Serialize, Deserialize)] /// State object of a resource /// /// This object serves three functions: /// 1. it is constructed by modification via Claims or via internal resource logic /// 2. it is serializable and storable in the database /// 3. it is sendable and forwarded to all Actors and Notifys pub struct State { hash: u64, inner: Vec<(String, Box)>, } impl PartialEq for State { fn eq(&self, other: &Self) -> bool { self.hash == other.hash } } impl Eq for State {} impl fmt::Debug for State { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut sf = f.debug_struct("State"); for (k, v) in self.inner.iter() { sf.field(k, v); } sf.finish() } } impl State { pub fn build() -> StateBuilder { StateBuilder::new() } pub fn hash(&self) -> u64 { self.hash } } pub struct StateBuilder { hasher: DefaultHasher, inner: Vec<(String, Box)> } impl StateBuilder { pub fn new() -> Self { let hasher = DefaultHasher::new(); Self { inner: Vec::new(), hasher } } pub fn finish(self) -> State { State { hash: self.hasher.finish(), inner: self.inner, } } /// Add key-value pair to the State being built. /// /// We have to use this split system here because type erasure prevents us from limiting values /// to `Hash`. Specifically, you can't have a trait object of `Hash` because `Hash` depends on /// `Self`. In this function however the compiler still knows the exact type of `V` and can /// call statically call its `hash` method. pub fn add(mut self, key: String, val: V) -> Self where V: SerializeValue + Hash { key.hash(&mut self.hasher); val.hash(&mut self.hasher); self.inner.push((key, Box::new(val))); self } } pub struct StateStorage { key: u64, db: StateDB } impl StateStorage { pub fn new(key: u64, db: StateDB) -> Self { Self { key, db } } pub fn store(&mut self, instate: &State, outstate: &State) -> Result<(), Error> { self.db.store(self.key, instate, outstate) } } struct SizeSerializer { pos: usize, scratch: FallbackScratch, AllocScratch>, } impl SizeSerializer { pub fn new() -> Self { Self { pos: 0, scratch: FallbackScratch::default() } } } impl Fallible for SizeSerializer { type Error = AllocScratchError; } impl Serializer for SizeSerializer { fn pos(&self) -> usize { self.pos } fn write(&mut self, bytes: &[u8]) -> Result<(), Self::Error> { self.pos += bytes.len(); Ok(()) } } impl ScratchSpace for SizeSerializer { unsafe fn push_scratch( &mut self, layout: Layout ) -> Result, Self::Error> { self.scratch.push_scratch(layout) } unsafe fn pop_scratch( &mut self, ptr: NonNull, layout: Layout ) -> Result<(), Self::Error> { self.scratch.pop_scratch(ptr, layout) } } type LmdbSerializer = CompositeSerializer< BufferSerializer, FallbackScratch, AllocScratch>, SharedSerializeMap, >; pub struct StateDB { input: DB, output: DB, } impl StateDB { pub fn new(input: DB, output: DB) -> Self { Self { input, output } } fn get_size(&self, state: &State) -> usize { let mut serializer = SizeSerializer::new(); serializer.serialize_value(state); serializer.pos() } pub fn store(&self, key: u64, instate: &State, outstate: &State) -> Result<(), Error> { let insize = self.get_size(instate); let outsize = self.get_size(outstate); let mut txn = self.input.begin_rw_txn()?; let mut inbuf = self.input.reserve(&mut txn, &key.to_ne_bytes(), insize, WriteFlags::empty())?; let bufser = BufferSerializer::new(inbuf); let ser: LmdbSerializer<&mut [u8], 1024> = LmdbSerializer::new( bufser, FallbackScratch::default(), SharedSerializeMap::default() ); let mut outbuf = self.output.reserve(&mut txn, &key.to_ne_bytes(), outsize, WriteFlags::empty())?; let bufser = BufferSerializer::new(outbuf); let ser: LmdbSerializer<&mut [u8], 1024> = LmdbSerializer::new( bufser, FallbackScratch::default(), SharedSerializeMap::default() ); txn.commit()?; Ok(()) } pub fn get_txn<'txn, T: Transaction>(&self, key: u64, txn: &'txn T) -> Result<(&'txn ArchivedState, &'txn ArchivedState), Error> { let inbuf = self.input.get(txn, &key.to_ne_bytes())?; let outbuf = self.output.get(txn, &key.to_ne_bytes())?; let instate = unsafe { archived_root::(inbuf.as_ref()) }; let outstate = unsafe { archived_root::(outbuf.as_ref()) }; Ok((instate, outstate)) } } #[cfg(test)] mod tests { use super::*; use crate::db::tests::open_test_env; use lmdb::{ EnvironmentFlags as EF, DatabaseFlags as DF, WriteFlags as WF, }; use rkyv::Infallible; use rkyv::ser::serializers::AllocSerializer; use rkyv::archived_root; use rkyv::util::archived_value; #[test] fn construct_state() { let b = State::build() .add("Colour".to_string(), Vec3u8 { a: 1, b: 2, c: 3}) .add("Powered".to_string(), Bool(true)) .add("Intensity".to_string(), UInt32(4242)) .finish(); println!("({}) {:?}", b.hash(), b); let mut serializer = AllocSerializer::<256>::default(); let pos = serializer.serialize_value(&b).unwrap(); let buf = serializer.into_serializer().into_inner(); println!("Encsize: {}", buf.len()); let archived_state = unsafe { archived_value::(buf.as_ref(), pos) }; let s: State = archived_state.deserialize(&mut Infallible).unwrap(); println!("({}) {:?}", pos, s); } #[test] fn function_name_test() { let te = open_text_env(); let ildb = e.create_db(Some("input"), DF::empty()).expect("Failed to create db file"); let oldb = e.create_db(Some("output"), DF::empty()).expect("Failed to create db file"); let idb = DB::new(e.env.clone(), ildb); let odb = DB::new(e.env.clone(), oldb); let db = StateDB::new(idb, odb); } }