pub const ANGLE_BIAS: i16 = 128;
#[inline]
pub fn encode_record(canonical: &[i8], out: &mut Vec<u8>) {
debug_assert!(
canonical.len() <= u8::MAX as usize,
"record longer than 255"
);
out.push(canonical.len() as u8);
for &a in canonical {
out.push((a as i16 + ANGLE_BIAS) as u8);
}
}
#[inline]
pub fn decode_record(bytes: &[u8]) -> Option<(&[u8], Vec<i8>)> {
if bytes.is_empty() {
return None;
}
let len = bytes[0] as usize;
if bytes.len() < 1 + len {
return None;
}
let angles: Vec<i8> = bytes[1..=len]
.iter()
.map(|&b| (b as i16 - ANGLE_BIAS) as i8)
.collect();
Some((&bytes[1 + len..], angles))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trip() {
let cases: &[&[i8]] = &[&[], &[0], &[-5, 3, -2, 4], &[127, -128, 0, 1, -1]];
for &seq in cases {
let mut buf = Vec::new();
encode_record(seq, &mut buf);
let (rest, decoded) = decode_record(&buf).expect("decoded");
assert!(rest.is_empty(), "trailing bytes");
assert_eq!(decoded, seq);
}
}
#[test]
fn byte_lex_equals_length_then_int_lex() {
let mut seqs: Vec<Vec<i8>> = vec![
vec![1, 2, 3],
vec![-1, 0],
vec![1, 1],
vec![],
vec![1, 2],
vec![-5, 0, 3],
vec![-5, 0, 2],
vec![1, -1, 0],
];
seqs.sort_by(|a, b| a.len().cmp(&b.len()).then_with(|| a.cmp(b)));
let mut encoded: Vec<Vec<u8>> = seqs
.iter()
.map(|s| {
let mut buf = Vec::new();
encode_record(s, &mut buf);
buf
})
.collect();
encoded.sort();
let decoded: Vec<Vec<i8>> = encoded
.iter()
.map(|e| decode_record(e).expect("decode").1)
.collect();
assert_eq!(decoded, seqs);
}
}