1use crate::error::{Error, Result};
4use crate::fse::{ncount, Norm};
5use crate::huf::{weights, HufWeights};
6use alloc::vec::Vec;
7
8pub const DICT_MAGIC: u32 = 0xEC30_A437;
9
10#[derive(Clone, Debug)]
12pub struct DictTables {
13 pub huf: HufWeights,
14 pub of: Norm,
15 pub ml: Norm,
16 pub ll: Norm,
17}
18
19#[derive(Clone, Debug)]
21pub struct Dictionary {
22 pub(crate) id: u32,
23 pub(crate) content: Vec<u8>,
24 pub(crate) tables: Option<DictTables>,
25 pub(crate) reps: [u32; 3],
26}
27
28#[derive(Clone, Copy, Debug, PartialEq, Eq)]
30pub enum DictionaryFormat {
31 Zstd,
33 Raw {
35 id: u32,
37 },
38 Detect,
40}
41
42impl Dictionary {
43 pub fn new(bytes: &[u8], format: DictionaryFormat) -> Result<Dictionary> {
45 let has_magic = bytes.len() >= 8 && u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) == DICT_MAGIC;
46 match format {
47 DictionaryFormat::Zstd if has_magic => Self::parse_zstd(bytes),
48 DictionaryFormat::Zstd => Err(Error::BadDictionary("missing zstd dictionary magic")),
49 DictionaryFormat::Raw { id } => Ok(Self::raw(bytes, id)),
50 DictionaryFormat::Detect if has_magic => Self::parse_zstd(bytes),
51 DictionaryFormat::Detect => Ok(Self::raw(bytes, 0)),
52 }
53 }
54
55 fn raw(bytes: &[u8], id: u32) -> Dictionary {
56 Dictionary { id, content: bytes.to_vec(), tables: None, reps: [1, 4, 8] }
57 }
58
59 fn parse_zstd(bytes: &[u8]) -> Result<Dictionary> {
60 let id = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
61 let mut at = 8;
62 let bad = |e: Error| match e {
63 Error::Corrupt(why) => Error::BadDictionary(why),
64 Error::Truncated => Error::BadDictionary("truncated entropy tables"),
65 other => other,
66 };
67 let (huf, used) = weights::read(&bytes[at..]).map_err(bad)?;
68 at += used;
69 let (of, used) = ncount::read(&bytes[at..], 32, 8).map_err(bad)?;
70 at += used;
71 let (ml, used) = ncount::read(&bytes[at..], 53, 9).map_err(bad)?;
72 at += used;
73 let (ll, used) = ncount::read(&bytes[at..], 36, 9).map_err(bad)?;
74 at += used;
75 let rep_bytes = bytes.get(at..at + 12).ok_or(Error::BadDictionary("truncated repeat offsets"))?;
76 let mut reps = [0u32; 3];
77 for (i, r) in reps.iter_mut().enumerate() {
78 let b = &rep_bytes[4 * i..4 * i + 4];
79 *r = u32::from_le_bytes([b[0], b[1], b[2], b[3]]);
80 }
81 let content = bytes[at + 12..].to_vec();
82 for &r in &reps {
83 if r == 0 || r as usize > content.len() {
84 return Err(Error::BadDictionary("repeat offset outside content"));
85 }
86 }
87 Ok(Dictionary { id, content, tables: Some(DictTables { huf, of, ml, ll }), reps })
88 }
89
90 pub fn id(&self) -> u32 {
92 self.id
93 }
94
95 pub fn content(&self) -> &[u8] {
97 &self.content
98 }
99
100 pub fn has_entropy_tables(&self) -> bool {
102 self.tables.is_some()
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109
110 #[test]
111 fn raw_and_detect() {
112 let d = Dictionary::new(b"hello world", DictionaryFormat::Detect).unwrap();
113 assert_eq!((d.id(), d.content(), d.has_entropy_tables()), (0, &b"hello world"[..], false));
114 assert!(Dictionary::new(b"hello world", DictionaryFormat::Zstd).is_err());
115 let r = Dictionary::new(b"x", DictionaryFormat::Raw { id: 9 }).unwrap();
116 assert_eq!(r.id(), 9);
117 }
118
119 #[test]
120 fn truncated_zstd_dict_is_error() {
121 let mut b = DICT_MAGIC.to_le_bytes().to_vec();
122 b.extend_from_slice(&[1, 0, 0, 0, 0x80]);
123 assert!(matches!(Dictionary::new(&b, DictionaryFormat::Zstd), Err(Error::BadDictionary(_))));
124 }
125}