use std::io::{Read, Write};
use ppmd_rust::{Ppmd8Decoder, Ppmd8Encoder, RestoreMethod, PPMD8_MIN_MEM_SIZE};
fn pseudo_text(size: usize) -> Vec<u8> {
let mut state: u64 = 0x2545_F491_4F6C_DD1D;
let mut next = move || {
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;
state
};
let vocabulary: Vec<String> = (0..60_000)
.map(|_| {
let length = 3 + (next() % 7) as usize;
(0..length)
.map(|_| (b'a' + (next() % 26) as u8) as char)
.collect()
})
.collect();
let mut text = Vec::with_capacity(size + 16);
while text.len() < size {
let word = &vocabulary[(next() % vocabulary.len() as u64) as usize];
text.extend_from_slice(word.as_bytes());
text.push(b' ');
}
text
}
#[test]
fn ghsa_rqc2_j9v2_j22v_ppmd8_cut_off_out_of_bounds() {
const ORDER: u32 = 16;
const MEMORY_SIZE: u32 = 1 << 20;
let text = pseudo_text(512 << 10);
let mut compressed = Vec::new();
{
let mut encoder =
Ppmd8Encoder::new(&mut compressed, ORDER, MEMORY_SIZE, RestoreMethod::Restart).unwrap();
encoder.write_all(&text).unwrap();
encoder.finish(true).unwrap();
}
let mut decoder = Ppmd8Decoder::new(
compressed.as_slice(),
ORDER,
MEMORY_SIZE,
RestoreMethod::CutOff,
)
.unwrap();
let mut decompressed = Vec::new();
let _ = decoder.read_to_end(&mut decompressed);
}
#[test]
fn ppmd8_cut_off_round_trip() {
const ORDER: u32 = 16;
const MEMORY_SIZE: u32 = 1 << 20;
let text = pseudo_text(4 * 1024 * 1024);
let mut compressed = Vec::new();
{
let mut encoder =
Ppmd8Encoder::new(&mut compressed, ORDER, MEMORY_SIZE, RestoreMethod::CutOff).unwrap();
encoder.write_all(&text).unwrap();
encoder.finish(true).unwrap();
}
let mut decompressed = Vec::new();
{
let mut decoder = Ppmd8Decoder::new(
compressed.as_slice(),
ORDER,
MEMORY_SIZE,
RestoreMethod::CutOff,
)
.unwrap();
decoder.read_to_end(&mut decompressed).unwrap();
}
assert_eq!(decompressed, text);
}
#[test]
fn ppmd8_minimum_memory_size_round_trip() {
let (sender, receiver) = std::sync::mpsc::channel();
std::thread::spawn(move || {
let text = pseudo_text(16 << 10);
for order in [2, 6, 16] {
for memory_size in PPMD8_MIN_MEM_SIZE..PPMD8_MIN_MEM_SIZE + 64 {
for restore_method in [
RestoreMethod::Restart,
RestoreMethod::CutOff,
RestoreMethod::Unsupported,
] {
let mut compressed = Vec::new();
{
let mut encoder =
Ppmd8Encoder::new(&mut compressed, order, memory_size, restore_method)
.unwrap();
encoder.write_all(&text).unwrap();
encoder.finish(true).unwrap();
}
let mut decompressed = Vec::new();
{
let mut decoder = Ppmd8Decoder::new(
compressed.as_slice(),
order,
memory_size,
restore_method,
)
.unwrap();
decoder.read_to_end(&mut decompressed).unwrap();
}
assert_eq!(decompressed, text);
}
}
}
sender.send(()).unwrap();
});
receiver
.recv_timeout(std::time::Duration::from_secs(120))
.expect("PPMd8 did not terminate");
}
#[test]
fn ppmd8_memory_size_below_minimum_is_rejected() {
for memory_size in [2048, PPMD8_MIN_MEM_SIZE - 1] {
assert!(Ppmd8Encoder::new(Vec::new(), 6, memory_size, RestoreMethod::CutOff).is_err());
assert!(
Ppmd8Decoder::new([0u8; 8].as_slice(), 6, memory_size, RestoreMethod::CutOff).is_err()
);
}
}