freeze reading and writing via CBOR

TODO: async and sync compression of this data
new-idea
Avril 4 years ago
parent 0a6c1304dc
commit 5327dadddf
Signed by: flanchan
GPG Key ID: 284488987C31F630

17
Cargo.lock generated

@ -531,6 +531,12 @@ dependencies = [
"tracing-futures",
]
[[package]]
name = "half"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3"
[[package]]
name = "hashbrown"
version = "0.9.1"
@ -1436,6 +1442,16 @@ dependencies = [
"serde_derive",
]
[[package]]
name = "serde_cbor"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e18acfa2f90e8b735b2836ab8d538de304cbb6729a7360729ea5a895d15a622"
dependencies = [
"half",
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.118"
@ -1980,6 +1996,7 @@ dependencies = [
"once_cell",
"pretty_env_logger",
"serde",
"serde_cbor",
"serde_json",
"sha2",
"smallmap",

@ -31,6 +31,7 @@ mopa = "0.2.2"
once_cell = "1.5.2"
pretty_env_logger = "0.4.0"
serde = {version = "1.0.118", features=["derive"]}
serde_cbor = "0.11.1"
serde_json = "1.0.61"
sha2 = "0.9.2"
smallmap = "1.3.0"

@ -40,3 +40,8 @@ pub const MNEMONIC_SALT_SIZE: usize = 16;
/// Mnemonic salt to use
pub const MNEMONIC_SALT: MnemonicSaltKind = MnemonicSaltKind::Random;
/// Max state image read size
///
/// Set to `0` for unlimited.
pub const MAX_IMAGE_READ_SIZE: usize = (1024 * 1024 * 1024) * 3; // 3GB

@ -1,6 +1,8 @@
//! Frozen, serialisable state
use super::*;
use futures::prelude::*;
use std::io;
use tokio::prelude::*;
/// An immutable image of `State`.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@ -10,6 +12,73 @@ pub struct Freeze
posts: Vec<(UserID, Post)>,
}
/// Reading and writing state
//TODO: Compression
impl Freeze
{
/// Serialise this instance into an output synchronously
pub fn write_sync(&self, output: impl io::Write) -> eyre::Result<()>
{
serde_cbor::to_writer(output, self)
.wrap_err(eyre!("Failed to write (sync) to output"))
}
/// Serialise this instance into an output asynchronously
///
/// # Notes
/// This function serialises to a heap buffer first
pub async fn write_async(&self, mut output: impl tokio::io::AsyncWrite + Unpin) -> eyre::Result<()>
{
let buf = serde_cbor::to_vec(self)
.wrap_err(eyre!("Failed to serialise to buffer"))?;
output.write_all(&buf[..]).await
.wrap_err(eyre!("Failed to write buffer to output stream"))?;
Ok(())
}
/// Deserialise an instance from an input synchronously
pub fn read_sync(input: impl io::Read) -> eyre::Result<Self>
{
serde_cbor::from_reader(input)
.wrap_err(eyre!("Failed to read (sync) from input"))
}
/// Deserialise an instance from an input asynchronously
///
/// # Notes
/// This function reads the entire input stream into a heap allocated buffer first
///
/// # Panics
/// If the buffer size exceeds `defaults::MAX_IMAGE_READ_SIZE`.
pub async fn read_async(mut input: impl tokio::io::AsyncRead + Unpin) -> eyre::Result<Self>
{
let whole = {
let mut whole = Vec::new();
let mut buffer = [0u8; 4096];
let mut read;
let mut done =0;
while { read = input.read(&mut buffer[..]).await.wrap_err(eyre!("Failed to read from input"))?; read > 0 }
{
whole.extend_from_slice(&buffer[..read]);
done+=read;
if defaults::MAX_IMAGE_READ_SIZE > 0 { //constant fn should
if done > defaults::MAX_IMAGE_READ_SIZE {
Err::<!, _>(eyre!("Image read exceeded max read size"))
.with_section(|| done.to_string().header("Current size is"))
.with_note(|| defaults::MAX_IMAGE_READ_SIZE.to_string().header("Max size is"))
.expect("Image read fatal"); //panic with this instance
}
}
}
whole
};
serde_cbor::from_slice(&whole)
.wrap_err(eyre!("Failed to deserialise buffer"))
}
}
impl Freeze
{
/// Create a `State` from this freeze
@ -39,6 +108,15 @@ impl Freeze
{
State::from_freeze(self)
}
/// Try to consume this `State` into a `Freeze`.
///
/// # Fails
/// See `State::try_into_freeze()`.
#[inline] pub fn try_from_state(state: State) -> Result<Self, State>
{
state.try_into_freeze()
}
}
impl State
@ -62,6 +140,32 @@ impl State
Freeze {users, posts}
}
/// Try to consume into a `Freeze`.
///
/// # Result
/// This method fails if there are more than one shared reference to the state
pub fn try_into_freeze(self) -> Result<Freeze, Self>
{
match Arc::try_unwrap(self.0) {
Ok(freeze) => {
let posts = freeze.posts.into_inner();
let users: HashSet<User> = {
posts.users.into_iter()
.map(|(_, x)| x.into_inner()).collect()
};
let posts: Vec<_> = {
posts.posts.into_iter()
.map(|(x, y)| y.into_iter().map(move |z| (x, z.into_inner()))).flatten()
//.map(|x| (x.0, x.1.into_inner()) )
.map(|(id, post)| (id, post)).collect()
};
Ok(Freeze {users, posts})
},
Err(e) => Err(Self(e))
}
}
/// Create `State` from this image
pub fn from_freeze(freeze: Freeze) -> Self
{
@ -89,3 +193,13 @@ impl From<Freeze> for State
Self::from_freeze(from)
}
}
impl TryFrom<State> for Freeze
{
type Error = State;
#[inline] fn try_from(from: State) -> Result<Self, Self::Error>
{
from.try_into_freeze()
}
}

Loading…
Cancel
Save