use crate::compression::CompressionStats;
pub struct MultiLevelDictionary {
level1: Vec<Vec<u8>>, level2: Vec<Vec<u8>>, level3: Vec<Vec<u8>>, indices_level1: Vec<u8>, indices_level2: Vec<u16>, level3_entries: Vec<(Vec<u8>, u32)>, }
impl MultiLevelDictionary {
pub fn new() -> Self {
Self {
level1: vec![],
level2: vec![],
level3: vec![],
indices_level1: vec![],
indices_level2: vec![],
level3_entries: vec![],
}
}
pub fn build(&mut self, value_counts: &[(Vec<u8>, u32)]) {
let mut sorted = value_counts.to_vec();
sorted.sort_by(|a, b| b.1.cmp(&a.1));
let mut total_count = 0;
let total_sum: u32 = sorted.iter().map(|(_, c)| c).sum();
for (value, count) in sorted {
total_count += count;
let percentage = (total_count as f64 / total_sum as f64) * 100.0;
if self.level1.len() < 256 && percentage < 80.0 {
self.level1.push(value);
} else if self.level2.len() < 65536 && percentage < 99.0 {
self.level2.push(value);
} else {
self.level3.push(value);
}
}
}
pub fn encode_value(&self, value: &[u8]) -> (Vec<u8>, u8) {
if let Some(idx) = self.level1.iter().position(|v| v == value) {
return (vec![idx as u8], 1);
}
if let Some(idx) = self.level2.iter().position(|v| v == value) {
let bytes = (idx as u16).to_le_bytes().to_vec();
return (bytes, 2);
}
(value.to_vec(), 3)
}
pub fn compress(&self, data: &[&[u8]], stats: &mut CompressionStats) -> Option<Vec<u8>> {
let mut result = vec![];
result.push(self.level1.len() as u8);
result.extend((self.level2.len() as u16).to_le_bytes());
for entry in &self.level1 {
result.push(entry.len() as u8);
result.extend_from_slice(entry);
}
for entry in &self.level2 {
result.push((entry.len() >> 8) as u8);
result.push(entry.len() as u8);
result.extend_from_slice(entry);
}
for value in data {
let (encoded, level) = self.encode_value(value);
result.push(level);
result.extend(encoded);
}
let original_total: usize = data.iter().map(|v| v.len()).sum();
stats.original_size += original_total;
stats.compressed_size += result.len();
if result.len() >= original_total {
return None; }
Some(result)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_multi_level_dictionary() {
let mut dict = MultiLevelDictionary::new();
let values = vec![
(b"active".to_vec(), 450),
(b"inactive".to_vec(), 350),
(b"pending".to_vec(), 190),
(b"archived".to_vec(), 10),
];
dict.build(&values);
let (encoded, level) = dict.encode_value(b"active");
assert_eq!(level, 1);
assert_eq!(encoded.len(), 1);
}
}