use serde::{Deserialize, Serialize};
use std::io;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SerializationFormat {
Bincode,
Json,
}
impl Default for SerializationFormat {
fn default() -> Self {
Self::Bincode
}
}
#[derive(Debug, thiserror::Error)]
pub enum SerializationError {
#[error("Bincode serialization error: {0}")]
Bincode(#[from] bincode::Error),
#[error("JSON serialization error: {0}")]
Json(#[from] serde_json::Error),
#[error("IO error: {0}")]
Io(#[from] io::Error),
}
pub struct Serializer {
format: SerializationFormat,
}
impl Serializer {
pub fn new(format: SerializationFormat) -> Self {
Self { format }
}
pub fn default() -> Self {
Self::new(SerializationFormat::default())
}
pub fn serialize<T: Serialize>(&self, value: &T) -> Result<Vec<u8>, SerializationError> {
match self.format {
SerializationFormat::Bincode => {
bincode::serialize(value).map_err(SerializationError::Bincode)
}
SerializationFormat::Json => {
serde_json::to_vec(value).map_err(SerializationError::Json)
}
}
}
pub fn deserialize<T: for<'de> Deserialize<'de>>(
&self,
bytes: &[u8],
) -> Result<T, SerializationError> {
match self.format {
SerializationFormat::Bincode => {
bincode::deserialize(bytes).map_err(SerializationError::Bincode)
}
SerializationFormat::Json => {
serde_json::from_slice(bytes).map_err(SerializationError::Json)
}
}
}
pub fn format(&self) -> SerializationFormat {
self.format
}
pub fn set_format(&mut self, format: SerializationFormat) {
self.format = format;
}
}
pub struct CRDTSerializer {
serializer: Serializer,
compression_enabled: bool,
}
impl CRDTSerializer {
pub fn new() -> Self {
Self {
serializer: Serializer::new(SerializationFormat::Bincode),
compression_enabled: true,
}
}
pub fn with_settings(format: SerializationFormat, compression: bool) -> Self {
Self {
serializer: Serializer::new(format),
compression_enabled: compression,
}
}
pub fn serialize_crdt<T: Serialize>(&self, value: &T) -> Result<Vec<u8>, SerializationError> {
let serialized = self.serializer.serialize(value)?;
if self.compression_enabled && serialized.len() > 1024 {
self.compress(&serialized)
} else {
Ok(serialized)
}
}
pub fn deserialize_crdt<T: for<'de> Deserialize<'de>>(
&self,
bytes: &[u8],
) -> Result<T, SerializationError> {
if self.is_compressed(bytes) {
let decompressed = self.decompress(bytes)?;
self.serializer.deserialize(&decompressed)
} else {
self.serializer.deserialize(bytes)
}
}
fn is_compressed(&self, bytes: &[u8]) -> bool {
bytes.len() > 0 && bytes[0] == 0x78 && bytes[1] == 0x9C
}
#[cfg(not(target_arch = "wasm32"))]
fn compress(&self, data: &[u8]) -> Result<Vec<u8>, SerializationError> {
#[cfg(feature = "compression")]
{
use flate2::write::DeflateEncoder;
use flate2::Compression;
use std::io::Write;
let mut encoder = DeflateEncoder::new(Vec::new(), Compression::default());
encoder.write_all(data)?;
Ok(encoder.finish()?)
}
#[cfg(not(feature = "compression"))]
{
Ok(data.to_vec())
}
}
#[cfg(target_arch = "wasm32")]
fn compress(&self, data: &[u8]) -> Result<Vec<u8>, SerializationError> {
Ok(data.to_vec())
}
#[cfg(not(target_arch = "wasm32"))]
fn decompress(&self, data: &[u8]) -> Result<Vec<u8>, SerializationError> {
#[cfg(feature = "compression")]
{
use flate2::read::DeflateDecoder;
use std::io::Read;
let mut decoder = DeflateDecoder::new(data);
let mut decompressed = Vec::new();
decoder.read_to_end(&mut decompressed)?;
Ok(decompressed)
}
#[cfg(not(feature = "compression"))]
{
Ok(data.to_vec())
}
}
#[cfg(target_arch = "wasm32")]
fn decompress(&self, data: &[u8]) -> Result<Vec<u8>, SerializationError> {
Ok(data.to_vec())
}
}
impl Default for CRDTSerializer {
fn default() -> Self {
Self::new()
}
}
pub mod benchmark {
use super::*;
use std::time::Instant;
pub fn benchmark_serialization<T: Serialize + for<'de> Deserialize<'de>>(
data: &T,
iterations: usize,
) -> SerializationBenchmark {
let mut bincode_serializer = Serializer::new(SerializationFormat::Bincode);
let mut json_serializer = Serializer::new(SerializationFormat::Json);
let _ = bincode_serializer.serialize(data);
let _ = json_serializer.serialize(data);
let start = Instant::now();
for _ in 0..iterations {
let _ = bincode_serializer.serialize(data);
}
let bincode_serialize_time = start.elapsed();
let serialized_bincode = bincode_serializer.serialize(data).unwrap();
let start = Instant::now();
for _ in 0..iterations {
let _ = bincode_serializer.deserialize::<T>(&serialized_bincode);
}
let bincode_deserialize_time = start.elapsed();
let start = Instant::now();
for _ in 0..iterations {
let _ = json_serializer.serialize(data);
}
let json_serialize_time = start.elapsed();
let serialized_json = json_serializer.serialize(data).unwrap();
let start = Instant::now();
for _ in 0..iterations {
let _ = json_serializer.deserialize::<T>(&serialized_json);
}
let json_deserialize_time = start.elapsed();
SerializationBenchmark {
bincode_serialize_time,
bincode_deserialize_time,
json_serialize_time,
json_deserialize_time,
bincode_size: serialized_bincode.len(),
json_size: serialized_json.len(),
iterations,
}
}
#[derive(Debug)]
pub struct SerializationBenchmark {
pub bincode_serialize_time: std::time::Duration,
pub bincode_deserialize_time: std::time::Duration,
pub json_serialize_time: std::time::Duration,
pub json_deserialize_time: std::time::Duration,
pub bincode_size: usize,
pub json_size: usize,
pub iterations: usize,
}
impl SerializationBenchmark {
pub fn serialize_improvement_ratio(&self) -> f64 {
self.json_serialize_time.as_nanos() as f64
/ self.bincode_serialize_time.as_nanos() as f64
}
pub fn size_reduction_ratio(&self) -> f64 {
self.json_size as f64 / self.bincode_size as f64
}
pub fn print_summary(&self) {
println!("=== Serialization Benchmark Results ===");
println!("Iterations: {}", self.iterations);
println!();
println!("Bincode:");
println!(" Serialize: {:?}", self.bincode_serialize_time);
println!(" Deserialize: {:?}", self.bincode_deserialize_time);
println!(" Size: {} bytes", self.bincode_size);
println!();
println!("JSON:");
println!(" Serialize: {:?}", self.json_serialize_time);
println!(" Deserialize: {:?}", self.json_deserialize_time);
println!(" Size: {} bytes", self.json_size);
println!();
println!("Improvements:");
println!(
" Serialize: {:.2}x faster",
self.serialize_improvement_ratio()
);
println!(" Size: {:.2}x smaller", self.size_reduction_ratio());
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::crdt::{LwwRegister, ReplicaId};
#[test]
fn test_serialization_formats() {
let data = LwwRegister::new("test_value".to_string(), ReplicaId::default());
let bincode_serializer = Serializer::new(SerializationFormat::Bincode);
let json_serializer = Serializer::new(SerializationFormat::Json);
let bincode_bytes = bincode_serializer.serialize(&data).unwrap();
let json_bytes = json_serializer.serialize(&data).unwrap();
let bincode_deserialized: LwwRegister<String> =
bincode_serializer.deserialize(&bincode_bytes).unwrap();
let json_deserialized: LwwRegister<String> =
json_serializer.deserialize(&json_bytes).unwrap();
assert_eq!(data, bincode_deserialized);
assert_eq!(data, json_deserialized);
assert!(bincode_bytes.len() < json_bytes.len());
}
#[test]
fn test_crdt_serializer() {
let data = LwwRegister::new("test_value".to_string(), ReplicaId::default());
let serializer = CRDTSerializer::new();
let serialized = serializer.serialize_crdt(&data).unwrap();
let deserialized: LwwRegister<String> = serializer.deserialize_crdt(&serialized).unwrap();
assert_eq!(data, deserialized);
}
#[test]
fn test_serialization_benchmark() {
let data = LwwRegister::new("test_value".to_string(), ReplicaId::default());
let benchmark = benchmark::benchmark_serialization(&data, 1000);
assert!(benchmark.serialize_improvement_ratio() > 1.0);
assert!(benchmark.size_reduction_ratio() > 1.0);
}
}