extern crate alloc;
use alloc::vec::Vec;
use core::num::NonZeroU64;
use crate::kairos::{KAIROS_WIRE_LEN, Kairos};
use super::super::placement::Dot;
use super::super::{Anchor, Locus};
use super::RhapsodyDecodeError;
use crate::metis::dot::RawDot;
const RHAPSODY_WIRE_LOCUS_PREFIX_LEN: usize = 13;
const RHAPSODY_WIRE_ANCHOR_LEN: usize = 12;
const RHAPSODY_WIRE_LOCUS_MIN_LEN: usize = RHAPSODY_WIRE_LOCUS_PREFIX_LEN + KAIROS_WIRE_LEN;
const ANCHOR_TAG_ORIGIN: u8 = 0x00;
const ANCHOR_TAG_AFTER: u8 = 0x01;
const ANCHOR_TAG_BEFORE: u8 = 0x02;
pub(super) fn encode_locus(out: &mut Vec<u8>, dot: Dot, locus: &Locus) {
out.extend_from_slice(&dot.station().to_be_bytes());
out.extend_from_slice(&dot.counter().to_be_bytes());
match locus.anchor {
Anchor::Origin => out.push(ANCHOR_TAG_ORIGIN),
Anchor::After(RawDot {
station: astation,
counter: aindex,
}) => {
out.push(ANCHOR_TAG_AFTER);
out.extend_from_slice(&astation.to_be_bytes());
out.extend_from_slice(&aindex.to_be_bytes());
}
Anchor::Before(RawDot {
station: astation,
counter: aindex,
}) => {
out.push(ANCHOR_TAG_BEFORE);
out.extend_from_slice(&astation.to_be_bytes());
out.extend_from_slice(&aindex.to_be_bytes());
}
}
out.extend_from_slice(&locus.rank.to_bytes());
}
type DecodedLocus<'a> = (Dot, Locus, &'a [u8]);
pub(super) fn decode_locus<'a>(
bytes: &[u8],
rest: &'a [u8],
) -> Result<DecodedLocus<'a>, RhapsodyDecodeError> {
let prefix: [u8; RHAPSODY_WIRE_LOCUS_PREFIX_LEN] = rest
.get(..RHAPSODY_WIRE_LOCUS_PREFIX_LEN)
.and_then(|p| p.try_into().ok())
.ok_or(RhapsodyDecodeError::UnexpectedLength {
expected: bytes.len() - rest.len() + RHAPSODY_WIRE_LOCUS_MIN_LEN,
found: bytes.len(),
})?;
let [s0, s1, s2, s3, i0, i1, i2, i3, i4, i5, i6, i7, tag] = prefix;
let station = u32::from_be_bytes([s0, s1, s2, s3]);
let index = u64::from_be_bytes([i0, i1, i2, i3, i4, i5, i6, i7]);
let Some(counter) = NonZeroU64::new(index) else {
return Err(RhapsodyDecodeError::ZeroDot { station });
};
let mut rest = &rest[RHAPSODY_WIRE_LOCUS_PREFIX_LEN..];
let anchor = match tag {
ANCHOR_TAG_ORIGIN => Anchor::Origin,
ANCHOR_TAG_AFTER | ANCHOR_TAG_BEFORE => {
let anchor: [u8; RHAPSODY_WIRE_ANCHOR_LEN] = rest
.get(..RHAPSODY_WIRE_ANCHOR_LEN)
.and_then(|a| a.try_into().ok())
.ok_or(RhapsodyDecodeError::UnexpectedLength {
expected: bytes.len() - rest.len() + RHAPSODY_WIRE_ANCHOR_LEN + KAIROS_WIRE_LEN,
found: bytes.len(),
})?;
rest = &rest[RHAPSODY_WIRE_ANCHOR_LEN..];
let [a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11] = anchor;
let astation = u32::from_be_bytes([a0, a1, a2, a3]);
let aindex = u64::from_be_bytes([a4, a5, a6, a7, a8, a9, a10, a11]);
let dot = (astation, aindex);
if tag == ANCHOR_TAG_AFTER {
Anchor::After(dot.into())
} else {
Anchor::Before(dot.into())
}
}
other => return Err(RhapsodyDecodeError::BadAnchorTag { tag: other }),
};
let rank_bytes: [u8; KAIROS_WIRE_LEN] = rest
.get(..KAIROS_WIRE_LEN)
.and_then(|r| r.try_into().ok())
.ok_or(RhapsodyDecodeError::UnexpectedLength {
expected: bytes.len() - rest.len() + KAIROS_WIRE_LEN,
found: bytes.len(),
})?;
let rank = Kairos::from_bytes(&rank_bytes).map_err(RhapsodyDecodeError::Rank)?;
rest = &rest[KAIROS_WIRE_LEN..];
Ok((Dot::new(station, counter), Locus { anchor, rank }, rest))
}