ozzy_replication/wire/
compact.rs1use ozzy_proto::{Opcode, layout::Fixed};
4
5use crate::{
6 OpNumber, Prefix,
7 flow::{FlowError, Report},
8};
9
10use super::WireError;
11
12pub const COMPACT_STATE_BYTES: usize = 29;
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub struct CompactState {
20 pub handle: u32,
22 pub revision: u64,
24 pub received: OpNumber,
26 pub received_bytes: u64,
28}
29
30impl CompactState {
31 pub fn from_report(handle: u32, report: Report) -> Result<Self, WireError> {
33 report.validate_shape()?;
34 let state = Self {
35 handle,
36 revision: report.revision,
37 received: report.received.op,
38 received_bytes: report.received_bytes,
39 };
40 state.validate()?;
41 Ok(state)
42 }
43
44 fn validate(self) -> Result<(), WireError> {
45 if self.handle == 0 || self.revision == 0 || self.received.0 == u64::MAX {
46 return Err(FlowError::Report.into());
47 }
48 Ok(())
49 }
50
51 pub fn encode(self) -> Result<[u8; COMPACT_STATE_BYTES], WireError> {
53 self.validate()?;
54 let mut bytes = [0; COMPACT_STATE_BYTES];
55 bytes[0] = Opcode::ReplicaReceipt as u8;
56 bytes[1..5].copy_from_slice(&self.handle.to_be_bytes());
57 for (chunk, value) in bytes[5..].as_chunks_mut::<8>().0.iter_mut().zip([
58 self.revision,
59 self.received.0,
60 self.received_bytes,
61 ]) {
62 chunk.copy_from_slice(&value.to_be_bytes());
63 }
64 Ok(bytes)
65 }
66
67 pub fn decode(bytes: &[u8]) -> Result<Self, WireError> {
69 let bytes: &[u8; COMPACT_STATE_BYTES] = bytes.try_into().map_err(|_| WireError::Length)?;
70 if bytes.u8_at::<0>() != Opcode::ReplicaReceipt as u8 {
71 return Err(WireError::UnsupportedCommand);
72 }
73 let state = Self {
74 handle: bytes.u32_at::<1>(),
75 revision: bytes.u64_at::<5>(),
76 received: OpNumber(bytes.u64_at::<13>()),
77 received_bytes: bytes.u64_at::<21>(),
78 };
79 state.validate()?;
80 Ok(state)
81 }
82
83 pub fn report(self, bound: Report, received: Prefix) -> Result<Report, WireError> {
87 self.validate()?;
88 if received.op != self.received {
89 return Err(WireError::History);
90 }
91 let report = Report {
92 revision: self.revision,
93 received,
94 received_bytes: self.received_bytes,
95
96 ..bound
97 };
98 report.validate_shape()?;
99 Ok(report)
100 }
101}