From 38ffad21200a59e5f1d29b43982607306834fee4 Mon Sep 17 00:00:00 2001 From: Avril Date: Mon, 28 Sep 2020 19:50:56 +0100 Subject: [PATCH] more serialise shit --- Cargo.lock | 12 +++ Cargo.toml | 1 + src/format/key/mod.rs | 43 +++++++-- src/format/mod.rs | 26 ++++++ src/main.rs | 86 ++++-------------- src/serialise.rs | 202 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 291 insertions(+), 79 deletions(-) create mode 100644 src/serialise.rs diff --git a/Cargo.lock b/Cargo.lock index 37293dc..a6e0cb4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,6 +39,17 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034" +[[package]] +name = "async-trait" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "687c230d85c0a52504709705fc8a53e4a692b83a2184f03dae73e38e1e93a783" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "atty" version = "0.2.14" @@ -752,6 +763,7 @@ dependencies = [ name = "rae" version = "0.1.0" dependencies = [ + "async-trait", "base64", "base65536", "cfg-if", diff --git a/Cargo.toml b/Cargo.toml index 349069b..5faabdc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ chrono = "0.4.15" pin-project = "0.4.23" base64 = "0.12.3" hex = "0.4.2" +async-trait = "0.1.40" #serde_json = "1.0.57" # serde not suitable for our text formatting :/ maybe just use `cbor` -> base64 with text header? would be a PEM-like format. sounds good imo [build-dependencies] diff --git a/src/format/key/mod.rs b/src/format/key/mod.rs index d07e165..02e167b 100644 --- a/src/format/key/mod.rs +++ b/src/format/key/mod.rs @@ -216,6 +216,33 @@ impl KeyHeader } } + +impl serialise::BinarySerialisable for KeyHeader +{ + #[inline(always)] fn serialise_bytes<'a, 'b: 'a, T: AsyncWrite+Unpin+?Sized>(&'a self, out: &'b mut T) -> LocalBoxFuture<'a, Result> + { + self.write_bytes(out).boxed_local() + } + #[inline(always)] fn deserialise_bytes<'b , T: AsyncRead+Unpin+?Sized>(input: &'b mut T) -> LocalBoxFuture<'b, Result> + where Self: 'b + { + Self::read_bytes(input).boxed_local() + } +} + +impl serialise::TextSerialiseable for KeyHeader +{ + #[inline(always)] fn serialise_text<'a, 'b: 'a, T: AsyncWrite+Unpin+?Sized>(&'a self, out: &'b mut T) -> LocalBoxFuture<'a, Result> + { + self.write_text(out).boxed_local() + } + #[inline(always)] fn deserialise_text<'a, T: AsyncBufRead+Unpin+?Sized>(input: &'a mut T) -> LocalBoxFuture<'a, Result> + where Self: 'a + { + Self::read_text(input).boxed_local() + } +} + #[cfg(test)] mod tests { @@ -247,15 +274,15 @@ mod tests let mut ser = Vec::new(); let superheader = SuperHeader::::new_for(&header); println!("Writing: {:?} + {:?}", superheader, header); - let written = superheader.write_bytes(&mut ser).await? + + let written = superheader.serialise_bytes(&mut ser).await? + header.write_bytes(&mut ser).await?; println!("Wrote {} bytes", written); println!("{}\n", ser.fmt_view()); let mut read = &ser[..]; - let reads = SuperHeader::read_bytes(&mut read).await?; + let reads = SuperHeader::deserialise_bytes(&mut read).await?; println!("Read super: {:?}", reads); - let readheader = KeyHeader::read_bytes(&mut read).await?; + let readheader = KeyHeader::deserialise_bytes(&mut read).await?; println!("Read real: {:?}", readheader); reads.verify_for(&header)?; @@ -307,16 +334,16 @@ mod tests let mut ser = Vec::new(); let superheader = SuperHeader::::new_for(&header); println!("Writing: {:?} + {:?}", superheader, header); - let written = superheader.write_text(&mut ser).await? + - header.write_text(&mut ser).await?; + let written = superheader.write_text(&mut ser).await?; + ser.extend(header.into_memory(serialise::Mode::Text)?); //header.write_text(&mut ser).await?; println!("Wrote {} bytes", written); println!("{}\n", ser.fmt_view()); let mut read = &ser[..]; - let read = &mut read; - let reads = SuperHeader::read_text(read).await?; + let (reads, readn) = SuperHeader::from_memory(&mut read, serialise::Mode::Text)?; // SuperHeader::read_text(read).await?; + let mut read = &read[readn..]; println!("Read super: {:?}", reads); - let readheader = KeyHeader::read_text(read).await?; + let readheader = KeyHeader::read_text(&mut read).await?; println!("Read real: {:?}", readheader); reads.verify_for(&header)?; diff --git a/src/format/mod.rs b/src/format/mod.rs index 7c0279d..970257e 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -321,5 +321,31 @@ impl fmt::Display for VerificationError } } +impl serialise::BinarySerialisable for SuperHeader +{ + #[inline(always)] fn serialise_bytes<'a, 'b: 'a, T: AsyncWrite+Unpin+?Sized>(&'a self, out: &'b mut T) -> LocalBoxFuture<'a, Result> + { + self.write_bytes(out).boxed_local() + } + #[inline(always)] fn deserialise_bytes<'b , T: AsyncRead+Unpin+?Sized>(input: &'b mut T) -> LocalBoxFuture<'b, Result> + where Self: 'b + { + Self::read_bytes(input).boxed_local() + } +} + +impl serialise::TextSerialiseable for SuperHeader +{ + #[inline(always)] fn serialise_text<'a, 'b: 'a, T: AsyncWrite+Unpin+?Sized>(&'a self, out: &'b mut T) -> LocalBoxFuture<'a, Result> + { + self.write_text(out).boxed_local() + } + #[inline(always)] fn deserialise_text<'a, T: AsyncBufRead+Unpin+?Sized>(input: &'a mut T) -> LocalBoxFuture<'a, Result> + where Self: 'a + { + Self::read_text(input).boxed_local() + } +} + mod key; const CHECK_KEY: u16 = 0x0001; diff --git a/src/main.rs b/src/main.rs index a086710..4626ff9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ #[macro_use] extern crate tracing; #[macro_use] extern crate pin_project; +//#[macro_use] extern crate async_trait; use std::{ convert::{TryFrom, TryInto}, }; @@ -21,6 +22,19 @@ use color_eyre::{ use lazy_static::lazy_static; use serde::{Serialize, Deserialize}; use tracing_futures::Instrument; +use futures::{ + future::{ + FutureExt as _, + BoxFuture, + LocalBoxFuture, + }, + +}; +use serialise::{ + TextSerialiseable as _, + BinarySerialisable as _, + Serialisable as _, +}; macro_rules! cfg_debug { (if {$($if:tt)*} else {$($else:tt)*}) => { @@ -95,77 +109,7 @@ pub mod timestamp } } -pub mod serialise -{ - use super::*; - - /// Serialise this object into base64 text - /// - /// # TODO - /// Line wrapping - #[instrument(err, skip(value), fields(value_type=?std::any::type_name::()))] - pub fn into_text(value: &T) -> Result - { - let bytes = serde_cbor::to_vec(value).map_err(Error::Serialise)?; - Ok(base64::encode(bytes)) - } - - #[derive(Debug)] - pub enum Error - { - Base64(base64::DecodeError), - Serialise(serde_cbor::Error), - Deserialise(serde_cbor::Error), - } - - impl std::error::Error for Error - { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match &self { - Self::Base64(b) => Some(b), - Self::Serialise(b) => Some(b), - Self::Deserialise(b) => Some(b), - } - } - } - - impl std::fmt::Display for Error - { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result - { - match self { - Self::Base64(_) => write!(f, "base64 decode failed"), - Self::Serialise(_) => write!(f, "serialisation failed"), - Self::Deserialise(_) => write!(f, "deserialisation failed"), - } - } - } - - /// Try to deserialise this object from text. - #[instrument(err, skip(string), fields(string = ?string.as_ref()))] - pub fn from_text(string: impl AsRef<[u8]>) -> Result - { - let bytes = base64::decode(string).map_err(Error::Base64)?; - serde_cbor::from_reader(&bytes[..]).map_err(Error::Deserialise) - } - - #[cfg(test)] - mod tests - { - use crate::*; - #[test] - fn test_ser() -> Result<(), eyre::Report> - { - let object = "hello world"; - let enc = super::into_text(&String::from(object)).wrap_err(eyre::eyre!("failed to encode"))?; - println!("Enc: {:?}", enc); - let dec: String = super::from_text(enc).wrap_err(eyre::eyre!("failed to decode"))?; - assert_eq!(object,dec); - Ok(()) - } - } - -} +pub mod serialise; /*/// Dispatch params operations that can be handled at top level (i.e. `Help`) async fn dispatch_args() -> Result diff --git a/src/serialise.rs b/src/serialise.rs new file mode 100644 index 0000000..cd06475 --- /dev/null +++ b/src/serialise.rs @@ -0,0 +1,202 @@ +//! Serialisation helpers & traits +use super::*; +use tokio::io::{AsyncWrite,AsyncBufRead,AsyncRead,}; +use std::{ + marker::Unpin, + fmt, +}; + +pub trait TextSerialiseable: Sized +{ + fn serialise_text<'a, 'b: 'a, T: AsyncWrite+Unpin+?Sized>(&'a self, out: &'b mut T) -> LocalBoxFuture<'a, Result>; + fn deserialise_text<'a, T: AsyncBufRead+Unpin+?Sized>(input: &'a mut T) -> LocalBoxFuture<'a, Result> + where Self: 'a; + + #[inline] fn size_hint(&self) -> (usize, Option) + { + (0,None) + } +} + + +pub trait BinarySerialisable: Sized +{ + fn serialise_bytes<'a, 'b, T: AsyncWrite+Unpin+?Sized>(&'a self, out: &'b mut T) -> LocalBoxFuture<'a, Result> + where 'b: 'a; + fn deserialise_bytes<'b , T: AsyncRead+Unpin+?Sized>(input: &'b mut T) -> LocalBoxFuture<'b, Result> + where Self: 'b; + + #[inline] fn size_hint(&self) -> (usize, Option) + { + (0,None) + } +} + +pub trait Serialisable: BinarySerialisable + TextSerialiseable +{ + /// Attempt to immediately serialise this object into memory. + /// + /// Returns a newly allocated `Vec` containing the bytes. + /// Attempts to use the appropriate `size_hint` to allocate vector capacity if possible. + /// + /// # Notes + /// This function will fail if any `await`ed future within the `serialise_*` method cannot complete immediately. + #[instrument(skip(self))] + fn into_memory(&self, mode: Mode) -> Result, eyre::Report> + { + let mut output = match mode.size_hint(self) + { + (0, None) | (_, Some(0)) => Vec::new(), + (_, Some(x)) | (x, None) => Vec::with_capacity(x), + }; + trace!("Cap for membuf is {} bytes", output.capacity()); + let note = || format!("While serialising {} into memory", std::any::type_name::()); + let written = match mode { + Mode::Text => self.serialise_text(&mut output).now_or_never(), + Mode::Binary => self.serialise_bytes(&mut output).now_or_never(), + } + .ok_or_else(|| eyre::eyre!("Failed to complete {} write syncronously. A future returned `Poll::Pending`.", mode)) + .with_note(note.clone())? + .wrap_err_with(|| eyre::eyre!("Failed to write {} into memory", mode)) + .with_note(note)?; + if written != output.len() + { + error!("Function reported {} bytes written, but actually wrote {}", written, output.len()); + } + Ok(output) + } + + /// Attempt to immediately deserialise this object from memory + /// + /// Returns the object and the number of bytes read from `buf`. + /// + /// # Notes + /// This function will fail if any `await`ed future within the `deserialise_*` method cannot complete immediately. + #[instrument(skip(buf),fields(buf = ?buf.as_ref()))] + fn from_memory>(buf: T, mode: Mode) -> Result<(Self, usize), eyre::Report> + { + let buf = buf.as_ref(); + let mut reader = &buf[..]; + + trace!("Attempting read of up to {} bytes in {} mode to instantiate type {}", buf.len(), mode, std::any::type_name::()); + let note = || format!("While deserialising {} from memory", std::any::type_name::()); + let bytes = || format!("{}", buf.fmt_view()).header("Binary buffer was"); + let text = || String::from_utf8_lossy(buf).into_owned().header("Text buffer was"); + let value = match mode { + Mode::Binary => Self::deserialise_bytes(&mut reader).now_or_never(), + Mode::Text => Self::deserialise_text(&mut reader).now_or_never(), + } + .ok_or_else(|| eyre::eyre!("Failed to complete {} read syncronously. A future returned `Poll::Pending`.", mode)) + .with_note(note.clone())? + .wrap_err_with(|| eyre::eyre!("Failed to read {} from memory", mode)) + .with_note(note) + .with_section(bytes) + .with_section(text)?; + + let diff = (&reader[0] as *const u8 as usize) - (&buf[0] as *const u8 as usize); + + Ok((value, diff)) + } +} + +impl Serialisable for T +where T: BinarySerialisable + TextSerialiseable{} + +/// Serialisation mode for `Serialisable` types +#[derive(Debug, Clone, Copy)] +pub enum Mode +{ + /// Serialise as text + Text, + /// Serialise as binary + Binary, +} + +impl fmt::Display for Mode +{ + #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + match self { + Self::Text => write!(f, "text"), + Self::Binary => write!(f, "binary"), + } + } +} + + +impl Mode +{ + #[inline(always)] fn size_hint(&self, other: &T) -> (usize, Option) + { + match self { + Self::Text => TextSerialiseable::size_hint(other), + Self::Binary => BinarySerialisable::size_hint(other), + } + } +} + +/// Serialise this object into base64 text +/// +/// # TODO +/// Line wrapping +#[instrument(err, skip(value), fields(value_type=?std::any::type_name::()))] +pub fn into_text(value: &T) -> Result +{ + let bytes = serde_cbor::to_vec(value).map_err(Error::Serialise)?; + Ok(base64::encode(bytes)) +} + +#[derive(Debug)] +pub enum Error +{ + Base64(base64::DecodeError), + Serialise(serde_cbor::Error), + Deserialise(serde_cbor::Error), +} + +impl std::error::Error for Error +{ + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self { + Self::Base64(b) => Some(b), + Self::Serialise(b) => Some(b), + Self::Deserialise(b) => Some(b), + } + } +} + +impl std::fmt::Display for Error +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result + { + match self { + Self::Base64(_) => write!(f, "base64 decode failed"), + Self::Serialise(_) => write!(f, "serialisation failed"), + Self::Deserialise(_) => write!(f, "deserialisation failed"), + } + } +} + +/// Try to deserialise this object from text. +#[instrument(err, skip(string), fields(string = ?string.as_ref()))] +pub fn from_text(string: impl AsRef<[u8]>) -> Result +{ + let bytes = base64::decode(string).map_err(Error::Base64)?; + serde_cbor::from_reader(&bytes[..]).map_err(Error::Deserialise) +} + +#[cfg(test)] +mod tests +{ + use crate::*; + #[test] + fn test_ser() -> Result<(), eyre::Report> + { + let object = "hello world"; + let enc = super::into_text(&String::from(object)).wrap_err(eyre::eyre!("failed to encode"))?; + println!("Enc: {:?}", enc); + let dec: String = super::from_text(enc).wrap_err(eyre::eyre!("failed to decode"))?; + assert_eq!(object,dec); + Ok(()) + } +}