use crate::misc::AccountError;
use bincode::BincodeRead;
use bytes::BufMut;
use bytes::BytesMut;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
pub trait SyncIO {
fn serialize_to_vector(&self) -> Result<Vec<u8>, AccountError>
where
Self: Serialize,
{
type_to_bytes(self)
}
fn deserialize_from_vector<'a>(input: &'a [u8]) -> Result<Self, AccountError>
where
Self: Deserialize<'a>,
{
bytes_to_type(input)
}
fn deserialize_from_owned_vector(input: Vec<u8>) -> Result<Self, AccountError>
where
Self: DeserializeOwned,
{
use bytes::Buf;
bincode::deserialize_from(input.reader()).map_err(|err| {
citadel_io::error!(
citadel_io::ErrorCode::DeserializationFailed,
err.to_string()
)
})
}
fn deserialize_in_place<'a, R, T>(reader: R, place: &mut T) -> Result<(), AccountError>
where
T: serde::de::Deserialize<'a>,
R: BincodeRead<'a>,
{
bincode::deserialize_in_place(reader, place).map_err(|err| {
citadel_io::error!(
citadel_io::ErrorCode::DeserializationFailed,
err.to_string()
)
})
}
fn serialize_into_buf(&self, buf: &mut BytesMut) -> Result<(), AccountError>
where
Self: Serialize,
{
bincode::serialized_size(self)
.and_then(|amt| {
buf.reserve(amt as usize);
bincode::serialize_into(buf.writer(), self)
})
.map_err(|err| {
citadel_io::error!(citadel_io::ErrorCode::SerializationFailed, err.to_string())
})
}
fn serialize_into_slice(&self, slice: &mut [u8]) -> Result<(), AccountError>
where
Self: Serialize,
{
bincode::serialize_into(slice, self).map_err(|err| {
citadel_io::error!(citadel_io::ErrorCode::SerializationFailed, err.to_string())
})
}
fn serialized_size(&self) -> Option<usize>
where
Self: Serialize,
{
bincode::serialized_size(self).ok().map(|res| res as usize)
}
}
impl<'a, T> SyncIO for T where T: Serialize + Deserialize<'a> + Sized {}
fn bytes_to_type<'a, D: Deserialize<'a>>(bytes: &'a [u8]) -> Result<D, AccountError> {
bincode::deserialize(bytes).map_err(|err| {
citadel_io::error!(
citadel_io::ErrorCode::DeserializationFailed,
err.to_string()
)
})
}
fn type_to_bytes<D: Serialize>(input: D) -> Result<Vec<u8>, AccountError> {
bincode::serialize(&input).map_err(|err| {
citadel_io::error!(citadel_io::ErrorCode::SerializationFailed, err.to_string())
})
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Sample {
id: u64,
name: String,
flags: Vec<bool>,
}
fn sample() -> Sample {
Sample {
id: 1234,
name: "alice".to_string(),
flags: vec![true, false, true],
}
}
#[test]
fn vector_roundtrip() {
let s = sample();
let bytes = s.serialize_to_vector().unwrap();
assert_eq!(Sample::deserialize_from_vector(&bytes).unwrap(), s);
assert_eq!(Sample::deserialize_from_owned_vector(bytes).unwrap(), s);
}
#[test]
fn buf_and_slice_roundtrip() {
let s = sample();
let mut buf = BytesMut::with_capacity(8);
s.serialize_into_buf(&mut buf).unwrap();
assert_eq!(Sample::deserialize_from_vector(&buf).unwrap(), s);
let size = s.serialized_size().unwrap();
assert_eq!(size, s.serialize_to_vector().unwrap().len());
let mut slice = vec![0u8; size];
s.serialize_into_slice(&mut slice).unwrap();
assert_eq!(Sample::deserialize_from_vector(&slice).unwrap(), s);
}
#[test]
fn serialize_into_undersized_slice_errors() {
let s = sample();
let mut tiny = [0u8; 1];
assert!(s.serialize_into_slice(&mut tiny).is_err());
}
#[test]
fn deserialize_garbage_errors() {
assert!(Sample::deserialize_from_vector(&[0xFFu8; 2]).is_err());
assert!(Sample::deserialize_from_owned_vector(vec![0xFFu8; 2]).is_err());
}
}