use ozzy_proto::Opcode;
use crate::{
OpNumber, Prefix,
flow::{FlowError, Report},
};
use super::WireError;
pub const COMPACT_STATE_BYTES: usize = 29;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CompactState {
pub handle: u32,
pub revision: u64,
pub received: OpNumber,
pub received_bytes: u64,
}
impl CompactState {
pub fn from_report(handle: u32, report: Report) -> Result<Self, WireError> {
report.validate_shape()?;
let state = Self {
handle,
revision: report.revision,
received: report.received.op,
received_bytes: report.received_bytes,
};
state.validate()?;
Ok(state)
}
fn validate(self) -> Result<(), WireError> {
if self.handle == 0 || self.revision == 0 || self.received.0 == u64::MAX {
return Err(FlowError::Report.into());
}
Ok(())
}
pub fn encode(self) -> Result<[u8; COMPACT_STATE_BYTES], WireError> {
self.validate()?;
let mut bytes = [0; COMPACT_STATE_BYTES];
bytes[0] = Opcode::ReplicaReceipt as u8;
bytes[1..5].copy_from_slice(&self.handle.to_be_bytes());
for (chunk, value) in bytes[5..].as_chunks_mut::<8>().0.iter_mut().zip([
self.revision,
self.received.0,
self.received_bytes,
]) {
chunk.copy_from_slice(&value.to_be_bytes());
}
Ok(bytes)
}
pub fn decode(bytes: &[u8]) -> Result<Self, WireError> {
if bytes.len() != COMPACT_STATE_BYTES {
return Err(WireError::Length);
}
if bytes[0] != Opcode::ReplicaReceipt as u8 {
return Err(WireError::UnsupportedCommand);
}
let read = |offset| u64::from_be_bytes(bytes[offset..offset + 8].try_into().unwrap());
let state = Self {
handle: u32::from_be_bytes(bytes[1..5].try_into().unwrap()),
revision: read(5),
received: OpNumber(read(13)),
received_bytes: read(21),
};
state.validate()?;
Ok(state)
}
pub fn report(self, bound: Report, received: Prefix) -> Result<Report, WireError> {
self.validate()?;
if received.op != self.received {
return Err(WireError::History);
}
let report = Report {
revision: self.revision,
received,
received_bytes: self.received_bytes,
..bound
};
report.validate_shape()?;
Ok(report)
}
}