use crate::block::{LL_SPEC, ML_SPEC, OF_SPEC};
use crate::error::Error;
use crate::fse::{self, FseTable};
use crate::huffman::{self, HuffmanTable};
const DICT_MAGIC: u32 = 0xEC30_A437;
pub struct Dictionary {
id: u32,
content: Vec<u8>,
huffman: Option<HuffmanTable>,
ll: Option<FseTable>,
of: Option<FseTable>,
ml: Option<FseTable>,
rep: [u64; 3],
}
impl Dictionary {
pub fn new(bytes: &[u8]) -> Result<Self, Error> {
if bytes.len() >= 8 && u32::from_le_bytes(bytes[..4].try_into().unwrap()) == DICT_MAGIC {
Self::parse_formatted(bytes)
} else {
Ok(Self::raw_content(bytes))
}
}
pub fn raw_content(content: &[u8]) -> Self {
Dictionary {
id: 0,
content: content.to_vec(),
huffman: None,
ll: None,
of: None,
ml: None,
rep: [1, 4, 8],
}
}
pub fn id(&self) -> u32 {
self.id
}
fn parse_formatted(bytes: &[u8]) -> Result<Self, Error> {
let corrupt = Error::DictionaryCorrupted;
let id = u32::from_le_bytes(bytes[4..8].try_into().unwrap());
let mut pos = 8usize;
let (huffman, used) =
huffman::read_table(bytes.get(pos..).ok_or(corrupt)?).map_err(|_| corrupt)?;
pos += used;
let of = read_dict_fse(bytes, &mut pos, &OF_SPEC)?;
let ml = read_dict_fse(bytes, &mut pos, &ML_SPEC)?;
let ll = read_dict_fse(bytes, &mut pos, &LL_SPEC)?;
let reps = bytes.get(pos..pos + 12).ok_or(corrupt)?;
let content = bytes[pos + 12..].to_vec();
let mut rep = [0u64; 3];
for (i, slot) in rep.iter_mut().enumerate() {
let r = u32::from_le_bytes(reps[4 * i..4 * i + 4].try_into().unwrap()) as u64;
if r == 0 || r > content.len() as u64 {
return Err(corrupt);
}
*slot = r;
}
Ok(Dictionary {
id,
content,
huffman: Some(huffman),
ll: Some(ll),
of: Some(of),
ml: Some(ml),
rep,
})
}
pub(crate) fn content(&self) -> &[u8] {
&self.content
}
pub(crate) fn huffman(&self) -> Option<&HuffmanTable> {
self.huffman.as_ref()
}
pub(crate) fn ll(&self) -> Option<&FseTable> {
self.ll.as_ref()
}
pub(crate) fn of(&self) -> Option<&FseTable> {
self.of.as_ref()
}
pub(crate) fn ml(&self) -> Option<&FseTable> {
self.ml.as_ref()
}
pub(crate) fn rep(&self) -> [u64; 3] {
self.rep
}
}
fn read_dict_fse(
bytes: &[u8],
pos: &mut usize,
spec: &crate::block::SeqTableSpec,
) -> Result<FseTable, Error> {
let corrupt = Error::DictionaryCorrupted;
let nc = fse::read_ncount(
bytes.get(*pos..).ok_or(corrupt)?,
spec.max_symbol,
spec.max_log,
)
.map_err(|_| corrupt)?;
let table = fse::build_dtable(&nc.counts, nc.table_log).map_err(|_| corrupt)?;
*pos += nc.bytes_consumed;
Ok(table)
}