use crate::error::ParseError;
const ROOT_KEY: usize = 5;
pub const PACKET_LEN: usize = 381;
const LATER_HEADER_LEN: usize = 372;
const PREAMBLE: usize = 990;
pub fn first_header_len(cat_len: usize, map_len: usize) -> usize {
let used = cat_len + map_len;
let mut room = PREAMBLE;
while room <= used {
room += PACKET_LEN;
}
room - used
}
pub fn header_len(index: usize, cat_len: usize, map_len: usize) -> usize {
if index > 0 {
LATER_HEADER_LEN
} else {
first_header_len(cat_len, map_len)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Stroke {
pub root_key: u8,
pub packets: Option<usize>,
}
pub fn read(
payload: &[u8],
index: usize,
cat_len: usize,
map_len: usize,
) -> Result<Stroke, ParseError> {
let root_key = *payload.get(ROOT_KEY).ok_or_else(|| {
ParseError::AssertFail(format!("stroke {index} is {} bytes", payload.len()))
})?;
let header = header_len(index, cat_len, map_len);
let packets = payload
.len()
.checked_sub(header)
.filter(|body| body % PACKET_LEN == 0)
.map(|body| body / PACKET_LEN);
Ok(Stroke { root_key, packets })
}
pub fn set_root_key(payload: &mut [u8], note: u8) -> Result<(), ParseError> {
let slot = payload
.get_mut(ROOT_KEY)
.ok_or_else(|| ParseError::AssertFail("stroke is too short to hold a root key".into()))?;
*slot = note;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
fn map_len(z: usize) -> usize {
801 + 15 * (z - 1)
}
const OUR_CAT: usize = 24;
fn stroke(index: usize, zones: usize, packets: usize, root: u8) -> Vec<u8> {
let mut v = vec![0u8; header_len(index, OUR_CAT, map_len(zones)) + packets * PACKET_LEN];
v[ROOT_KEY] = root;
v
}
#[test]
fn header_shrinks_only_for_the_first_stroke() {
assert_eq!(header_len(0, OUR_CAT, map_len(1)), 165);
assert_eq!(header_len(0, OUR_CAT, map_len(2)), 150);
assert_eq!(header_len(0, OUR_CAT, map_len(3)), 135);
assert_eq!(header_len(1, OUR_CAT, map_len(2)), 372);
assert_eq!(header_len(2, OUR_CAT, map_len(3)), 372);
}
#[test]
fn the_preamble_grows_by_a_packet_rather_than_going_negative() {
for (zones, header) in [(4, 120), (6, 90), (8, 60), (12, 381), (16, 321)] {
assert_eq!(
header_len(0, OUR_CAT, map_len(zones)),
header,
"{zones} zones"
);
}
assert_eq!(header_len(0, OUR_CAT, map_len(11)), 15);
}
#[test]
fn a_smaller_cat_section_lends_its_bytes_to_the_header() {
const VENDOR_CAT: usize = 9;
assert_eq!(header_len(0, VENDOR_CAT, map_len(6)), 105);
assert_eq!(header_len(0, VENDOR_CAT, map_len(11)), 30);
assert_eq!(header_len(0, VENDOR_CAT, map_len(12)), 15);
assert_eq!(header_len(0, VENDOR_CAT, map_len(16)), 336);
}
#[test]
fn reads_root_key_and_packet_count() {
let s = read(&stroke(0, 1, 4, 60), 0, OUR_CAT, map_len(1)).unwrap();
assert_eq!(s.root_key, 60);
assert_eq!(s.packets, Some(4));
}
#[test]
fn lengths_from_the_corpus_decompose_exactly() {
for (index, zones, cat, len, packets) in [
(0, 1, OUR_CAT, 1689, 4), (0, 2, OUR_CAT, 1674, 4), (1, 2, OUR_CAT, 1896, 4),
(0, 3, OUR_CAT, 2040, 5),
(2, 3, OUR_CAT, 1896, 4),
(0, 1, OUR_CAT, 165, 0), (0, 4, OUR_CAT, 2787, 7), (0, 12, OUR_CAT, 3048, 7), (0, 16, OUR_CAT, 2988, 7),
(0, 6, 9, 10773, 28), (0, 11, 9, 7269, 19), (0, 16, 9, 1860, 4), ] {
let mut v = vec![0u8; len];
v[ROOT_KEY] = 60;
let s = read(&v, index, cat, map_len(zones))
.unwrap_or_else(|e| panic!("stroke {index} of {zones}, {len} bytes: {e}"));
assert_eq!(
s.packets,
Some(packets),
"stroke {index} of {zones}, {len} bytes"
);
}
}
#[test]
fn a_length_that_is_not_header_plus_packets_has_no_count() {
let mut v = stroke(0, 1, 2, 60);
v.push(0);
assert_eq!(read(&v, 0, OUR_CAT, map_len(1)).unwrap().packets, None);
}
#[test]
fn a_stroke_shorter_than_its_header_has_no_count() {
assert_eq!(
read(&[0u8; 8], 1, OUR_CAT, map_len(2)).unwrap().packets,
None
);
}
#[test]
fn set_root_key_moves_one_byte() {
let before = stroke(0, 1, 1, 60);
let mut after = before.clone();
set_root_key(&mut after, 48).unwrap();
let differing: Vec<_> = (0..before.len())
.filter(|&i| before[i] != after[i])
.collect();
assert_eq!(differing, vec![ROOT_KEY]);
}
}