use bincode::{Decode, Encode};
use bytes::{BufMut, Bytes, BytesMut};
use serde::de::DeserializeOwned;
use tracing::error;
pub const SERIALIZATION_VERSION_V1: u8 = 1u8;
pub const SERIALIZATION_VERSION_V2: u8 = 2u8;
pub fn serialize<T: Encode>(value: &T) -> Result<Bytes, String> {
let data = bincode::encode_to_vec(value, bincode::config::standard())
.map_err(|e| format!("Failed to serialize value: {e}"))?;
let mut bytes = BytesMut::new();
bytes.put_u8(SERIALIZATION_VERSION_V2);
bytes.extend_from_slice(&data);
Ok(bytes.freeze())
}
pub fn deserialize<T: DeserializeOwned + Decode>(bytes: &[u8]) -> Result<T, String> {
let (version, data) = bytes.split_at(1);
deserialize_with_version(data, version[0])
}
pub fn try_deserialize<T: DeserializeOwned + Decode>(bytes: &[u8]) -> Result<Option<T>, String> {
if bytes.is_empty() {
Ok(None)
} else {
let (version, data) = bytes.split_at(1);
try_deserialize_with_version(data, version[0])
}
}
pub fn deserialize_with_version<T: DeserializeOwned + Decode>(
data: &[u8],
version: u8,
) -> Result<T, String> {
match try_deserialize_with_version(data, version)? {
Some(value) => Ok(value),
None => {
error!(
"invalid serialization version: {}, full data set: {:?}",
version, data
);
panic!("invalid serialization version: {}", version)
}
}
}
pub fn try_deserialize_with_version<T: DeserializeOwned + Decode>(
data: &[u8],
version: u8,
) -> Result<Option<T>, String> {
match version {
SERIALIZATION_VERSION_V1 => {
let value = serde_json::from_slice(data)
.map_err(|e| format!("Failed to deserialize value: {e}"))?;
Ok(Some(value))
}
SERIALIZATION_VERSION_V2 => {
let (entry, _) = bincode::decode_from_slice(data, bincode::config::standard())
.map_err(|e| format!("Failed to deserialize value: {e}"))?;
Ok(Some(entry))
}
_ => Ok(None),
}
}
#[cfg(test)]
mod tests {
use bincode::{Decode, Encode};
use rand::distributions::Alphanumeric;
use rand::Rng;
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Encode, Decode)]
enum Example {
First(String),
Second { x: i64, y: bool, z: Box<Example> },
}
impl Example {
pub fn random(rng: &mut impl Rng) -> Example {
match rng.gen_range(0..2) {
0 => Example::First(
rng.sample_iter(Alphanumeric)
.take(7)
.map(char::from)
.collect(),
),
1 => Example::Second {
x: rng.gen::<i64>(),
y: rng.gen::<bool>(),
z: Box::new(Example::random(rng)),
},
_ => unreachable!(),
}
}
}
#[test]
pub fn roundtrip() {
let mut rng = rand::thread_rng();
for _ in 0..1000 {
let example = Example::random(&mut rng);
let serialized = super::serialize(&example).unwrap();
let deserialized = super::deserialize(&serialized).unwrap();
assert_eq!(example, deserialized);
}
}
#[test]
pub fn from_v1() {
let mut rng = rand::thread_rng();
for _ in 0..1000 {
let example = Example::random(&mut rng);
let serialized = serde_json::to_vec(&example).unwrap();
let prefixed = [vec![1], serialized].concat();
let deserialized = super::deserialize(&prefixed).unwrap();
assert_eq!(example, deserialized);
}
}
#[test]
pub fn try_deserialize_without_version() {
let mut rng = rand::thread_rng();
for _ in 0..1000 {
let example = Example::random(&mut rng);
let serialized = serde_json::to_vec(&example).unwrap();
let result: Option<Example> = super::try_deserialize(&serialized).unwrap();
assert_eq!(result, None);
}
}
}