#![allow(dead_code, reason = "each test binary uses a different subset")]
use mzstd::{DecodeError, DecodeOperation, DecoderStatus, Decompressor};
use zstd_ffi::oracle::{self, Params};
pub struct Rng(u64);
impl Rng {
pub fn new(seed: u64) -> Self {
Self(seed.wrapping_mul(0x9E37_79B9_7F4A_7C15) | 1)
}
pub fn next_u64(&mut self) -> u64 {
self.0 ^= self.0 >> 12;
self.0 ^= self.0 << 25;
self.0 ^= self.0 >> 27;
self.0.wrapping_mul(0x2545_F491_4F6C_DD1D)
}
pub fn below(&mut self, bound: usize) -> usize {
(self.next_u64() % bound as u64) as usize
}
}
const WORDS: &[&str] = &[
"the",
"frame",
"block",
"window",
"literal",
"sequence",
"offset",
"match",
"length",
"huffman",
"table",
"state",
"decoder",
"stream",
"checksum",
"zstandard",
"entropy",
"of",
"and",
"a",
"to",
"in",
"is",
"with",
"compressed",
"data",
"bits",
"repeat",
];
pub fn text(len: usize, seed: u64) -> Vec<u8> {
let mut rng = Rng::new(seed);
let mut out = Vec::with_capacity(len + 16);
while out.len() < len {
let index = rng.below(WORDS.len()).min(rng.below(WORDS.len()));
out.extend_from_slice(WORDS[index].as_bytes());
out.push(match rng.below(16) {
0 => b'\n',
1 => b',',
2 => b'.',
_ => b' ',
});
}
out.truncate(len);
out
}
pub fn random(len: usize, seed: u64) -> Vec<u8> {
let mut rng = Rng::new(seed);
(0..len).map(|_| rng.next_u64() as u8).collect()
}
pub fn records(len: usize, seed: u64) -> Vec<u8> {
let mut rng = Rng::new(seed);
let mut out = Vec::with_capacity(len + 16);
let mut counter = 0u32;
while out.len() < len {
counter = counter.wrapping_add(1 + rng.below(3) as u32);
out.extend_from_slice(&counter.to_le_bytes());
out.extend_from_slice(&[0x5a, 0, 0, (rng.below(4)) as u8]);
out.extend_from_slice(&(rng.next_u64() & 0xff_ffff).to_le_bytes()[..3]);
out.push(b'|');
}
out.truncate(len);
out
}
pub fn runs(len: usize, seed: u64) -> Vec<u8> {
let mut rng = Rng::new(seed);
let mut out = Vec::with_capacity(len);
while out.len() < len {
let byte = [0u8, 0xff, b'a', 7][rng.below(4)];
let longest = if rng.below(8) == 0 { 50_000 } else { 40 };
let run = 1 + rng.below(longest);
out.resize((out.len() + run).min(len), byte);
}
out
}
pub fn mixed(len: usize, seed: u64) -> Vec<u8> {
let mut rng = Rng::new(seed);
let mut out = Vec::with_capacity(len);
while out.len() < len {
let segment = 1 + rng.below(20_000);
let seed = rng.next_u64();
let part = match rng.below(4) {
0 => text(segment, seed),
1 => random(segment.min(2_000), seed),
2 => records(segment, seed),
_ => runs(segment, seed),
};
out.extend_from_slice(&part);
}
out.truncate(len);
out
}
pub fn corpora(len: usize, seed: u64) -> Vec<(&'static str, Vec<u8>)> {
vec![
("text", text(len, seed)),
("random", random(len, seed)),
("records", records(len, seed)),
("runs", runs(len, seed)),
("mixed", mixed(len, seed)),
]
}
pub fn params(level: i32, checksum: bool, content_size: bool) -> Params {
Params::level(level)
.with(zstd_ffi::ZSTD_c_checksumFlag, i32::from(checksum))
.with(zstd_ffi::ZSTD_c_contentSizeFlag, i32::from(content_size))
}
pub fn compress(input: &[u8], params: &Params) -> Vec<u8> {
oracle::compress2(input, params).expect("the reference compresses")
}
pub fn reference_decompress(input: &[u8], capacity: usize) -> Result<Vec<u8>, String> {
oracle::decompress(input, capacity)
}
pub fn decode_streaming(
decoder: &mut Decompressor,
input: &[u8],
mut chunk: impl FnMut() -> usize,
out_chunk: usize,
) -> Result<Vec<u8>, DecodeError> {
let mut session = decoder.start();
let mut output = Vec::new();
let mut buffer = vec![0u8; out_chunk];
let mut position = 0;
let mut finishing = false;
loop {
let end = if finishing {
input.len()
} else {
(position + chunk().max(1)).min(input.len())
};
finishing |= end == input.len();
let operation = if finishing {
DecodeOperation::Finish
} else {
DecodeOperation::Process
};
let progress = session
.process(&input[position..end], &mut buffer, operation)
.map_err(mzstd::DecodeFailure::into_error)?;
position += progress.consumed;
output.extend_from_slice(&buffer[..progress.produced]);
match progress.status {
DecoderStatus::Finished => return Ok(output),
DecoderStatus::NeedsInput => assert_eq!(position, end, "NeedsInput consumed all"),
DecoderStatus::NeedsOutput => {}
}
}
}
pub fn decode_fixed(
decoder: &mut Decompressor,
input: &[u8],
in_chunk: usize,
out_chunk: usize,
) -> Result<Vec<u8>, DecodeError> {
decode_streaming(decoder, input, || in_chunk, out_chunk)
}
pub fn skippable(variant: u8, payload: &[u8]) -> Vec<u8> {
let mut frame = (0x184D_2A50u32 + u32::from(variant & 15))
.to_le_bytes()
.to_vec();
frame.extend_from_slice(&(payload.len() as u32).to_le_bytes());
frame.extend_from_slice(payload);
frame
}
pub fn golden_dir(name: &str) -> std::path::PathBuf {
std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("zstd-ffi/vendor/zstd/tests")
.join(name)
}