extern crate alloc;
use alloc::collections::BTreeMap;
use alloc::vec::Vec;
use core::num::NonZeroU64;
use crate::metis::VersionVector;
use super::super::placement::Dot;
use super::RefoundMap;
mod error;
pub use error::{RefoundMapDecodeBudget, RefoundMapDecodeError};
const REFOUND_MAP_WIRE_V1: u8 = 0x01;
const REFOUND_MAP_WIRE_HEADER_LEN: usize = 9;
const REFOUND_MAP_WIRE_ROW_PREFIX_LEN: usize = 20;
const REFOUND_MAP_WIRE_ENTRY_LEN: usize = 8;
impl RefoundMap {
#[must_use]
pub fn to_bytes(&self) -> Vec<u8> {
let count = self.ceilings.iter().count();
let mut out = Vec::with_capacity(
REFOUND_MAP_WIRE_HEADER_LEN
+ count * REFOUND_MAP_WIRE_ROW_PREFIX_LEN
+ self.live_len() * REFOUND_MAP_WIRE_ENTRY_LEN,
);
out.push(REFOUND_MAP_WIRE_V1);
out.extend_from_slice(&(count as u64).to_be_bytes());
let mut entries_emitted: usize = 0;
for (station, ceiling) in &self.ceilings {
let mut images: Vec<(u64, u64)> = self
.compacted
.range(Dot::new(station, NonZeroU64::MIN)..=Dot::new(station, NonZeroU64::MAX))
.map(|(old, new)| {
debug_assert_eq!(new.station(), station, "the re-mint preserves the station");
(new.counter(), old.counter())
})
.collect();
images.sort_unstable();
debug_assert!(
images
.iter()
.enumerate()
.all(|(position, &(new, _))| new == position as u64 + 1),
"station {station}'s new indices are exactly 1..=n (the proven compaction image)"
);
debug_assert_eq!(images.len() as u64, self.live_of(station));
out.extend_from_slice(&station.to_be_bytes());
out.extend_from_slice(&ceiling.to_be_bytes());
out.extend_from_slice(&(images.len() as u64).to_be_bytes());
for &(_, old) in &images {
out.extend_from_slice(&old.to_be_bytes());
}
entries_emitted += images.len();
}
debug_assert_eq!(
entries_emitted,
self.live_len(),
"every compacted station sits inside the ceilings (the bound-cut invariant)"
);
out
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, RefoundMapDecodeError> {
Self::decode(bytes, None)
}
pub fn from_bytes_with_budget(
bytes: &[u8],
budget: RefoundMapDecodeBudget,
) -> Result<Self, RefoundMapDecodeError> {
Self::decode(bytes, Some(budget))
}
fn decode(
bytes: &[u8],
budget: Option<RefoundMapDecodeBudget>,
) -> Result<Self, RefoundMapDecodeError> {
let header: [u8; REFOUND_MAP_WIRE_HEADER_LEN] = bytes
.get(..REFOUND_MAP_WIRE_HEADER_LEN)
.and_then(|h| h.try_into().ok())
.ok_or(RefoundMapDecodeError::UnexpectedLength {
expected: REFOUND_MAP_WIRE_HEADER_LEN,
found: bytes.len(),
})?;
let [version, c0, c1, c2, c3, c4, c5, c6, c7] = header;
if version != REFOUND_MAP_WIRE_V1 {
return Err(RefoundMapDecodeError::UnknownVersion(version));
}
let count = u64::from_be_bytes([c0, c1, c2, c3, c4, c5, c6, c7]);
if let Some(budget) = budget
&& count > budget.max_stations() as u64
{
return Err(RefoundMapDecodeError::TooManyStations {
count,
budget: budget.max_stations() as u64,
});
}
let body = bytes.len() - REFOUND_MAP_WIRE_HEADER_LEN;
if count > (body / REFOUND_MAP_WIRE_ROW_PREFIX_LEN) as u64 {
let expected = count
.saturating_mul(REFOUND_MAP_WIRE_ROW_PREFIX_LEN as u64)
.saturating_add(REFOUND_MAP_WIRE_HEADER_LEN as u64);
return Err(RefoundMapDecodeError::UnexpectedLength {
expected: usize::try_from(expected).unwrap_or(usize::MAX),
found: bytes.len(),
});
}
let mut rest = &bytes[REFOUND_MAP_WIRE_HEADER_LEN..];
let mut compacted: BTreeMap<Dot, Dot> = BTreeMap::new();
let mut ceilings = VersionVector::new();
let mut live: BTreeMap<u32, u64> = BTreeMap::new();
let mut previous_station: Option<u32> = None;
let mut entries_declared: u64 = 0;
for _ in 0..count {
let (station, ceiling, declared_live, tail) = decode_row(
bytes,
rest,
previous_station,
budget,
&mut entries_declared,
&mut compacted,
)?;
rest = tail;
previous_station = Some(station);
ceilings.observe(station, ceiling);
if declared_live > 0 {
let _ = live.insert(station, declared_live);
}
}
if rest.is_empty() {
Ok(Self {
compacted,
ceilings,
live,
})
} else {
Err(RefoundMapDecodeError::UnexpectedLength {
expected: bytes.len() - rest.len(),
found: bytes.len(),
})
}
}
}
type DecodedRow<'a> = (u32, u64, u64, &'a [u8]);
fn decode_row<'a>(
bytes: &[u8],
rest: &'a [u8],
previous_station: Option<u32>,
budget: Option<RefoundMapDecodeBudget>,
entries_declared: &mut u64,
compacted: &mut BTreeMap<Dot, Dot>,
) -> Result<DecodedRow<'a>, RefoundMapDecodeError> {
let prefix: [u8; REFOUND_MAP_WIRE_ROW_PREFIX_LEN] = rest
.get(..REFOUND_MAP_WIRE_ROW_PREFIX_LEN)
.and_then(|p| p.try_into().ok())
.ok_or(RefoundMapDecodeError::UnexpectedLength {
expected: bytes.len() - rest.len() + REFOUND_MAP_WIRE_ROW_PREFIX_LEN,
found: bytes.len(),
})?;
let [
s0,
s1,
s2,
s3,
h0,
h1,
h2,
h3,
h4,
h5,
h6,
h7,
n0,
n1,
n2,
n3,
n4,
n5,
n6,
n7,
] = prefix;
let station = u32::from_be_bytes([s0, s1, s2, s3]);
let ceiling = u64::from_be_bytes([h0, h1, h2, h3, h4, h5, h6, h7]);
let declared_live = u64::from_be_bytes([n0, n1, n2, n3, n4, n5, n6, n7]);
let rest = &rest[REFOUND_MAP_WIRE_ROW_PREFIX_LEN..];
if let Some(previous) = previous_station
&& station <= previous
{
return Err(RefoundMapDecodeError::NonAscendingStations {
previous,
found: station,
});
}
if ceiling == 0 {
return Err(RefoundMapDecodeError::ZeroCeiling { station });
}
if declared_live > ceiling {
return Err(RefoundMapDecodeError::LiveExceedsCeiling {
station,
live: declared_live,
ceiling,
});
}
*entries_declared = entries_declared.saturating_add(declared_live);
if let Some(budget) = budget
&& *entries_declared > budget.max_entries() as u64
{
return Err(RefoundMapDecodeError::TooManyEntries {
count: *entries_declared,
budget: budget.max_entries() as u64,
});
}
if declared_live > (rest.len() / REFOUND_MAP_WIRE_ENTRY_LEN) as u64 {
let expected = declared_live
.saturating_mul(REFOUND_MAP_WIRE_ENTRY_LEN as u64)
.saturating_add((bytes.len() - rest.len()) as u64);
return Err(RefoundMapDecodeError::UnexpectedLength {
expected: usize::try_from(expected).unwrap_or(usize::MAX),
found: bytes.len(),
});
}
let rest = decode_entries(bytes, rest, station, ceiling, declared_live, compacted)?;
Ok((station, ceiling, declared_live, rest))
}
fn decode_entries<'a>(
bytes: &[u8],
mut rest: &'a [u8],
station: u32,
ceiling: u64,
declared_live: u64,
compacted: &mut BTreeMap<Dot, Dot>,
) -> Result<&'a [u8], RefoundMapDecodeError> {
for new_index in 1..=declared_live {
let entry: [u8; REFOUND_MAP_WIRE_ENTRY_LEN] = rest
.get(..REFOUND_MAP_WIRE_ENTRY_LEN)
.and_then(|e| e.try_into().ok())
.ok_or(RefoundMapDecodeError::UnexpectedLength {
expected: bytes.len() - rest.len() + REFOUND_MAP_WIRE_ENTRY_LEN,
found: bytes.len(),
})?;
rest = &rest[REFOUND_MAP_WIRE_ENTRY_LEN..];
let old_index = u64::from_be_bytes(entry);
let (Some(old), Some(new)) = (
NonZeroU64::new(old_index).filter(|_| old_index <= ceiling),
NonZeroU64::new(new_index),
) else {
return Err(RefoundMapDecodeError::IndexOutOfRange {
station,
index: old_index,
ceiling,
});
};
if compacted
.insert(Dot::new(station, old), Dot::new(station, new))
.is_some()
{
return Err(RefoundMapDecodeError::DuplicateIndex {
station,
index: old_index,
});
}
}
Ok(rest)
}