use super::{Decoder, Encoder, ExiError, ExiResult};
pub const BELOW_MIN_WIDTH: u32 = 1;
pub const SIMPLE_WIDTH: u32 = 1;
#[derive(Debug, Clone, Copy)]
pub struct Shape {
pub prod_before: &'static [u64],
pub width: &'static [u32],
pub repeat_width: &'static [u32],
pub min: &'static [u32],
pub max: &'static [u32],
}
impl Shape {
#[must_use]
pub const fn len(&self) -> usize {
self.min.len()
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.min.is_empty()
}
fn total(&self) -> u64 {
self.prod_before[self.prod_before.len() - 1]
}
const fn branches(&self, j: usize) -> u64 {
self.prod_before[j + 1] - self.prod_before[j]
}
pub fn assert_below_min_is_narrow(&self) {
for j in 0..self.len() {
assert!(
self.min[j] < 2 || self.branches(j) == 1,
"item {j} has minOccurs={} over {} branches; BELOW_MIN_WIDTH is one bit",
self.min[j],
self.branches(j),
);
}
}
}
#[derive(Debug)]
pub struct SeqWriter {
shape: Shape,
pos: usize,
base: u64,
width: u32,
}
impl SeqWriter {
#[must_use]
pub fn new(shape: Shape) -> Self {
let width = shape.width[0];
#[cfg(debug_assertions)]
shape.assert_below_min_is_narrow();
Self { shape, pos: 0, base: 0, width }
}
pub fn start(&mut self, e: &mut Encoder<'_>, j: usize, c: u32) -> ExiResult<()> {
self.start_branch(e, j, c, 0)
}
pub fn start_branch(
&mut self,
e: &mut Encoder<'_>,
j: usize,
c: u32,
branch: u64,
) -> ExiResult<()> {
let (code, width) = if c == 0 {
let code =
self.base + self.shape.prod_before[j] - self.shape.prod_before[self.pos] + branch;
(code, self.width)
} else if c < self.shape.min[j] {
(branch, BELOW_MIN_WIDTH)
} else {
(branch, self.shape.repeat_width[j])
};
e.event(code, width)
}
pub fn finish(&mut self, j: usize, count: u32) {
self.pos = j + 1;
if count < self.shape.max[j] {
self.base = 1;
self.width = if count < self.shape.min[j] {
BELOW_MIN_WIDTH
} else {
self.shape.repeat_width[j]
};
} else {
self.base = 0;
self.width = self.shape.width[self.pos];
}
}
pub fn end(&mut self, e: &mut Encoder<'_>) -> ExiResult<()> {
let code = self.base + self.shape.total() - self.shape.prod_before[self.pos];
e.event(code, self.width)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Step {
Item {
index: usize,
branch: u64,
first: bool,
},
End,
}
#[derive(Debug)]
pub struct SeqReader {
shape: Shape,
pos: usize,
base: u64,
width: u32,
repeating: usize,
count: u32,
repeating_branches: u64,
below_min: bool,
}
impl SeqReader {
#[must_use]
pub fn new(shape: Shape) -> Self {
let width = shape.width[0];
#[cfg(debug_assertions)]
shape.assert_below_min_is_narrow();
Self {
shape,
pos: 0,
base: 0,
width,
repeating: 0,
count: 0,
repeating_branches: 1,
below_min: false,
}
}
pub fn next(&mut self, d: &mut Decoder<'_>) -> ExiResult<Step> {
let code = d.event(self.width)?;
if self.base != 0 && code < self.repeating_branches {
self.count += 1;
let j = self.repeating;
self.advance_repeat(j);
return Ok(Step::Item { index: j, branch: code, first: false });
}
if self.below_min {
return Err(ExiError::UnknownEventCode);
}
let rel = code.checked_sub(self.base).ok_or(ExiError::UnknownEventCode)?;
let abs = rel + self.shape.prod_before[self.pos];
if abs == self.shape.total() {
return Ok(Step::End);
}
let mut index = self.pos;
while index < self.shape.len() && self.shape.prod_before[index + 1] <= abs {
if self.shape.min[index] > 0 {
return Err(ExiError::UnknownEventCode);
}
index += 1;
}
if index >= self.shape.len() {
return Err(ExiError::UnknownEventCode);
}
let branch = abs - self.shape.prod_before[index];
self.repeating = index;
self.repeating_branches = self.shape.branches(index);
self.count = 1;
self.advance_repeat(index);
Ok(Step::Item { index, branch, first: true })
}
fn advance_repeat(&mut self, j: usize) {
self.pos = j + 1;
self.below_min = self.count < self.shape.min[j];
if self.count < self.shape.max[j] {
self.base = 1;
self.width = if self.below_min { BELOW_MIN_WIDTH } else { self.shape.repeat_width[j] };
} else {
self.base = 0;
self.width = self.shape.width[self.pos];
}
}
}
#[cfg(test)]
mod tests {
use alloc::vec;
use alloc::vec::Vec;
use super::*;
use crate::exi::Header;
const REQ: Shape =
Shape { prod_before: &[0, 1], width: &[1, 1], repeat_width: &[2], min: &[1], max: &[20] };
const RES: Shape = Shape {
prod_before: &[0, 1, 2],
width: &[1, 2, 1],
repeat_width: &[0, 0],
min: &[1, 0],
max: &[1, 1],
};
fn roundtrip(shape: Shape, items: &[usize]) -> Vec<usize> {
let mut buf = [0u8; 512];
let mut encoder = Encoder::new(&mut buf);
encoder.write_header(Header::ISO15118).unwrap();
let mut writer = SeqWriter::new(shape);
let mut pos = 0;
while pos < items.len() {
let item = items[pos];
let mut count = 0u32;
while items.get(pos + count as usize) == Some(&item) {
writer.start(&mut encoder, item, count).unwrap();
count += 1;
}
writer.finish(item, count);
pos += count as usize;
}
writer.end(&mut encoder).unwrap();
let len = encoder.finish().unwrap();
let mut decoder = Decoder::new(&buf[..len]);
decoder.read_header().unwrap();
let mut reader = SeqReader::new(shape);
let mut out = Vec::new();
while let Step::Item { index, .. } = reader.next(&mut decoder).unwrap() {
out.push(index);
}
decoder.finish().unwrap();
out
}
#[test]
fn a_single_repeated_item_roundtrips_at_every_count() {
for n in 1..=20 {
let items: Vec<usize> = core::iter::repeat_n(0, n).collect();
assert_eq!(roundtrip(REQ, &items), items, "{n} occurrences");
}
}
#[test]
fn an_optional_trailing_item_roundtrips_either_way() {
assert_eq!(roundtrip(RES, &[0]), vec![0]);
assert_eq!(roundtrip(RES, &[0, 1]), vec![0, 1]);
}
#[test]
fn widths_match_the_hand_written_app_protocol_codec() {
let mut buf = [0u8; 512];
let mut e = Encoder::new(&mut buf);
let mut w = SeqWriter::new(REQ);
w.start(&mut e, 0, 0).unwrap();
assert_eq!(e.bit_len(), 1);
w.finish(0, 1);
let before = e.bit_len();
w.end(&mut e).unwrap();
assert_eq!(e.bit_len() - before, 2, "EE after one of twenty costs two bits");
}
#[test]
fn a_code_naming_an_item_past_a_required_one_is_refused() {
const S: Shape = Shape {
prod_before: &[0, 1, 2],
width: &[1, 1, 1],
repeat_width: &[0, 0],
min: &[1, 1],
max: &[1, 1],
};
let mut buf = [0u8; 64];
let mut e = Encoder::new(&mut buf);
e.event(1, 1).unwrap(); let len = e.finish().unwrap();
let mut d = Decoder::new(&buf[..len]);
let mut r = SeqReader::new(S);
assert_eq!(r.next(&mut d), Err(ExiError::UnknownEventCode));
}
#[test]
fn the_second_level_escape_below_min_occurs_is_refused() {
const S: Shape = Shape {
prod_before: &[0, 1, 2],
width: &[1, 1, 1],
repeat_width: &[0, 2],
min: &[1, 2],
max: &[1, 255],
};
let mut buf = [0u8; 64];
let mut e = Encoder::new(&mut buf);
let mut w = SeqWriter::new(S);
w.start(&mut e, 0, 0).unwrap(); w.finish(0, 1);
w.start(&mut e, 1, 0).unwrap(); e.event(1, BELOW_MIN_WIDTH).unwrap();
let len = e.finish().unwrap();
let mut d = Decoder::new(&buf[..len]);
let mut r = SeqReader::new(S);
assert_eq!(r.next(&mut d).unwrap(), Step::Item { index: 0, branch: 0, first: true });
assert_eq!(r.next(&mut d).unwrap(), Step::Item { index: 1, branch: 0, first: true });
assert_eq!(r.next(&mut d), Err(ExiError::UnknownEventCode));
}
#[test]
fn a_repetition_that_meets_its_minimum_can_end() {
const S: Shape = Shape {
prod_before: &[0, 1],
width: &[1, 1],
repeat_width: &[2],
min: &[2],
max: &[255],
};
assert_eq!(roundtrip(S, &[0, 0]), vec![0, 0]);
assert_eq!(roundtrip(S, &[0, 0, 0]), vec![0, 0, 0]);
}
#[test]
fn an_empty_content_model_reads_as_an_immediate_end() {
const EMPTY: Shape =
Shape { prod_before: &[0], width: &[0], repeat_width: &[], min: &[], max: &[] };
assert!(EMPTY.is_empty());
assert_eq!(roundtrip(EMPTY, &[]), Vec::<usize>::new());
}
}