extern crate alloc;
use alloc::collections::BTreeMap;
use alloc::vec::Vec;
use super::placement::Dot;
use super::{DotSet, Rhapsody};
use crate::metis::HaveSetDecodeBudget;
const RHAPSODY_WIRE_V2: u8 = 0x02;
const RHAPSODY_WIRE_HEADER_LEN: usize = 5;
mod error;
mod locus;
mod snapshot;
pub use error::RhapsodyDecodeError;
pub use snapshot::SnapshotDecodeError;
#[cfg(any(test, kani))]
pub use snapshot::rank_successor;
pub(super) use snapshot::{apply_chain_step, chain_step};
use locus::{decode_locus, encode_locus};
impl Rhapsody {
#[must_use]
pub fn to_bytes(&self) -> Vec<u8> {
let locus_count = u32::try_from(self.skeleton.len()).unwrap_or(u32::MAX);
let mut out = Vec::with_capacity(RHAPSODY_WIRE_HEADER_LEN);
out.push(RHAPSODY_WIRE_V2);
out.extend_from_slice(&locus_count.to_be_bytes());
for (dot, locus) in self.skeleton.iter() {
encode_locus(&mut out, dot, &locus);
}
out.extend_from_slice(&self.visible.to_bytes());
out
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, RhapsodyDecodeError> {
let (rhapsody, tail) = Self::from_prefix(bytes)?;
if tail.is_empty() {
Ok(rhapsody)
} else {
Err(RhapsodyDecodeError::UnexpectedLength {
expected: bytes.len() - tail.len(),
found: bytes.len(),
})
}
}
pub fn from_prefix(bytes: &[u8]) -> Result<(Self, &[u8]), RhapsodyDecodeError> {
let header: [u8; RHAPSODY_WIRE_HEADER_LEN] = bytes
.get(..RHAPSODY_WIRE_HEADER_LEN)
.and_then(|h| h.try_into().ok())
.ok_or(RhapsodyDecodeError::UnexpectedLength {
expected: RHAPSODY_WIRE_HEADER_LEN,
found: bytes.len(),
})?;
let [version, c0, c1, c2, c3] = header;
if version != RHAPSODY_WIRE_V2 {
return Err(RhapsodyDecodeError::UnknownVersion(version));
}
let locus_count = u32::from_be_bytes([c0, c1, c2, c3]);
if u64::from(locus_count) > super::identity::PLANE_CAPACITY as u64 {
return Err(RhapsodyDecodeError::TooManyLoci {
count: u64::from(locus_count),
capacity: super::identity::PLANE_CAPACITY as u64,
});
}
let mut rest = &bytes[RHAPSODY_WIRE_HEADER_LEN..];
let mut skeleton = BTreeMap::new();
let mut previous_dot: Option<Dot> = None;
for _ in 0..locus_count {
let (dot, locus, tail) = decode_locus(bytes, rest)?;
rest = tail;
if let Some(previous) = previous_dot
&& dot <= previous
{
return Err(RhapsodyDecodeError::NonAscendingLoci {
previous: previous.into(),
found: dot.into(),
});
}
previous_dot = Some(dot);
let _ = skeleton.insert(dot, locus);
}
let budget = HaveSetDecodeBudget::new(skeleton.len());
let (visible, tail) =
DotSet::from_prefix_with_budget(rest, budget).map_err(RhapsodyDecodeError::Visible)?;
rest = tail;
for dot in visible.dots() {
if !skeleton.contains_key(&dot) {
return Err(RhapsodyDecodeError::VisibleWithoutLocus {
station: dot.station(),
index: dot.counter(),
});
}
}
let consumed = bytes.len() - rest.len();
Ok((Self::from_parts(skeleton, visible), &bytes[consumed..]))
}
}