pub mod containers;
pub mod primitives;
pub mod rpc;
use crate::{buffer::Buffer, codec::Codec};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::{
error::Error,
hash::{Hash, Hasher},
io::{Cursor, ErrorKind, Read, Write},
marker::PhantomData,
ops::{Deref, DerefMut},
sync::Mutex,
};
pub type FullReplicated<T> = Replicated<(), T>;
pub type HashReplicated<T> = Replicated<HashRep, T>;
pub type MutReplicated<T> = Replicated<MutRep, T>;
pub type ManReplicated<T> = Replicated<ManRep, T>;
pub type CodecReplicated<P, T, C> = Replicated<P, CodecRep<T, C>>;
pub type FullCodecReplicated<T, C> = CodecReplicated<(), T, C>;
pub type HashCodecReplicated<T, C> = CodecReplicated<HashRep, T, C>;
pub type MutCodecReplicated<T, C> = CodecReplicated<MutRep, T, C>;
pub type ManCodecReplicated<T, C> = CodecReplicated<ManRep, T, C>;
pub trait ReplicationPolicy<T>
where
Self: Sized + Default,
T: Replicable,
{
fn did_changed(&self, data: &T) -> Option<Self>;
#[allow(unused_variables)]
fn on_mutation(&mut self, data: &T) {}
}
#[derive(Default)]
pub struct HashRep(u64);
impl<T> ReplicationPolicy<T> for HashRep
where
T: Replicable + Hash,
{
fn did_changed(&self, data: &T) -> Option<Self> {
let hash = crate::hash(data);
if self.0 != hash {
Some(Self(hash))
} else {
None
}
}
}
impl<T> ReplicationPolicy<T> for ()
where
T: Replicable,
{
fn did_changed(&self, _: &T) -> Option<Self> {
Some(())
}
}
pub struct MutRep(bool);
impl Default for MutRep {
fn default() -> Self {
Self(true)
}
}
impl<T: Replicable> ReplicationPolicy<T> for MutRep {
fn did_changed(&self, _: &T) -> Option<Self> {
if self.0 { Some(Self(false)) } else { None }
}
fn on_mutation(&mut self, _data: &T) {
self.0 = true;
}
}
pub struct ManRep(bool);
impl Default for ManRep {
fn default() -> Self {
Self(true)
}
}
impl<T: Replicable> ReplicationPolicy<T> for ManRep {
fn did_changed(&self, _: &T) -> Option<Self> {
if self.0 { Some(Self(false)) } else { None }
}
}
pub trait Replicable: Sized {
fn collect_changes(&self, buffer: &mut Buffer) -> Result<(), Box<dyn Error>>;
fn apply_changes(&mut self, buffer: &mut Buffer) -> Result<(), Box<dyn Error>>;
}
impl Replicable for () {
fn collect_changes(&self, _: &mut Buffer) -> Result<(), Box<dyn Error>> {
Ok(())
}
fn apply_changes(&mut self, _: &mut Buffer) -> Result<(), Box<dyn Error>> {
Ok(())
}
}
macro_rules! impl_replicable_tuple {
( $( $id:ident ),+ ) => {
#[allow(non_snake_case)]
impl<$( $id ),+> Replicable for ( $( $id, )+ )
where
$( $id: Replicable ),+
{
fn collect_changes(&self, buffer: &mut Buffer) -> Result<(), Box<dyn Error>> {
let ( $( $id, )+ ) = self;
$(
$id.collect_changes(buffer)?;
)+
Ok(())
}
fn apply_changes(&mut self, buffer: &mut Buffer) -> Result<(), Box<dyn Error>> {
let ( $( $id, )+ ) = self;
$(
$id.apply_changes(buffer)?;
)+
Ok(())
}
}
};
}
impl_replicable_tuple!(A);
impl_replicable_tuple!(A, B);
impl_replicable_tuple!(A, B, C);
impl_replicable_tuple!(A, B, C, D);
impl_replicable_tuple!(A, B, C, D, E);
impl_replicable_tuple!(A, B, C, D, E, F);
impl_replicable_tuple!(A, B, C, D, E, F, G);
impl_replicable_tuple!(A, B, C, D, E, F, G, H);
impl_replicable_tuple!(A, B, C, D, E, F, G, H, I);
impl_replicable_tuple!(A, B, C, D, E, F, G, H, I, J);
impl_replicable_tuple!(A, B, C, D, E, F, G, H, I, J, K);
impl_replicable_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
impl_replicable_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M);
impl_replicable_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
impl_replicable_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
impl_replicable_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
pub struct Replicated<P, T>
where
P: ReplicationPolicy<T>,
T: Replicable,
{
data: T,
meta: Mutex<P>,
}
impl<P, T> Replicated<P, T>
where
P: ReplicationPolicy<T>,
T: Replicable,
{
pub fn new(data: T) -> Self {
Self {
meta: Mutex::new(P::default()),
data,
}
}
pub fn new_unchanged(data: T) -> Self {
let meta = P::default();
meta.did_changed(&data);
Self {
meta: Mutex::new(meta),
data,
}
}
pub fn into_inner(self) -> T {
self.data
}
pub fn did_changed(this: &Self) -> Result<bool, Box<dyn Error>> {
Ok(this
.meta
.lock()
.map_err(|_| "Replicated meta lock error")?
.did_changed(&this.data)
.is_some())
}
pub fn collect_changes(this: &Self, buffer: &mut Buffer) -> Result<bool, Box<dyn Error>> {
let mut meta = this.meta.lock().map_err(|_| "Replicated meta lock error")?;
if let Some(new_meta) = meta.did_changed(&this.data) {
*meta = new_meta;
this.data.collect_changes(buffer)?;
Ok(true)
} else {
Ok(false)
}
}
pub fn maybe_collect_changes<const TAG: u8>(
this: &Self,
buffer: &mut Buffer,
) -> Result<bool, Box<dyn Error>> {
let mut meta = this.meta.lock().map_err(|_| "Replicated meta lock error")?;
if let Some(new_meta) = meta.did_changed(&this.data) {
*meta = new_meta;
let mut temp_buffer = Cursor::new(Vec::new());
this.data.collect_changes(&mut temp_buffer)?;
if temp_buffer.get_ref().is_empty() {
return Ok(false);
}
buffer.write_all(&[TAG])?;
let data_length = temp_buffer.get_ref().len() as u64;
leb128::write::unsigned(buffer, data_length)?;
buffer.write_all(temp_buffer.get_ref())?;
Ok(true)
} else {
Ok(false)
}
}
pub fn apply_changes(this: &mut Self, buffer: &mut Buffer) -> Result<(), Box<dyn Error>> {
this.data.apply_changes(buffer)?;
Ok(())
}
pub fn maybe_apply_changes<const TAG: u8>(
this: &mut Self,
buffer: &mut Buffer,
) -> Result<(), Box<dyn Error>> {
let position = buffer.position();
let mut tag_buf = [0u8; 1];
match buffer.read_exact(&mut tag_buf) {
Ok(_) => {}
Err(e) if e.kind() == ErrorKind::UnexpectedEof => {
buffer.set_position(position);
return Ok(());
}
Err(e) => return Err(Box::new(e)),
}
if tag_buf[0] != TAG {
buffer.set_position(position);
return Ok(());
}
let data_length = leb128::read::unsigned(buffer)?;
let start_position = buffer.position();
Self::apply_changes(this, buffer)?;
let end_position = buffer.position();
if (end_position - start_position) != data_length {
buffer.set_position(position);
return Err(format!(
"Data length mismatch: expected {}, got {}",
data_length,
end_position - start_position
)
.into());
}
Ok(())
}
pub fn mark_changed(this: &mut Self) {
*this
.meta
.lock()
.unwrap_or_else(|_| panic!("Replicated meta lock error")) = P::default();
}
}
impl<P, T> Serialize for Replicated<P, T>
where
P: ReplicationPolicy<T>,
T: Replicable + Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.data.serialize(serializer)
}
}
impl<'de, P, T> Deserialize<'de> for Replicated<P, T>
where
P: ReplicationPolicy<T>,
T: Replicable + Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let data = T::deserialize(deserializer)?;
let meta = Mutex::new(P::default());
Ok(Self { data, meta })
}
}
impl<P, T> std::fmt::Debug for Replicated<P, T>
where
P: ReplicationPolicy<T>,
T: Replicable + std::fmt::Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(&self.data, f)
}
}
impl<P, T> std::fmt::Display for Replicated<P, T>
where
P: ReplicationPolicy<T>,
T: Replicable + std::fmt::Display,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.data, f)
}
}
impl<P, T> AsRef<T> for Replicated<P, T>
where
P: ReplicationPolicy<T>,
T: Replicable,
{
fn as_ref(&self) -> &T {
&self.data
}
}
impl<P, T> AsMut<T> for Replicated<P, T>
where
P: ReplicationPolicy<T>,
T: Replicable,
{
fn as_mut(&mut self) -> &mut T {
&mut self.data
}
}
impl<P, T> From<T> for Replicated<P, T>
where
P: ReplicationPolicy<T>,
T: Replicable,
{
fn from(data: T) -> Self {
Self::new(data)
}
}
impl<P, T> Default for Replicated<P, T>
where
P: ReplicationPolicy<T>,
T: Replicable + Default,
{
fn default() -> Self {
let data = T::default();
Self {
meta: Mutex::new(P::default()),
data,
}
}
}
impl<P, T> Clone for Replicated<P, T>
where
P: ReplicationPolicy<T>,
T: Replicable + Clone,
{
fn clone(&self) -> Self {
Self {
meta: Mutex::new(P::default()),
data: self.data.clone(),
}
}
}
impl<P, T> PartialEq for Replicated<P, T>
where
P: ReplicationPolicy<T>,
T: Replicable + PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.data == other.data
}
}
impl<P, T> Eq for Replicated<P, T>
where
P: ReplicationPolicy<T>,
T: Replicable + Eq,
{
}
impl<P, T> PartialOrd for Replicated<P, T>
where
P: ReplicationPolicy<T>,
T: Replicable + PartialOrd,
{
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.data.partial_cmp(&other.data)
}
}
impl<P, T> Ord for Replicated<P, T>
where
P: ReplicationPolicy<T>,
T: Replicable + Ord,
{
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.data.cmp(&other.data)
}
}
impl<P, T> Hash for Replicated<P, T>
where
P: ReplicationPolicy<T>,
T: Replicable + Hash,
{
fn hash<H: Hasher>(&self, state: &mut H) {
self.data.hash(state);
}
}
impl<P, T> Deref for Replicated<P, T>
where
P: ReplicationPolicy<T>,
T: Replicable,
{
type Target = T;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<P, T> DerefMut for Replicated<P, T>
where
P: ReplicationPolicy<T>,
T: Replicable,
{
fn deref_mut(&mut self) -> &mut Self::Target {
self.meta
.lock()
.unwrap_or_else(|_| panic!("Replicated meta lock error"))
.on_mutation(&self.data);
&mut self.data
}
}
pub struct CodecRep<T, C: Codec<Value = T>> {
data: T,
_phantom: PhantomData<fn() -> C>,
}
impl<T: Replicable, C: Codec<Value = T>> CodecRep<T, C> {
pub fn new(data: T) -> Self {
Self {
data,
_phantom: PhantomData,
}
}
pub fn into_inner(self) -> T {
self.data
}
}
impl<T: Serialize, C: Codec<Value = T>> Serialize for CodecRep<T, C> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.data.serialize(serializer)
}
}
impl<'de, T: Deserialize<'de>, C: Codec<Value = T>> Deserialize<'de> for CodecRep<T, C> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let data = T::deserialize(deserializer)?;
Ok(Self {
data,
_phantom: PhantomData,
})
}
}
impl<T, C: Codec<Value = T>> Replicable for CodecRep<T, C> {
fn collect_changes(&self, buffer: &mut Buffer) -> Result<(), Box<dyn Error>> {
C::encode(&self.data, buffer)
}
fn apply_changes(&mut self, buffer: &mut Buffer) -> Result<(), Box<dyn Error>> {
self.data = C::decode(buffer)?;
Ok(())
}
}
impl<T: std::fmt::Debug, C: Codec<Value = T>> std::fmt::Debug for CodecRep<T, C> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(&self.data, f)
}
}
impl<T: std::fmt::Display, C: Codec<Value = T>> std::fmt::Display for CodecRep<T, C> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.data, f)
}
}
impl<T, C: Codec<Value = T>> AsRef<T> for CodecRep<T, C> {
fn as_ref(&self) -> &T {
&self.data
}
}
impl<T, C: Codec<Value = T>> AsMut<T> for CodecRep<T, C> {
fn as_mut(&mut self) -> &mut T {
&mut self.data
}
}
impl<T, C: Codec<Value = T>> From<T> for CodecRep<T, C> {
fn from(data: T) -> Self {
Self {
data,
_phantom: PhantomData,
}
}
}
impl<T: Default, C: Codec<Value = T>> Default for CodecRep<T, C> {
fn default() -> Self {
Self {
data: T::default(),
_phantom: PhantomData,
}
}
}
impl<T: Clone, C: Codec<Value = T>> Clone for CodecRep<T, C> {
fn clone(&self) -> Self {
Self {
data: self.data.clone(),
_phantom: PhantomData,
}
}
}
impl<T: PartialEq, C: Codec<Value = T>> PartialEq for CodecRep<T, C> {
fn eq(&self, other: &Self) -> bool {
self.data == other.data
}
}
impl<T: Eq, C: Codec<Value = T>> Eq for CodecRep<T, C> {}
impl<T: PartialOrd, C: Codec<Value = T>> PartialOrd for CodecRep<T, C> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.data.partial_cmp(&other.data)
}
}
impl<T: Ord, C: Codec<Value = T>> Ord for CodecRep<T, C> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.data.cmp(&other.data)
}
}
impl<T: Hash, C: Codec<Value = T>> Hash for CodecRep<T, C> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.data.hash(state);
}
}
impl<T, C: Codec<Value = T>> Deref for CodecRep<T, C> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<T, C: Codec<Value = T>> DerefMut for CodecRep<T, C> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.data
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{codec::postcard::PostcardCodec, replication::primitives::RepF32};
use serde::{Deserialize, Serialize};
use std::{error::Error, io::Cursor};
pub type HashPostcardReplicated<T> = HashCodecReplicated<T, PostcardCodec<T>>;
pub type MutPostcardReplicated<T> = MutCodecReplicated<T, PostcardCodec<T>>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
struct Foo {
a: u32,
b: bool,
}
impl Replicable for Foo {
fn collect_changes(&self, buffer: &mut Buffer) -> Result<(), Box<dyn Error>> {
self.a.collect_changes(buffer)?;
self.b.collect_changes(buffer)?;
Ok(())
}
fn apply_changes(&mut self, buffer: &mut Buffer) -> Result<(), Box<dyn Error>> {
self.a.apply_changes(buffer)?;
self.b.apply_changes(buffer)?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
struct Bar {
a: f32,
foo: HashPostcardReplicated<Foo>,
}
#[derive(Debug, Default, Clone, PartialEq, Hash)]
struct Zee {
a: HashReplicated<i32>,
b: HashReplicated<RepF32>,
}
impl Replicable for Zee {
fn collect_changes(&self, buffer: &mut Buffer) -> Result<(), Box<dyn Error>> {
Replicated::maybe_collect_changes::<0>(&self.a, buffer)?;
Replicated::maybe_collect_changes::<1>(&self.b, buffer)?;
Ok(())
}
fn apply_changes(&mut self, buffer: &mut Buffer) -> Result<(), Box<dyn Error>> {
Replicated::maybe_apply_changes::<0>(&mut self.a, buffer)?;
Replicated::maybe_apply_changes::<1>(&mut self.b, buffer)?;
Ok(())
}
}
#[test]
fn test_hashed_replication() {
let mut data = HashReplicated::new(Foo { a: 42, b: false });
let mut buffer = Cursor::default();
assert!(Replicated::collect_changes(&data, &mut buffer).unwrap());
assert_eq!(buffer.get_ref().len(), 2);
let mut buffer = Cursor::default();
assert!(!Replicated::collect_changes(&data, &mut buffer).unwrap());
assert_eq!(buffer.get_ref().len(), 0);
data.a = 100;
data.b = true;
let mut buffer = Cursor::default();
assert!(Replicated::collect_changes(&data, &mut buffer).unwrap());
assert_eq!(buffer.get_ref().len(), 2);
let mut data2 = HashReplicated::new(Foo { a: 42, b: false });
let buffer = buffer.into_inner();
let mut cursor = Cursor::new(buffer);
Replicated::apply_changes(&mut data2, &mut cursor).unwrap();
assert_eq!(data2.a, 100);
assert!(data2.b);
}
#[test]
fn test_mutated_replication() {
let mut data = MutReplicated::new(Foo { a: 42, b: false });
let mut buffer = Cursor::default();
assert!(Replicated::collect_changes(&data, &mut buffer).unwrap());
assert_eq!(buffer.get_ref().len(), 2);
let mut buffer = Cursor::default();
assert!(!Replicated::collect_changes(&data, &mut buffer).unwrap());
assert_eq!(buffer.get_ref().len(), 0);
data.a = 100;
let mut buffer = Cursor::default();
assert!(Replicated::collect_changes(&data, &mut buffer).unwrap());
assert_eq!(buffer.get_ref().len(), 2);
let mut data2 = MutReplicated::new(Foo { a: 42, b: false });
let buffer = buffer.into_inner();
let mut cursor = Cursor::new(buffer);
Replicated::apply_changes(&mut data2, &mut cursor).unwrap();
assert_eq!(data2.a, 100);
assert!(!data2.b);
}
#[test]
fn test_manual_replication() {
let mut data = ManReplicated::new(Foo { a: 42, b: false });
let mut buffer = Cursor::default();
assert!(Replicated::collect_changes(&data, &mut buffer).unwrap());
assert_eq!(buffer.get_ref().len(), 2);
let mut buffer = Cursor::default();
assert!(!Replicated::collect_changes(&data, &mut buffer).unwrap());
assert_eq!(buffer.get_ref().len(), 0);
data.b = true;
let mut buffer = Cursor::default();
assert!(!Replicated::collect_changes(&data, &mut buffer).unwrap());
assert_eq!(buffer.get_ref().len(), 0);
Replicated::mark_changed(&mut data);
let mut buffer = Cursor::default();
assert!(Replicated::collect_changes(&data, &mut buffer).unwrap());
assert_eq!(buffer.get_ref().len(), 2);
let mut data2 = ManReplicated::new(Foo { a: 42, b: false });
let buffer = buffer.into_inner();
let mut cursor = Cursor::new(buffer);
Replicated::apply_changes(&mut data2, &mut cursor).unwrap();
assert_eq!(data2.a, 42);
assert!(data2.b);
}
#[test]
fn test_codec_replication() {
let mut data = HashPostcardReplicated::<Foo>::new(Foo { a: 42, b: false }.into());
let mut buffer = Cursor::default();
assert!(Replicated::collect_changes(&data, &mut buffer).unwrap());
assert_eq!(buffer.get_ref().len(), 10);
let mut buffer = Cursor::default();
assert!(!Replicated::collect_changes(&data, &mut buffer).unwrap());
assert_eq!(buffer.get_ref().len(), 0);
data.a = 100;
let mut buffer = Cursor::default();
assert!(Replicated::collect_changes(&data, &mut buffer).unwrap());
assert_eq!(buffer.get_ref().len(), 10);
let mut data2 = HashPostcardReplicated::<Foo>::new(Foo { a: 42, b: false }.into());
let buffer = buffer.into_inner();
let mut cursor = Cursor::new(buffer);
Replicated::apply_changes(&mut data2, &mut cursor).unwrap();
assert_eq!(data2.a, 100);
assert!(!data2.b);
}
#[test]
fn test_nested_replication() {
let mut data = MutPostcardReplicated::<Bar>::new(
Bar {
a: 4.2,
foo: HashPostcardReplicated::<Foo>::new(Foo { a: 42, b: false }.into()),
}
.into(),
);
let mut buffer = Cursor::default();
assert!(Replicated::collect_changes(&data, &mut buffer).unwrap());
assert_eq!(buffer.get_ref().len(), 14);
let mut buffer = Cursor::default();
assert!(!Replicated::collect_changes(&data, &mut buffer).unwrap());
assert_eq!(buffer.get_ref().len(), 0);
data.a = 2.71;
data.foo.a = 100;
let mut buffer = Cursor::default();
assert!(Replicated::collect_changes(&data, &mut buffer).unwrap());
assert_eq!(buffer.get_ref().len(), 14);
let mut data2 = MutPostcardReplicated::<Bar>::new(
Bar {
a: 4.2,
foo: HashPostcardReplicated::<Foo>::new(Foo { a: 42, b: false }.into()),
}
.into(),
);
let buffer = buffer.into_inner();
let mut cursor = Cursor::new(buffer);
Replicated::apply_changes(&mut data2, &mut cursor).unwrap();
assert_eq!(data2.a, 2.71);
assert_eq!(data2.foo.a, 100);
assert!(!data2.foo.b);
}
#[test]
fn test_partial_replication() {
let mut data = HashReplicated::new(Zee {
a: HashReplicated::new(10),
b: HashReplicated::new(RepF32(4.2)),
});
let mut buffer = Cursor::default();
assert!(Replicated::collect_changes(&data, &mut buffer).unwrap());
assert_eq!(buffer.get_ref().len(), 9);
let buffer = buffer.into_inner();
let mut cursor = Cursor::new(buffer);
let mut data2 = HashReplicated::new(Zee::default());
Replicated::apply_changes(&mut data2, &mut cursor).unwrap();
assert_eq!(*data2.a, 10);
assert_eq!(data2.b.0, 4.2);
let mut buffer = Cursor::default();
assert!(!Replicated::collect_changes(&data, &mut buffer).unwrap());
assert_eq!(buffer.get_ref().len(), 0);
*data.a = 20;
let mut buffer = Cursor::default();
assert!(Replicated::collect_changes(&data, &mut buffer).unwrap());
assert_eq!(buffer.get_ref().len(), 3);
let buffer = buffer.into_inner();
let mut cursor = Cursor::new(buffer);
let mut data2 = HashReplicated::new(Zee::default());
Replicated::apply_changes(&mut data2, &mut cursor).unwrap();
assert_eq!(*data2.a, 20);
assert_eq!(data2.b.0, 0.0);
**data.b = 5.7;
let mut buffer = Cursor::default();
assert!(Replicated::collect_changes(&data, &mut buffer).unwrap());
assert_eq!(buffer.get_ref().len(), 6);
let buffer = buffer.into_inner();
let mut cursor = Cursor::new(buffer);
let mut data2 = HashReplicated::new(Zee::default());
Replicated::apply_changes(&mut data2, &mut cursor).unwrap();
assert_eq!(*data2.a, 0);
assert_eq!(data2.b.0, 5.7);
}
}