extern crate alloc;
use alloc::collections::BTreeMap;
use alloc::vec::Vec;
use thiserror::Error;
use super::VersionVector;
const VV_WIRE_V1: u8 = 0x01;
const VV_WIRE_HEADER_LEN: usize = 5;
const VV_WIRE_ENTRY_LEN: usize = 12;
impl VersionVector {
#[must_use]
pub fn encoded_len(&self) -> usize {
VV_WIRE_HEADER_LEN.saturating_add(self.counters.len().saturating_mul(VV_WIRE_ENTRY_LEN))
}
#[must_use]
pub fn to_bytes(&self) -> Vec<u8> {
let count = u32::try_from(self.counters.len()).unwrap_or(u32::MAX);
let mut out = Vec::with_capacity(self.encoded_len());
out.push(VV_WIRE_V1);
out.extend_from_slice(&count.to_be_bytes());
for (&station, &counter) in &self.counters {
out.extend_from_slice(&station.to_be_bytes());
out.extend_from_slice(&counter.to_be_bytes());
}
out
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
let (vector, tail) = Self::from_prefix(bytes)?;
if tail.is_empty() {
Ok(vector)
} else {
Err(DecodeError::UnexpectedLength {
expected: bytes.len() - tail.len(),
found: bytes.len(),
})
}
}
pub fn from_prefix(bytes: &[u8]) -> Result<(Self, &[u8]), DecodeError> {
let header: [u8; VV_WIRE_HEADER_LEN] = bytes
.get(..VV_WIRE_HEADER_LEN)
.and_then(|h| h.try_into().ok())
.ok_or(DecodeError::UnexpectedLength {
expected: VV_WIRE_HEADER_LEN,
found: bytes.len(),
})?;
let [version, c0, c1, c2, c3] = header;
if version != VV_WIRE_V1 {
return Err(DecodeError::UnknownVersion(version));
}
let count = u32::from_be_bytes([c0, c1, c2, c3]);
let frame_len = VV_WIRE_HEADER_LEN as u64 + u64::from(count) * VV_WIRE_ENTRY_LEN as u64;
let expected = usize::try_from(frame_len).unwrap_or(usize::MAX);
if bytes.len() < expected {
return Err(DecodeError::UnexpectedLength {
expected,
found: bytes.len(),
});
}
let (frame, tail) = bytes.split_at(expected);
let mut counters = BTreeMap::new();
let mut previous: Option<u32> = None;
for entry in frame
.get(VV_WIRE_HEADER_LEN..)
.unwrap_or_default()
.chunks_exact(VV_WIRE_ENTRY_LEN)
{
let entry: [u8; VV_WIRE_ENTRY_LEN] =
entry
.try_into()
.map_err(|_| DecodeError::UnexpectedLength {
expected,
found: bytes.len(),
})?;
let [s0, s1, s2, s3, counter @ ..] = entry;
let station = u32::from_be_bytes([s0, s1, s2, s3]);
let counter = u64::from_be_bytes(counter);
if counter == 0 {
return Err(DecodeError::ZeroCounter { station });
}
if let Some(previous) = previous
&& station <= previous
{
return Err(DecodeError::NonAscendingStations {
previous,
found: station,
});
}
previous = Some(station);
let _ = counters.insert(station, counter);
}
Ok((Self { counters }, tail))
}
}
#[non_exhaustive]
#[derive(Error, Debug, Clone, Copy, PartialEq, Eq)]
pub enum DecodeError {
#[error("unknown VersionVector wire version: {0:#04x}")]
UnknownVersion(u8),
#[error("unexpected VersionVector frame length: expected {expected}, found {found}")]
UnexpectedLength {
expected: usize,
found: usize,
},
#[error("non-canonical VersionVector: zero counter for station {station}")]
ZeroCounter {
station: u32,
},
#[error("non-canonical VersionVector: station {found} is not strictly after {previous}")]
NonAscendingStations {
previous: u32,
found: u32,
},
}