use super::*;
use crate::jbig2::mq::{CodedSpan, ContextBank, ContextState, MqTable};
use crate::test_support::mq_encoder;
use crate::{Limits, Payload};
fn round_trip(code_len: u32, values: &[u64]) -> (Vec<u64>, Vec<ContextState>) {
let mut encoder = mq_encoder();
for &value in values {
encoder.iaid(IAID_BASE, code_len, value);
}
let symbols = encoder.decisions();
let bytes = encoder.finish();
let limits = Limits::default();
let contexts = IAID_BASE + (1 << code_len);
let mut bank = ContextBank::new(contexts, &limits).unwrap();
let source = Payload::from(&bytes[..]);
let table = MqTable::standard();
let mut decoder = MqDecoder::new(
source,
CodedSpan {
offset: 0,
length: bytes.len() as u64,
},
&table,
&mut bank,
&limits,
)
.unwrap();
let decoded = values
.iter()
.map(|_| decode_iaid(&mut decoder, code_len).unwrap())
.collect();
assert_eq!(decoder.snapshot().symbols_decoded, symbols);
assert_eq!(symbols, u64::from(code_len) * values.len() as u64);
decoder.finish(symbols).unwrap();
let states = (0..contexts)
.map(|index| bank.get(index).unwrap())
.collect();
(decoded, states)
}
#[test]
fn annex_a3_example_and_every_small_codeword() {
let (decoded, states) = round_trip(3, &[2]);
assert_eq!(decoded, [2]);
assert_ne!(states[IAID_BASE + 2], ContextState::default());
for (index, state) in states.iter().enumerate() {
if ![IAID_BASE + 1, IAID_BASE + 2, IAID_BASE + 5].contains(&index) {
assert_eq!(*state, ContextState::default(), "context {index}");
}
}
for len in 0..=3u32 {
let values: Vec<_> = (0..1u64 << len).collect();
assert_eq!(round_trip(len, &values).0, values);
}
}
#[test]
fn largest_default_width_and_truncated_stream_error() {
let (decoded, states) = round_trip(15, &[0x5555, 0x7fff, 0]);
assert_eq!(decoded, [0x5555, 0x7fff, 0]);
assert_eq!(states.len(), IAID_BASE + 32_768);
assert!(
states[..IAID_BASE]
.iter()
.all(|state| *state == ContextState::default())
);
let bytes = [0x00, 0x00];
let limits = Limits::default();
let mut bank = ContextBank::new(IAID_BASE + 32_768, &limits).unwrap();
let source = Payload::from(&bytes[..]);
let table = MqTable::standard();
let mut decoder = MqDecoder::new(
source,
CodedSpan {
offset: 0,
length: 2,
},
&table,
&mut bank,
&limits,
)
.unwrap();
let error = decode_iaid(&mut decoder, 15).unwrap_err();
assert_eq!(error.reason, "MQ coding unit lacks its terminal marker");
assert_eq!(error.offset, Some(2));
}
#[test]
fn widths_beyond_the_bank_or_the_address_space_are_refused_before_input() {
let bytes = [0xff, 0xac];
let limits = Limits::default();
let mut bank = ContextBank::new(IAID_BASE + 8, &limits).unwrap();
let source = Payload::from(&bytes[..]);
let table = MqTable::standard();
let mut decoder = MqDecoder::new(
source,
CodedSpan {
offset: 0,
length: 2,
},
&table,
&mut bank,
&limits,
)
.unwrap();
let before = decoder.snapshot();
for len in [4, usize::BITS, u32::MAX] {
let error = decode_iaid(&mut decoder, len).unwrap_err();
assert_eq!(
error.reason, "invalid arithmetic context index or count",
"width {len}"
);
assert_eq!(decoder.snapshot(), before);
}
assert_eq!(decode_iaid(&mut decoder, 0).unwrap(), 0);
assert_eq!(decoder.snapshot(), before);
decoder.finish(0).unwrap();
}