use alloc::string::String;
use super::string_table::{Hit, ValueCtx, ValueTable};
use super::{BitReader, BitWriter, ExiError, ExiResult, Header, header, primitives as prim};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Lengths {
min: usize,
max: usize,
}
impl Lengths {
#[must_use]
pub const fn max(max: usize) -> Self {
Self { min: 0, max }
}
#[must_use]
pub const fn exact(len: usize) -> Self {
Self { min: len, max: len }
}
#[must_use]
pub const fn new(min: usize, max: usize) -> Self {
Self { min, max }
}
#[must_use]
pub const fn min_len(self) -> usize {
self.min
}
#[must_use]
pub const fn max_len(self) -> usize {
self.max
}
#[must_use]
pub const fn admits(self, len: usize) -> bool {
len >= self.min && len <= self.max
}
const fn check(self, len: usize) -> ExiResult<()> {
if len > self.max {
Err(ExiError::ValueTooLong)
} else if len < self.min {
Err(ExiError::ValueTooShort)
} else {
Ok(())
}
}
}
const fn restricted_width(span: u64) -> u32 {
match span.checked_add(1) {
Some(values) => prim::bit_width(values),
None => u64::BITS,
}
}
pub const MAX_DEPTH: u16 = 64;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ValueCoding {
#[default]
Literal,
Referenced,
}
#[derive(Debug)]
pub struct Encoder<'a> {
bits: BitWriter<'a>,
values: ValueTable,
coding: ValueCoding,
depth: u16,
}
impl<'a> Encoder<'a> {
#[must_use]
pub fn new(buf: &'a mut [u8]) -> Self {
Self::with_value_coding(buf, ValueCoding::Literal)
}
#[must_use]
pub fn with_value_coding(buf: &'a mut [u8], coding: ValueCoding) -> Self {
Self { bits: BitWriter::new(buf), values: ValueTable::new(), coding, depth: 0 }
}
#[must_use]
pub const fn value_coding(&self) -> ValueCoding {
self.coding
}
pub fn write_header(&mut self, header: Header) -> ExiResult<()> {
header::write_header(&mut self.bits, header)
}
pub fn event(&mut self, code: u64, width: u32) -> ExiResult<()> {
prim::write_nbit_uint(&mut self.bits, code, width)
}
pub fn enter(&mut self) -> ExiResult<()> {
self.depth = self.depth.saturating_add(1);
if self.depth > MAX_DEPTH {
return Err(ExiError::DepthLimitExceeded);
}
Ok(())
}
pub fn leave(&mut self) {
self.depth = self.depth.saturating_sub(1);
}
pub fn uint(&mut self, v: u64) -> ExiResult<()> {
prim::write_uint(&mut self.bits, v)
}
pub fn int(&mut self, v: i64) -> ExiResult<()> {
prim::write_int(&mut self.bits, v)
}
pub fn nbit(&mut self, v: u64, n: u32) -> ExiResult<()> {
prim::write_nbit_uint(&mut self.bits, v, n)
}
pub fn boolean(&mut self, v: bool) -> ExiResult<()> {
prim::write_bool(&mut self.bits, v)
}
pub fn restricted(&mut self, value: i64, min: i64, max: i64) -> ExiResult<()> {
debug_assert!(min <= max, "empty range {min}..={max}");
if value < min || value > max {
return Err(ExiError::ValueOutOfRange);
}
let span = u64::try_from(i128::from(max) - i128::from(min))
.map_err(|_| ExiError::ValueOutOfRange)?;
let index = u64::try_from(i128::from(value) - i128::from(min))
.map_err(|_| ExiError::ValueOutOfRange)?;
self.nbit(index, restricted_width(span))
}
pub fn binary(&mut self, bytes: &[u8], lengths: Lengths) -> ExiResult<()> {
lengths.check(bytes.len())?;
prim::write_binary(&mut self.bits, bytes)
}
pub fn decimal(&mut self, v: prim::Decimal) -> ExiResult<()> {
prim::write_decimal(&mut self.bits, v)
}
pub fn float(&mut self, v: prim::Float) -> ExiResult<()> {
prim::write_float(&mut self.bits, v)
}
pub fn datetime(&mut self, v: prim::DateTime) -> ExiResult<()> {
prim::write_datetime(&mut self.bits, v)
}
pub fn string(&mut self, ctx: ValueCtx, value: &str, lengths: Lengths) -> ExiResult<()> {
let n = prim::char_len(value);
lengths.check(n)?;
if self.coding == ValueCoding::Referenced {
match self.values.find(ctx, value) {
Hit::Local(idx) => {
let width = self.values.local_index_width(ctx);
self.uint(0)?;
return self.nbit(idx, width);
}
Hit::Global(idx) => {
let width = self.values.global_index_width();
self.uint(1)?;
return self.nbit(idx, width);
}
Hit::Miss => {}
}
}
self.uint(n as u64 + 2)?;
prim::write_chars(&mut self.bits, value)?;
if self.coding == ValueCoding::Referenced {
self.values.insert(ctx, value);
}
Ok(())
}
#[must_use]
pub const fn bit_len(&self) -> usize {
self.bits.bit_len()
}
pub fn finish(self) -> ExiResult<usize> {
self.bits.finish()
}
}
#[derive(Debug)]
pub struct Decoder<'a> {
bits: BitReader<'a>,
values: ValueTable,
depth: u16,
}
impl<'a> Decoder<'a> {
#[must_use]
pub const fn new(buf: &'a [u8]) -> Self {
Self { bits: BitReader::new(buf), values: ValueTable::new(), depth: 0 }
}
pub fn read_header(&mut self) -> ExiResult<Header> {
header::read_header(&mut self.bits)
}
pub fn event(&mut self, width: u32) -> ExiResult<u64> {
prim::read_nbit_uint(&mut self.bits, width)
}
pub fn expect_event(&mut self, code: u64, width: u32) -> ExiResult<()> {
if self.event(width)? == code { Ok(()) } else { Err(ExiError::UnknownEventCode) }
}
pub fn enter(&mut self) -> ExiResult<()> {
self.depth = self.depth.saturating_add(1);
if self.depth > MAX_DEPTH {
return Err(ExiError::DepthLimitExceeded);
}
Ok(())
}
pub fn leave(&mut self) {
self.depth = self.depth.saturating_sub(1);
}
pub fn uint(&mut self) -> ExiResult<u64> {
prim::read_uint(&mut self.bits)
}
pub fn int(&mut self) -> ExiResult<i64> {
prim::read_int(&mut self.bits)
}
pub fn nbit(&mut self, n: u32) -> ExiResult<u64> {
prim::read_nbit_uint(&mut self.bits, n)
}
pub fn boolean(&mut self) -> ExiResult<bool> {
prim::read_bool(&mut self.bits)
}
pub fn restricted(&mut self, min: i64, max: i64) -> ExiResult<i64> {
debug_assert!(min <= max, "empty range {min}..={max}");
let span = u64::try_from(i128::from(max) - i128::from(min))
.map_err(|_| ExiError::ValueOutOfRange)?;
let index = self.nbit(restricted_width(span))?;
if index > span {
return Err(ExiError::ValueOutOfRange);
}
i64::try_from(i128::from(min) + i128::from(index)).map_err(|_| ExiError::ValueOutOfRange)
}
pub fn binary(&mut self, lengths: Lengths) -> ExiResult<alloc::vec::Vec<u8>> {
let bytes = prim::read_binary(&mut self.bits, lengths.max_len())?;
lengths.check(bytes.len())?;
Ok(bytes)
}
pub fn decimal(&mut self) -> ExiResult<prim::Decimal> {
prim::read_decimal(&mut self.bits)
}
pub fn float(&mut self) -> ExiResult<prim::Float> {
prim::read_float(&mut self.bits)
}
pub fn datetime(&mut self) -> ExiResult<prim::DateTime> {
prim::read_datetime(&mut self.bits)
}
pub fn string(&mut self, ctx: ValueCtx, lengths: Lengths) -> ExiResult<String> {
let value = self.string_inner(ctx, lengths)?;
lengths.check(prim::char_len(&value))?;
Ok(value)
}
fn string_inner(&mut self, ctx: ValueCtx, lengths: Lengths) -> ExiResult<String> {
let marker = self.uint()?;
match marker {
0 => {
let width = self.values.local_index_width(ctx);
let idx = self.nbit(width)?;
self.values.local(ctx, idx).map(String::from).ok_or(ExiError::BadStringTableIndex)
}
1 => {
let width = self.values.global_index_width();
let idx = self.nbit(width)?;
self.values.global(idx).map(String::from).ok_or(ExiError::BadStringTableIndex)
}
n => {
let len = usize::try_from(n - 2).map_err(|_| ExiError::ValueTooLong)?;
if len > lengths.max_len() {
return Err(ExiError::ValueTooLong);
}
let s = prim::read_chars(&mut self.bits, len)?;
self.values.insert(ctx, &s);
Ok(s)
}
}
}
#[must_use]
pub const fn bit_pos(&self) -> usize {
self.bits.bit_pos()
}
#[must_use]
pub const fn bits_remaining(&self) -> usize {
self.bits.bits_remaining()
}
pub fn finish(mut self) -> ExiResult<()> {
self.bits.skip_padding()?;
if self.bits.bits_remaining() != 0 {
return Err(ExiError::TrailingData);
}
Ok(())
}
}
pub trait ExiDocument: Sized {
fn to_slice_with(&self, buf: &mut [u8], coding: ValueCoding) -> ExiResult<usize>;
fn from_bytes(bytes: &[u8]) -> ExiResult<Self>;
fn to_slice(&self, buf: &mut [u8]) -> ExiResult<usize> {
self.to_slice_with(buf, ValueCoding::Literal)
}
fn to_vec(&self) -> ExiResult<alloc::vec::Vec<u8>> {
encode_growing(|buf| self.to_slice(buf))
}
fn to_vec_with(&self, coding: ValueCoding) -> ExiResult<alloc::vec::Vec<u8>> {
encode_growing(|buf| self.to_slice_with(buf, coding))
}
}
pub fn encode_growing(
mut encode: impl FnMut(&mut [u8]) -> ExiResult<usize>,
) -> ExiResult<alloc::vec::Vec<u8>> {
let mut buf = alloc::vec![0u8; 4096];
loop {
match encode(&mut buf) {
Ok(n) => {
buf.truncate(n);
return Ok(buf);
}
Err(ExiError::OutputFull) if buf.len() < crate::MAX_EXI_PAYLOAD_LEN => {
buf.resize((buf.len() * 2).min(crate::MAX_EXI_PAYLOAD_LEN), 0);
}
Err(e) => return Err(e),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
const CTX_A: ValueCtx = ValueCtx(1);
const CTX_B: ValueCtx = ValueCtx(2);
fn roundtrip_strings(values: &[(ValueCtx, &str)]) {
let mut buf = [0u8; 1024];
let mut e = Encoder::with_value_coding(&mut buf, ValueCoding::Referenced);
e.write_header(Header::ISO15118).unwrap();
for &(ctx, s) in values {
e.string(ctx, s, Lengths::max(4096)).unwrap();
}
let len = e.finish().unwrap();
let mut d = Decoder::new(&buf[..len]);
d.read_header().unwrap();
for &(ctx, s) in values {
assert_eq!(d.string(ctx, Lengths::max(4096)).unwrap(), s);
}
d.finish().unwrap();
}
#[test]
fn repeated_strings_roundtrip_through_the_table() {
roundtrip_strings(&[
(CTX_A, "urn:iso:15118:2:2013:MsgDef"),
(CTX_A, "urn:iso:15118:2:2013:MsgDef"), (CTX_B, "urn:iso:15118:2:2013:MsgDef"), (CTX_B, "urn:iso:15118:2:2013:MsgDef"), (CTX_A, "something else"),
]);
}
#[test]
fn a_global_hit_leaves_the_local_partition_empty() {
let mut buf = [0u8; 256];
let mut e = Encoder::with_value_coding(&mut buf, ValueCoding::Referenced);
e.string(CTX_A, "Sample", Lengths::max(64)).unwrap(); let before = e.bit_len();
e.string(CTX_B, "Sample", Lengths::max(64)).unwrap(); let first_global = e.bit_len() - before;
let before = e.bit_len();
e.string(CTX_B, "Sample", Lengths::max(64)).unwrap(); assert_eq!(e.bit_len() - before, first_global, "the second must cost the same");
}
#[test]
fn the_referenced_coding_actually_saves_bits() {
let long = "urn:iso:std:iso:15118:-20:CommonMessages";
let mut single_buf = [0u8; 512];
let mut e = Encoder::with_value_coding(&mut single_buf, ValueCoding::Referenced);
e.string(CTX_A, long, Lengths::max(4096)).unwrap();
let one = e.bit_len();
let mut repeat_buf = [0u8; 512];
let mut e = Encoder::with_value_coding(&mut repeat_buf, ValueCoding::Referenced);
e.string(CTX_A, long, Lengths::max(4096)).unwrap();
e.string(CTX_A, long, Lengths::max(4096)).unwrap();
let two = e.bit_len();
assert!(two < one + 16, "a repeat should cost a handful of bits, not {} ", two - one);
}
#[test]
fn the_default_coding_never_writes_a_reference() {
let mut buf = [0u8; 512];
let mut e = Encoder::new(&mut buf);
e.string(CTX_A, "abc", Lengths::max(64)).unwrap();
let one = e.bit_len();
e.string(CTX_A, "abc", Lengths::max(64)).unwrap();
let two = e.bit_len();
let len = e.finish().unwrap();
assert_eq!(two - one, one, "the repeat must cost exactly what the first one did");
let mut d = Decoder::new(&buf[..len]);
assert_eq!(d.string(CTX_A, Lengths::max(64)).unwrap(), "abc");
assert_eq!(d.string(CTX_A, Lengths::max(64)).unwrap(), "abc");
d.finish().unwrap();
}
#[test]
fn a_decoder_accepts_what_the_encoder_declines_to_write() {
let mut buf = [0u8; 512];
let mut e = Encoder::with_value_coding(&mut buf, ValueCoding::Referenced);
e.string(CTX_A, "abc", Lengths::max(64)).unwrap();
e.string(CTX_A, "abc", Lengths::max(64)).unwrap();
let len = e.finish().unwrap();
let mut d = Decoder::new(&buf[..len]);
assert_eq!(d.string(CTX_A, Lengths::max(64)).unwrap(), "abc");
assert_eq!(d.string(CTX_A, Lengths::max(64)).unwrap(), "abc");
d.finish().unwrap();
}
#[test]
fn an_exact_length_refuses_a_short_value_both_ways() {
const CHALLENGE: Lengths = Lengths::exact(16);
let mut buf = [0u8; 128];
let mut e = Encoder::new(&mut buf);
assert_eq!(e.binary(&[0xA5; 15], CHALLENGE), Err(ExiError::ValueTooShort));
assert_eq!(e.binary(&[0xA5; 17], CHALLENGE), Err(ExiError::ValueTooLong));
e.binary(&[0xA5; 16], CHALLENGE).unwrap();
let mut e = Encoder::new(&mut buf);
e.binary(&[0xA5; 15], Lengths::max(16)).unwrap();
let len = e.finish().unwrap();
let mut d = Decoder::new(&buf[..len]);
assert_eq!(d.binary(CHALLENGE), Err(ExiError::ValueTooShort));
}
#[test]
fn a_minimum_length_applies_to_strings_as_well() {
const EMAID: Lengths = Lengths::new(14, 15);
let mut buf = [0u8; 128];
let mut e = Encoder::new(&mut buf);
assert_eq!(e.string(CTX_A, "DE123", EMAID), Err(ExiError::ValueTooShort));
e.string(CTX_A, "DE8ACME123456A", EMAID).unwrap();
let len = e.finish().unwrap();
let mut d = Decoder::new(&buf[..len]);
assert_eq!(d.string(CTX_A, EMAID).unwrap(), "DE8ACME123456A");
let mut e = Encoder::new(&mut buf);
e.string(CTX_A, "short", Lengths::max(64)).unwrap();
let len = e.finish().unwrap();
let mut d = Decoder::new(&buf[..len]);
assert_eq!(d.string(CTX_A, EMAID), Err(ExiError::ValueTooShort));
}
#[test]
fn over_long_strings_are_refused_both_ways() {
let mut buf = [0u8; 128];
let mut e = Encoder::new(&mut buf);
assert_eq!(e.string(CTX_A, "abcdef", Lengths::max(3)), Err(ExiError::ValueTooLong));
let mut e = Encoder::new(&mut buf);
e.string(CTX_A, "abcdef", Lengths::max(64)).unwrap();
let len = e.finish().unwrap();
let mut d = Decoder::new(&buf[..len]);
assert_eq!(d.string(CTX_A, Lengths::max(3)), Err(ExiError::ValueTooLong));
}
#[test]
fn a_forged_table_index_is_rejected() {
let mut buf = [0u8; 16];
let mut e = Encoder::new(&mut buf);
e.uint(0).unwrap();
let len = e.finish().unwrap();
let mut d = Decoder::new(&buf[..len]);
assert_eq!(d.string(CTX_A, Lengths::max(64)), Err(ExiError::BadStringTableIndex));
}
#[test]
fn trailing_bytes_are_rejected() {
let mut buf = [0u8; 16];
let mut e = Encoder::new(&mut buf);
e.write_header(Header::ISO15118).unwrap();
e.uint(7).unwrap();
let len = e.finish().unwrap();
let mut with_junk = alloc::vec::Vec::from(&buf[..len]);
with_junk.push(0xAA);
let mut d = Decoder::new(&with_junk);
d.read_header().unwrap();
assert_eq!(d.uint().unwrap(), 7);
assert_eq!(d.finish(), Err(ExiError::TrailingData));
}
#[test]
fn restricted_integers_use_the_range_not_the_type_width() {
let mut buf = [0u8; 16];
let mut e = Encoder::new(&mut buf);
e.restricted(20, 1, 20).unwrap();
assert_eq!(e.bit_len(), 5);
}
#[test]
fn restricted_integers_roundtrip_across_their_whole_range() {
for (min, max) in [(0i64, 255i64), (1, 20), (0, 0), (7, 9), (0, 4095), (-3, 3), (-128, 127)]
{
for v in [min, max, i64::midpoint(min, max)] {
let mut buf = [0u8; 32];
let mut e = Encoder::new(&mut buf);
e.restricted(v, min, max).unwrap();
let len = e.finish().unwrap();
let mut d = Decoder::new(&buf[..len]);
assert_eq!(d.restricted(min, max).unwrap(), v, "range {min}..={max}");
}
}
}
#[test]
fn encoding_out_of_range_is_refused() {
let mut buf = [0u8; 16];
let mut e = Encoder::new(&mut buf);
assert_eq!(e.restricted(21, 1, 20), Err(ExiError::ValueOutOfRange));
assert_eq!(e.restricted(0, 1, 20), Err(ExiError::ValueOutOfRange));
}
#[test]
fn a_range_straddling_zero_roundtrips() {
let mut buf = [0u8; 16];
let mut e = Encoder::new(&mut buf);
e.restricted(-3, -3, 3).unwrap();
assert_eq!(e.bit_len(), 3, "seven values need three bits");
e.restricted(3, -3, 3).unwrap();
let len = e.finish().unwrap();
let mut d = Decoder::new(&buf[..len]);
assert_eq!(d.restricted(-3, 3).unwrap(), -3);
assert_eq!(d.restricted(-3, 3).unwrap(), 3);
}
#[test]
fn decoding_an_unused_index_is_refused() {
for index in 20u64..32 {
let mut buf = [0u8; 16];
let mut e = Encoder::new(&mut buf);
e.nbit(index, 5).unwrap();
let len = e.finish().unwrap();
let mut d = Decoder::new(&buf[..len]);
assert_eq!(d.restricted(1, 20), Err(ExiError::ValueOutOfRange), "index {index}");
}
}
#[test]
fn a_full_width_range_does_not_wrap_to_zero_bits() {
let mut buf = [0u8; 32];
let mut e = Encoder::new(&mut buf);
e.restricted(0, i64::MIN, i64::MAX).unwrap();
assert_eq!(e.bit_len(), 64, "2^64 values need 64 bits, not none");
let len = e.finish().unwrap();
let mut d = Decoder::new(&buf[..len]);
assert_eq!(d.restricted(i64::MIN, i64::MAX).unwrap(), 0);
}
#[test]
fn a_single_valued_range_costs_no_bits() {
let mut buf = [0u8; 16];
let mut e = Encoder::new(&mut buf);
e.restricted(5, 5, 5).unwrap();
assert_eq!(e.bit_len(), 0);
let mut d = Decoder::new(&[]);
assert_eq!(d.restricted(5, 5).unwrap(), 5);
}
#[test]
fn depth_is_bounded() {
let mut buf = [0u8; 16];
let mut e = Encoder::new(&mut buf);
for _ in 0..MAX_DEPTH {
e.enter().unwrap();
}
assert_eq!(e.enter(), Err(ExiError::DepthLimitExceeded));
}
#[test]
fn the_depth_ceiling_survives_a_caller_that_ignores_it() {
let mut d = Decoder::new(&[]);
for _ in 0..MAX_DEPTH {
d.enter().unwrap();
}
for _ in 0..70_000 {
assert_eq!(d.enter(), Err(ExiError::DepthLimitExceeded));
}
}
}