use crate::bytes::LeCursor;
use crate::error::{Error, Result};
use super::container::{read_itf8, read_itf8_array};
#[derive(Debug)]
pub struct BitReader<'a> {
data: &'a [u8],
bit: usize,
path: &'a str,
}
impl<'a> BitReader<'a> {
pub fn new(data: &'a [u8], path: &'a str) -> Self {
Self { data, bit: 0, path }
}
fn exhausted(&self) -> Error {
Error::corrupt(
self.path,
(self.bit / 8) as u64,
"a core data block ran out of bits mid-record",
)
}
#[inline]
pub fn read_bit(&mut self) -> Result<u32> {
let byte = *self
.data
.get(self.bit >> 3)
.ok_or_else(|| self.exhausted())?;
let value = u32::from(byte >> (7 - (self.bit & 7))) & 1;
self.bit += 1;
Ok(value)
}
#[inline]
pub fn read_bits(&mut self, n: u32) -> Result<u32> {
if n > 32 {
return Err(Error::corrupt(
self.path,
(self.bit / 8) as u64,
format!("a {n}-bit field, which does not fit the 32 bits it is read into"),
));
}
let mut value = 0u32;
for _ in 0..n {
value = (value << 1) | self.read_bit()?;
}
Ok(value)
}
}
#[derive(Debug)]
struct BlockCursor<'a> {
data: &'a [u8],
pos: usize,
}
#[derive(Debug)]
pub struct Streams<'a> {
pub core: BitReader<'a>,
ids: Vec<i32>,
cursors: Vec<BlockCursor<'a>>,
path: &'a str,
}
impl<'a> Streams<'a> {
pub fn new(
core: &'a [u8],
external: impl IntoIterator<Item = (i32, &'a [u8])>,
path: &'a str,
) -> Self {
let (ids, cursors) = external
.into_iter()
.map(|(id, data)| (id, BlockCursor { data, pos: 0 }))
.unzip();
Self {
core: BitReader::new(core, path),
ids,
cursors,
path,
}
}
fn index(&self, id: i32) -> Result<usize> {
self.ids
.iter()
.position(|candidate| *candidate == id)
.ok_or_else(|| {
Error::corrupt(
self.path,
0,
format!(
"a data series reads external block {id}, which its slice does not carry"
),
)
})
}
fn block(&mut self, id: i32) -> Result<&mut BlockCursor<'a>> {
let index = self.index(id)?;
Ok(&mut self.cursors[index])
}
fn take(&mut self, id: i32, n: usize) -> Result<&'a [u8]> {
let path = self.path;
let cursor = self.block(id)?;
let end = cursor
.pos
.checked_add(n)
.filter(|end| *end <= cursor.data.len())
.ok_or_else(|| {
Error::corrupt(
path,
cursor.pos as u64,
format!(
"a data series wants {n} bytes of external block {id} with {} left",
cursor.data.len() - cursor.pos
),
)
})?;
let out = &cursor.data[cursor.pos..end];
cursor.pos = end;
Ok(out)
}
fn byte(&mut self, id: i32) -> Result<u8> {
Ok(self.take(id, 1)?[0])
}
fn take_until(&mut self, id: i32, stop: u8) -> Result<&'a [u8]> {
let path = self.path;
let cursor = self.block(id)?;
let rest = &cursor.data[cursor.pos..];
let end = memchr::memchr(stop, rest).ok_or_else(|| {
Error::corrupt(
path,
cursor.pos as u64,
format!("a byte array in external block {id} with no {stop:#04x} to end it"),
)
})?;
cursor.pos += end + 1;
Ok(&rest[..end])
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub enum Encoding {
#[default]
Null,
External {
block_id: i32,
},
Huffman {
symbols: Vec<i32>,
lengths: Vec<(u32, u32, usize, usize)>,
constant: Option<i32>,
},
ByteArrayLen {
len: Box<Encoding>,
value: Box<Encoding>,
},
ByteArrayStop {
stop: u8,
block_id: i32,
},
Beta {
offset: i32,
bits: u32,
},
Subexp {
offset: i32,
k: u32,
},
Gamma {
offset: i32,
},
}
impl Encoding {
pub fn read(cursor: &mut LeCursor<'_>) -> Result<Self> {
let codec = read_itf8(cursor)?;
let n_bytes = read_itf8(cursor)?;
if n_bytes < 0 {
return Err(Error::corrupt(
cursor.path(),
cursor.file_offset(),
format!("an encoding with {n_bytes} parameter bytes"),
));
}
let start = cursor.position();
let end = start + n_bytes as usize;
let encoding = Self::read_params(cursor, codec)?;
cursor.seek(end)?;
Ok(encoding)
}
fn read_params(cursor: &mut LeCursor<'_>, codec: i32) -> Result<Self> {
Ok(match codec {
0 => Self::Null,
1 => Self::External {
block_id: read_itf8(cursor)?,
},
3 => {
let symbols = read_itf8_array(cursor)?;
let lengths = read_itf8_array(cursor)?;
Self::huffman(symbols, lengths, cursor)?
}
4 => Self::ByteArrayLen {
len: Box::new(Self::read(cursor)?),
value: Box::new(Self::read(cursor)?),
},
5 => Self::ByteArrayStop {
stop: cursor.take(1)?[0],
block_id: read_itf8(cursor)?,
},
6 => Self::Beta {
offset: read_itf8(cursor)?,
bits: Self::bit_width(read_itf8(cursor)?, cursor)?,
},
7 => Self::Subexp {
offset: read_itf8(cursor)?,
k: Self::bit_width(read_itf8(cursor)?, cursor)?,
},
9 => Self::Gamma {
offset: read_itf8(cursor)?,
},
2 | 8 => {
return Err(Error::Unsupported(format!(
"{}: a data series uses golomb coding, which cram 3 does not define",
cursor.path()
)))
}
other => {
return Err(Error::corrupt(
cursor.path(),
cursor.file_offset(),
format!("encoding codec {other} is not one the format defines"),
))
}
})
}
fn bit_width(value: i32, cursor: &LeCursor<'_>) -> Result<u32> {
if !(0..=32).contains(&value) {
return Err(Error::corrupt(
cursor.path(),
cursor.file_offset(),
format!("an encoding of {value} bits"),
));
}
Ok(value as u32)
}
fn huffman(symbols: Vec<i32>, bit_lengths: Vec<i32>, cursor: &LeCursor<'_>) -> Result<Self> {
if symbols.len() != bit_lengths.len() {
return Err(Error::corrupt(
cursor.path(),
cursor.file_offset(),
format!(
"a huffman table of {} symbols and {} code lengths",
symbols.len(),
bit_lengths.len()
),
));
}
if symbols.is_empty() {
return Err(Error::corrupt(
cursor.path(),
cursor.file_offset(),
"an empty huffman table",
));
}
if symbols.len() == 1 {
return Ok(Self::Huffman {
symbols,
lengths: Vec::new(),
constant: None,
}
.with_constant());
}
let mut pairs: Vec<(u32, i32)> = bit_lengths
.iter()
.zip(&symbols)
.map(|(len, symbol)| {
if !(1..=32).contains(len) {
return Err(Error::corrupt(
cursor.path(),
cursor.file_offset(),
format!("a huffman codeword of {len} bits"),
));
}
Ok((*len as u32, *symbol))
})
.collect::<Result<_>>()?;
pairs.sort_unstable();
let mut sorted = Vec::with_capacity(pairs.len());
let mut lengths: Vec<(u32, u32, usize, usize)> = Vec::new();
let mut code = 0u32;
let mut previous_len = 0u32;
let mut kraft = 0u64;
for (index, (len, symbol)) in pairs.into_iter().enumerate() {
kraft += 1u64 << (32 - len.min(32));
if kraft > 1u64 << 32 {
return Err(Error::corrupt(
cursor.path(),
cursor.file_offset(),
"a huffman table whose code lengths over-subscribe the code",
));
}
if index > 0 {
code = code.wrapping_add(1);
code = code.wrapping_shl(len - previous_len);
}
if lengths.last().map(|(l, ..)| *l) != Some(len) {
lengths.push((len, code, index, 0));
}
let last = lengths.last_mut().expect("just pushed");
last.3 += 1;
previous_len = len;
sorted.push(symbol);
}
Ok(Self::Huffman {
symbols: sorted,
lengths,
constant: None,
})
}
fn with_constant(self) -> Self {
match self {
Self::Huffman { symbols, .. } => {
let constant = symbols.first().copied();
Self::Huffman {
symbols,
lengths: Vec::new(),
constant,
}
}
other => other,
}
}
pub fn is_null(&self) -> bool {
matches!(self, Self::Null)
}
pub fn block_ids(&self, out: &mut Vec<i32>) {
match self {
Self::External { block_id } | Self::ByteArrayStop { block_id, .. } => {
out.push(*block_id)
}
Self::ByteArrayLen { len, value } => {
len.block_ids(out);
value.block_ids(out);
}
_ => {}
}
}
pub fn decode_int(&self, streams: &mut Streams<'_>) -> Result<i32> {
Ok(match self {
Self::External { block_id } => {
let path = streams.path;
let cursor = streams.block(*block_id)?;
let (data, pos) = (cursor.data, cursor.pos);
let mut le = LeCursor::new(&data[pos..], pos as u64, path);
let value = read_itf8(&mut le)?;
cursor.pos += le.position();
value
}
Self::Huffman {
symbols,
lengths,
constant,
} => {
if let Some(value) = constant {
return Ok(*value);
}
Self::huffman_decode(symbols, lengths, streams)?
}
Self::Beta { offset, bits } => {
(streams.core.read_bits(*bits)? as i32).wrapping_sub(*offset)
}
Self::Subexp { offset, k } => {
let mut u = 0u32;
while streams.core.read_bit()? == 1 {
u += 1;
if u > 32 {
return Err(Error::corrupt(
streams.path,
0,
"a subexponential codeword with more than 32 leading ones",
));
}
}
let n = if u == 0 {
streams.core.read_bits(*k)?
} else {
let width = u + k - 1;
if width >= 32 {
return Err(Error::corrupt(
streams.path,
0,
format!("a subexponential codeword {width} bits wide"),
));
}
(1u32 << width) + streams.core.read_bits(width)?
};
(n as i32).wrapping_sub(*offset)
}
Self::Gamma { offset } => {
let mut zeros = 0u32;
while streams.core.read_bit()? == 0 {
zeros += 1;
if zeros > 32 {
return Err(Error::corrupt(
streams.path,
0,
"an elias gamma codeword with more than 32 leading zeros",
));
}
}
let mut value = 1u32;
for _ in 0..zeros {
value = (value << 1) | streams.core.read_bit()?;
}
(value as i32).wrapping_sub(*offset)
}
Self::Null => return Err(self.null_error(streams.path)),
Self::ByteArrayLen { .. } | Self::ByteArrayStop { .. } => {
return Err(Error::corrupt(
streams.path,
0,
"a byte-array encoding used for an integer data series",
))
}
})
}
pub fn decode_byte(&self, streams: &mut Streams<'_>) -> Result<u8> {
Ok(match self {
Self::External { block_id } => streams.byte(*block_id)?,
Self::Huffman { .. } | Self::Beta { .. } | Self::Subexp { .. } | Self::Gamma { .. } => {
self.decode_int(streams)? as u8
}
Self::Null => return Err(self.null_error(streams.path)),
Self::ByteArrayLen { .. } | Self::ByteArrayStop { .. } => {
return Err(Error::corrupt(
streams.path,
0,
"a byte-array encoding used for a single-byte data series",
))
}
})
}
pub fn decode_array(
&self,
streams: &mut Streams<'_>,
len: Option<usize>,
out: &mut Vec<u8>,
) -> Result<()> {
match self {
Self::ByteArrayStop { stop, block_id } => {
out.extend_from_slice(streams.take_until(*block_id, *stop)?);
}
Self::ByteArrayLen {
len: len_enc,
value,
} => {
let n = len_enc.decode_int(streams)?;
if n < 0 {
return Err(Error::corrupt(
streams.path,
0,
format!("a byte array of {n} bytes"),
));
}
value.decode_n(streams, n as usize, out)?;
}
Self::External { .. } | Self::Huffman { .. } => {
let n = len.ok_or_else(|| {
Error::corrupt(
streams.path,
0,
"a byte-array data series with neither a stored length nor one to hand",
)
})?;
self.decode_n(streams, n, out)?;
}
Self::Null => return Err(self.null_error(streams.path)),
other => {
return Err(Error::corrupt(
streams.path,
0,
format!("{other:?} cannot encode a byte array"),
))
}
}
Ok(())
}
fn decode_n(&self, streams: &mut Streams<'_>, n: usize, out: &mut Vec<u8>) -> Result<()> {
if let Self::External { block_id } = self {
out.extend_from_slice(streams.take(*block_id, n)?);
return Ok(());
}
out.reserve(n.min(1 << 20));
for _ in 0..n {
out.push(self.decode_byte(streams)?);
}
Ok(())
}
fn huffman_decode(
symbols: &[i32],
lengths: &[(u32, u32, usize, usize)],
streams: &mut Streams<'_>,
) -> Result<i32> {
let mut code = 0u32;
let mut bits = 0u32;
for &(len, first_code, first_index, count) in lengths {
while bits < len {
code = (code << 1) | streams.core.read_bit()?;
bits += 1;
}
let offset = code.wrapping_sub(first_code) as usize;
if offset < count {
return Ok(symbols[first_index + offset]);
}
}
Err(Error::corrupt(
streams.path,
0,
"a bit pattern no huffman codeword in this slice's table matches",
))
}
fn null_error(&self, path: &str) -> Error {
Error::corrupt(
path,
0,
"a record reads a data series this file's encoding map stores as NULL",
)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn streams<'a>(core: &'a [u8], external: &'a [(i32, &'a [u8])]) -> Streams<'a> {
Streams::new(core, external.iter().map(|(id, d)| (*id, *d)), "test")
}
fn parse(bytes: &[u8]) -> Encoding {
let mut cursor = LeCursor::new(bytes, 0, "test");
Encoding::read(&mut cursor).expect("an encoding")
}
#[test]
fn hostile_bit_codec_parameters_give_errors_rather_than_overflow() {
let subexp = Encoding::Subexp { offset: 0, k: 1 };
let ones = [0xffu8; 8];
let mut s = streams(&ones, &[]);
assert!(subexp.decode_int(&mut s).is_err());
let beta = Encoding::Beta {
offset: -1,
bits: 31,
};
let mut s = streams(&ones, &[]);
assert_eq!(
beta.decode_int(&mut s).expect("wraps, does not abort"),
i32::MIN
);
let gamma = Encoding::Gamma { offset: -1 };
let zeros_then_ones = [0x00u8, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
let mut s = streams(&zeros_then_ones, &[]);
let _ = gamma.decode_int(&mut s);
}
#[test]
fn an_over_subscribed_huffman_table_is_refused() {
let mut bytes = vec![3u8, 12u8]; bytes.push(4); bytes.extend_from_slice(&[0, 1, 2, 3]);
bytes.push(4); bytes.extend_from_slice(&[1, 1, 1, 1]);
let mut cursor = LeCursor::new(&bytes, 0, "test");
let error = Encoding::read(&mut cursor).expect_err("over-subscribed");
assert!(error.to_string().contains("over-subscribe"), "{error}");
let mut bytes = vec![3u8, 0u8];
let mut params = vec![34u8];
params.extend(0..34u8);
params.push(34);
params.extend((1..=32u8).chain([32, 32]));
bytes[1] = params.len() as u8;
bytes.extend_from_slice(¶ms);
let mut cursor = LeCursor::new(&bytes, 0, "test");
assert!(Encoding::read(&mut cursor).is_err(), "one code too many");
}
#[test]
fn the_widest_complete_huffman_table_is_accepted_without_overflow() {
let mut bytes = vec![3u8, 0u8];
let mut params = vec![33u8];
params.extend(0..33u8);
params.push(33);
params.extend((1..=32u8).chain(std::iter::once(32)));
bytes[1] = params.len() as u8;
bytes.extend_from_slice(¶ms);
let mut cursor = LeCursor::new(&bytes, 0, "test");
Encoding::read(&mut cursor).expect("a complete code, however wide");
}
#[test]
fn a_complete_huffman_table_is_accepted() {
let mut bytes = vec![3u8, 0u8];
let params = vec![2u8, 0, 1, 2, 1, 1];
bytes[1] = params.len() as u8;
bytes.extend_from_slice(¶ms);
let mut cursor = LeCursor::new(&bytes, 0, "test");
let encoding = Encoding::read(&mut cursor).expect("two one-bit codes fill the code");
let mut s = streams(&[0b0100_0000], &[]);
assert_eq!(encoding.decode_int(&mut s).expect("first"), 0);
assert_eq!(encoding.decode_int(&mut s).expect("second"), 1);
}
#[test]
fn the_spec_byte_array_len_example_parses_to_what_it_describes() {
let bytes = [
0x04, 0x0a, 0x03, 0x04, 0x01, 0x02, 0x01, 0x00, 0x01, 0x02, 0x80, 0xc8, ];
let mut cursor = LeCursor::new(&bytes, 0, "test");
let encoding = Encoding::read(&mut cursor).expect("an encoding");
let Encoding::ByteArrayLen { len, value } = &encoding else {
panic!("not a byte array len: {encoding:?}")
};
assert_eq!(**value, Encoding::External { block_id: 200 });
let mut s = streams(&[], &[(200, b"ab")]);
assert_eq!(len.decode_int(&mut s).expect("constant"), 2);
let mut out = Vec::new();
encoding
.decode_array(&mut s, None, &mut out)
.expect("array");
assert_eq!(out, b"ab");
}
#[test]
fn canonical_huffman_assigns_the_codewords_the_spec_prints() {
let mut bytes = vec![0x03, 0x00];
let mut params = vec![6u8, 0, 1, 2, 3, 4, 5]; params.extend_from_slice(&[6, 1, 3, 3, 3, 4, 4]); bytes[1] = params.len() as u8;
bytes.extend_from_slice(¶ms);
let encoding = parse(&bytes);
let core = [0b0100_1011, 0b1011_1011, 0b1100_0000];
let mut s = streams(&core, &[]);
for expected in [0, 1, 2, 3, 4, 5] {
assert_eq!(encoding.decode_int(&mut s).expect("a symbol"), expected);
}
}
#[test]
fn a_one_symbol_huffman_is_a_constant_and_reads_no_bits() {
let bytes = [0x03, 0x04, 0x01, 0x2a, 0x01, 0x00];
let encoding = parse(&bytes);
let mut s = streams(&[], &[]);
for _ in 0..100 {
assert_eq!(encoding.decode_int(&mut s).expect("constant"), 42);
}
}
#[test]
fn beta_coding_reads_the_spec_example() {
let mut params = vec![0xff, 0xff, 0xff, 0xff, 0x06]; params.push(3);
let mut bytes = vec![0x06, params.len() as u8];
bytes.extend_from_slice(¶ms);
let encoding = parse(&bytes);
assert_eq!(
encoding,
Encoding::Beta {
offset: -10,
bits: 3
}
);
let core = [0b0000_0101, 0b0011_1001, 0b0100_0000];
let mut s = streams(&core, &[]);
for expected in 10..=15 {
assert_eq!(encoding.decode_int(&mut s).expect("a value"), expected);
}
}
#[test]
fn subexponential_coding_reads_the_spec_examples() {
let cases: [(u32, &[&str]); 3] = [
(
0,
&[
"0", "10", "1100", "1101", "111000", "111001", "111010", "111011", "11110000",
"11110001", "11110010",
],
),
(
1,
&[
"00", "01", "100", "101", "11000", "11001", "11010", "11011", "1110000",
"1110001", "1110010",
],
),
(
2,
&[
"000", "001", "010", "011", "1000", "1001", "1010", "1011", "110000", "110001",
"110010",
],
),
];
for (k, codewords) in cases {
let encoding = Encoding::Subexp { offset: 0, k };
let bits: String = codewords.concat();
let core = pack_bits(&bits);
let mut s = streams(&core, &[]);
for (expected, codeword) in codewords.iter().enumerate() {
assert_eq!(
encoding.decode_int(&mut s).expect("a value"),
expected as i32,
"k={k}, codeword {codeword}"
);
}
}
}
#[test]
fn gamma_coding_reads_the_spec_example() {
let encoding = Encoding::Gamma { offset: 0 };
let core = pack_bits("1010011 00100");
let mut s = streams(&core, &[]);
for expected in [1, 2, 3, 4] {
assert_eq!(encoding.decode_int(&mut s).expect("a value"), expected);
}
}
#[test]
fn byte_array_stop_returns_what_precedes_its_terminator() {
let bytes = [0x05, 0x02, 0x00, 0x0b]; let encoding = parse(&bytes);
let mut s = streams(&[], &[(11, b"first\0second\0")]);
let mut out = Vec::new();
encoding
.decode_array(&mut s, None, &mut out)
.expect("array");
assert_eq!(out, b"first");
out.clear();
encoding
.decode_array(&mut s, None, &mut out)
.expect("array");
assert_eq!(out, b"second");
assert!(encoding.decode_array(&mut s, None, &mut out).is_err());
}
#[test]
fn external_reads_itf8_for_integers_and_raw_bytes_for_bytes() {
let encoding = Encoding::External { block_id: 1 };
let mut s = streams(&[], &[(1, &[0xc1, 0x00, 0x00])]);
assert_eq!(encoding.decode_int(&mut s).expect("an int"), 0x1_0000);
let mut s = streams(&[], &[(1, &[0xc1, 0x00, 0x00])]);
assert_eq!(encoding.decode_byte(&mut s).expect("a byte"), 0xc1);
}
#[test]
fn two_series_on_one_block_consume_it_in_read_order() {
let a = Encoding::External { block_id: 7 };
let b = Encoding::External { block_id: 7 };
let mut s = streams(&[], &[(7, b"xy")]);
assert_eq!(a.decode_byte(&mut s).expect("a"), b'x');
assert_eq!(b.decode_byte(&mut s).expect("b"), b'y');
}
#[test]
fn a_null_series_is_an_error_rather_than_a_default() {
let mut s = streams(&[], &[]);
assert!(Encoding::Null.decode_int(&mut s).is_err());
assert!(Encoding::Null.decode_byte(&mut s).is_err());
}
#[test]
fn golomb_is_refused_by_name() {
for codec in [2u8, 8] {
let bytes = [codec, 0x02, 0x00, 0x01];
let mut cursor = LeCursor::new(&bytes, 0, "test");
match Encoding::read(&mut cursor) {
Err(Error::Unsupported(message)) => {
assert!(message.contains("golomb"), "{message}")
}
other => panic!("codec {codec} gave {other:?}"),
}
}
}
#[test]
fn a_longer_parameter_block_than_the_codec_reads_is_skipped_whole() {
let bytes = [
0x01, 0x04, 0x0b, 0xff, 0xff, 0xff, 0x01, 0x01, 0x0c, ];
let mut cursor = LeCursor::new(&bytes, 0, "test");
assert_eq!(
Encoding::read(&mut cursor).expect("first"),
Encoding::External { block_id: 11 }
);
assert_eq!(
Encoding::read(&mut cursor).expect("second"),
Encoding::External { block_id: 12 }
);
}
fn pack_bits(bits: &str) -> Vec<u8> {
let mut out = Vec::new();
let mut byte = 0u8;
let mut n = 0;
for c in bits.chars().filter(|c| *c == '0' || *c == '1') {
byte = (byte << 1) | u8::from(c == '1');
n += 1;
if n == 8 {
out.push(byte);
byte = 0;
n = 0;
}
}
if n > 0 {
out.push(byte << (8 - n));
}
out
}
}