diff --git a/src/post/mod.rs b/src/post/mod.rs index 1914acd..649d891 100644 --- a/src/post/mod.rs +++ b/src/post/mod.rs @@ -20,6 +20,20 @@ pub struct PostTimestamp pub last_edit: DateTime, } +impl PostTimestamp +{ + /// Create a new timestamp for a new post + pub fn new() -> Self + { + let now = Utc::now(); + Self { + opened: now.clone(), + closed: None, + last_edit: now, + } + } +} + impl Hash for PostTimestamp { fn hash(&self, state: &mut H) { self.opened.hash(state); @@ -61,6 +75,26 @@ pub struct Static hash: crypto::sha256::Sha256Hash, } +impl Static +{ + /// Compute the hash for this instance + pub fn into_hashed(mut self) -> Self + { + self.hash = Default::default(); + let bytes = serde_cbor::to_vec(&self).expect("Failed to serialise static post to CBOR"); + Self { + hash: crypto::sha256::compute_slice(&bytes), + ..self + } + } + + /// Validate the internal hash of this instance + pub fn validate_hash(&self) -> bool + { + self.clone().into_hashed().hash == self.hash + } +} + use suspend::{ Suspendable, SuspendStream, @@ -81,3 +115,42 @@ impl Suspendable for Static input.try_get("post-static").ok_or(suspend::Error::BadObject) } } + +#[cfg(test)] +mod tests +{ + use super::*; + #[tokio::test] + async fn static_post_ser() + { + let mut output = suspend::MemorySsuspendStream::new(); + let post1 = Static { + id: identity::PostID::new(), + user: Default::default(), + title: None, + karada: "hello world".to_owned(), + timestamp: PostTimestamp::new(), + hash: Default::default(), + }.into_hashed(); + + let post1c = post1.clone(); + assert!(post1.validate_hash()); + post1.suspend(&mut output).await.expect("Suspend failed"); + + let buffer = output.buffer().clone(); + let post2 = Static::load(&mut output).await.expect("Suspend load failed"); + + assert_eq!(post1c, post2); + assert!(post1c.validate_hash()); + assert!(post2.validate_hash()); + + let buffer2 = suspend::oneshot(post2.clone()).await.expect("Oneshot failed"); + + assert_eq!(&buffer2[..], &buffer[..]); + + let post3: Static = suspend::single(buffer2).await.expect("Single failed"); + + assert!(post3.validate_hash()); + assert_eq!(post3, post2); + } +} diff --git a/src/suspend.rs b/src/suspend.rs index 0cf548b..24ee738 100644 --- a/src/suspend.rs +++ b/src/suspend.rs @@ -70,7 +70,7 @@ impl Object } true } - + /// Try to get a value of type `T` from `name`. pub fn get<'a, T>(&'a mut self, name: impl Borrow) -> Option where T: Deserialize<'a> @@ -165,26 +165,47 @@ impl Object } /// Consume into the data - #[deprecated = "Invalid ABI"] fn into_bytes(self) -> Box<[u8]> + pub fn into_bytes(self) -> Box<[u8]> { let mut output = Vec::new(); //TOOO: Get cap debug_assert!(self.validate(), "passing invalid object to serialise"); - unsafe { - output.extend_from_slice(bytes::refer(&self.data_instances.len())); - for (name, Range{start, end}) in self.data_instances.into_iter() { - let name = name.as_bytes(); + macro_rules! bin { + ($bytes:expr) => { + { + let bytes = $bytes; + output.extend_from_slice(bytes.as_ref()); + } + }; + (usize $value:expr) => { + { + use std::convert::TryInto; + use byteorder::{ + WriteBytesExt, + LittleEndian, + }; + let val: u64 = $value.try_into().expect("Value could not fit into `u64`"); + WriteBytesExt::write_u64::(&mut output,val).expect("Failed to append `u64` to output buffer"); + + } + }; + } - output.extend_from_slice(bytes::refer(&name.len())); - output.extend_from_slice(name); + bin!(usize self.data_instances.len()); + for (name, &Range{start, end}) in self.data_instances.iter() { + let name = name.as_bytes(); - output.extend_from_slice(bytes::refer(&start)); - output.extend_from_slice(bytes::refer(&end)); - } + bin!(usize name.len()); + bin!(name); + + bin!(usize start); + bin!(usize end); } - + + bin!(usize self.data.len()); //for additional checks output.extend(self.data); + output.into_boxed_slice() } @@ -329,6 +350,14 @@ impl fmt::Display for Error } } +impl From for Error +{ + #[inline] fn from(from: io::Error) -> Self + { + Self::IO(from) + } +} + /// A suspendable type, that can save and reload its data atomically #[async_trait] @@ -338,11 +367,154 @@ pub trait Suspendable: Sized async fn load(from: &mut S) -> Result; } +/// An in-memory `SuspendStream`. +#[derive(Debug, Clone)] +pub struct MemorySsuspendStream(Vec); + +impl MemorySsuspendStream +{ + /// Create a new empty instance + pub fn new() -> Self + { + Self(Vec::new()) + } + + /// Create from a vector of bytes + pub fn from_bytes(from: impl Into>) -> Self + { + Self(from.into()) + } + + /// Create from a slice of bytes + pub fn from_slice(from: impl AsRef<[u8]>) -> Self + { + Self(Vec::from(from.as_ref())) + } + + /// Return the internal bytes + pub fn into_bytes(self) -> Vec + { + self.0 + } + + /// The internal buffer + pub fn buffer(&self) -> &Vec + { + &self.0 + } + + /// The internal buffer + pub fn buffer_mut(&mut self) -> &mut Vec + { + &mut self.0 + } + +} + +impl AsRef<[u8]> for MemorySsuspendStream +{ + fn as_ref(&self) -> &[u8] + { + &self.0[..] + } +} + +impl AsMut<[u8]> for MemorySsuspendStream +{ + fn as_mut(&mut self) -> &mut [u8] + { + &mut self.0[..] + } +} + +impl From> for MemorySsuspendStream +{ + #[inline] fn from(from: Vec) -> Self + { + Self(from.into()) + } +} + +impl From> for MemorySsuspendStream +{ + fn from(from: Box<[u8]>) -> Self + { + Self::from_bytes(from) + } +} + +impl From for Box<[u8]> +{ + fn from(from: MemorySsuspendStream) -> Self + { + from.0.into() + } +} + +impl From for Vec +{ + #[inline] fn from(from: MemorySsuspendStream) -> Self + { + from.0 + } +} + +#[async_trait] +impl SuspendStream for MemorySsuspendStream +{ + async fn get_object(&mut self) -> Result, Error> { + if self.0.len() ==0 { + return Ok(None); + } + + let mut ptr = &self.0[..]; + let vl = Object::from_stream(&mut ptr).await?; + let diff = (ptr.as_ptr() as usize) - ((&self.0[..]).as_ptr() as usize); + self.0.drain(0..diff); + + Ok(Some(vl)) + } + async fn set_object(&mut self, obj: Object) -> Result<(), Error> { + obj.into_stream(&mut self.0).await?; + Ok(()) + } +} -/* -pub struct SuspenceState -where T: Suspendable +/// Suspend a single object to memory +pub async fn oneshot(value: T) -> Result, Error> { + let mut output = MemorySsuspendStream::new(); + value.suspend(&mut output).await?; + Ok(output.into_bytes()) +} -_phantom: PhantomData, -}*/ +/// Load a single value from memory +pub async fn single(from: impl AsRef<[u8]>) -> Result +{ + struct BorrowedStream<'a>(&'a [u8]); + + #[async_trait] + impl<'a> SuspendStream for BorrowedStream<'a> + { + async fn get_object(&mut self) -> Result, Error> { + if self.0.len() ==0 { + return Ok(None); + } + + let mut ptr = &self.0[..]; + let vl = Object::from_stream(&mut ptr).await?; + let diff = (ptr.as_ptr() as usize) - ((&self.0[..]).as_ptr() as usize); + self.0 = &self.0[diff..]; + + Ok(Some(vl)) + } + async fn set_object(&mut self, _: Object) -> Result<(), Error> { + panic!("Cannot write to borrowed stream") + } + } + + let bytes = from.as_ref(); + let mut stream = BorrowedStream(bytes); + + T::load(&mut stream).await +}