#![forbid(unsafe_code)]
use crate::core::candidate::{CandidateContext, Encoder};
use crate::core::cost::Policy;
use crate::core::limits::Limits;
use crate::core::materialize::materialize_to_vec;
use crate::core::representation::{Representation, Residual};
use crate::entropy::palette::PaletteEncoder;
use crate::entropy::periodic::PeriodicEncoder;
use crate::entropy::permutation::PermutationEncoder;
use crate::entropy::residual::BaseResidualEncoder;
use crate::entropy::sparse::SparseEncoder;
use crate::entropy::universe::UniverseEncoder;
use crate::rans::residual::{RansEncoder, RansResidualEncoder};
use crate::tests::helpers::MemResolver;
fn ctx<'a>(
input: &[u8],
limits: &'a Limits,
policy: &'a Policy,
bases: &'a [crate::core::candidate::BaseChunk],
) -> CandidateContext<'a> {
CandidateContext {
limits,
policy,
content_id: crate::core::extent::ChunkId::of(input),
bases,
dedup: None,
}
}
fn assert_all_candidates_roundtrip(input: &[u8], bases: &[crate::core::candidate::BaseChunk]) {
let limits = Limits::default();
let policy = Policy::default();
let cctx = ctx(input, &limits, &policy, bases);
let encoders: Vec<Box<dyn Encoder>> = vec![
Box::new(SparseEncoder),
Box::new(PaletteEncoder),
Box::new(PermutationEncoder),
Box::new(PeriodicEncoder),
Box::new(BaseResidualEncoder),
Box::new(UniverseEncoder),
Box::new(RansEncoder),
Box::new(RansResidualEncoder),
];
let mut resolver = MemResolver::empty();
for base in bases {
resolver.put_object(base.id, base.bytes.clone());
resolver.put_chunk(base.id, RepresentationRawForTest(&base.bytes).to_rep());
}
let mut checked = 0usize;
for enc in encoders.iter() {
for cand in enc.encode(input, &cctx) {
let mut local = resolver.clone();
for o in &cand.objects {
local.put_object(o.id, o.payload.clone());
}
let out = materialize_to_vec(&cand.representation, &local, &limits)
.unwrap_or_else(|e| panic!("{}: materialize failed: {e:?}", enc.name()));
assert_eq!(
out,
input,
"{}: round trip mismatch (len {} vs {})",
enc.name(),
out.len(),
input.len()
);
let persisted = cand.cost.persisted_bytes();
let expected = cand.representation.encoded_size()
+ cand.cost.model_bytes
+ cand.cost.object_payload_bytes
+ cand.cost.integrity_bytes;
assert_eq!(
persisted,
expected,
"{}: cost accounting broken: persisted {persisted} != {expected}",
enc.name()
);
checked += 1;
}
}
let _ = checked;
}
struct RepresentationRawForTest<'a>(&'a [u8]);
impl RepresentationRawForTest<'_> {
fn to_rep(&self) -> Representation {
Representation::Raw {
obj: crate::core::extent::ChunkId::of(self.0),
len: self.0.len() as u64,
}
}
}
#[test]
fn sparse_roundtrip() {
let mut input = vec![0u8; 8192];
for &p in &[7u32, 4096, 8191] {
input[p as usize] = 0x5A;
}
assert_all_candidates_roundtrip(&input, &[]);
}
#[test]
fn palette_roundtrip() {
let input: Vec<u8> = (0..64u32)
.map(|i| match i % 6 {
0..=2 => 0x11,
3..=4 => 0x22,
_ => 0x33,
})
.collect();
assert_all_candidates_roundtrip(&input, &[]);
}
#[test]
fn periodic_roundtrip() {
let mut input = Vec::new();
for _ in 0..200 {
input.extend_from_slice(b"entropyfs");
}
input.extend_from_slice(b"ent"); assert_all_candidates_roundtrip(&input, &[]);
}
#[test]
fn fill_roundtrip() {
assert_all_candidates_roundtrip(&[0x42u8; 4096], &[]);
assert_all_candidates_roundtrip(&[0u8; 4096], &[]);
}
#[test]
fn base_residual_roundtrip() {
let base = vec![0x10u8; 4096];
let mut target = base.clone();
for slot in target.iter_mut().take(200).skip(100) {
*slot = 0xFF;
}
target[4000] = 0x01;
let base_id = crate::core::extent::ChunkId::of(&base);
let bases = vec![crate::core::candidate::BaseChunk {
id: base_id,
bytes: base,
depth: 0,
}];
assert_all_candidates_roundtrip(&target, &bases);
}
#[test]
fn rans_roundtrip() {
let input: Vec<u8> = (0..65536u32)
.map(|i| ((i * 5 + i / 64) % 97) as u8)
.collect();
assert_all_candidates_roundtrip(&input, &[]);
}
#[test]
fn entropy_ref_exact_match() {
let seed = [0x42u8; 16];
let input = crate::entropy::universe::UniformXofV1::generate(seed, 0, 4096);
let rep = Representation::EntropyRef {
universe: crate::core::representation::UniverseId::UniformXofV1,
seed,
coordinate: 0,
transform: crate::core::representation::TransformId::Identity,
residual: Residual::XorSparse {
len: 4096,
edits: Vec::new(),
},
len: 4096,
};
rep.validate(&Limits::default()).unwrap();
let resolver = MemResolver::empty();
let out = materialize_to_vec(&rep, &resolver, &Limits::default()).unwrap();
assert_eq!(out, input);
let split = crate::core::cost::ByteSplit {
seed_state: 24,
..Default::default()
};
let cost = crate::core::cost::estimate(&rep, &split, 0);
assert!(cost.persisted_bytes() < 100);
}
#[test]
fn entropy_ref_random_data_loses_to_raw() {
let input: Vec<u8> = (0..4096u32).map(|i| ((i * 31 + 7) % 251) as u8).collect();
let limits = Limits::default();
let policy = Policy::default();
let cctx = ctx(&input, &limits, &policy, &[]);
let enc = UniverseEncoder;
let cands = enc.encode(&input, &cctx);
for c in &cands {
let raw = crate::core::candidate::raw_candidate(&input, cctx.content_id, &limits).unwrap();
assert!(c.total(&policy) > raw.total(&policy));
}
}
#[test]
fn all_families_agree_on_content_id() {
let input: Vec<u8> = (0..4096u32).map(|i| ((i * 3) % 29) as u8).collect();
let limits = Limits::default();
let policy = Policy::default();
let cctx = ctx(&input, &limits, &policy, &[]);
for cand in [
SparseEncoder.encode(&input, &cctx),
PaletteEncoder.encode(&input, &cctx),
PeriodicEncoder.encode(&input, &cctx),
RansEncoder.encode(&input, &cctx),
UniverseEncoder.encode(&input, &cctx),
]
.into_iter()
.flatten()
{
assert_eq!(cand.content_id, crate::core::extent::ChunkId::of(&input));
}
}
#[test]
fn inline_and_raw_pipeline_basics() {
let limits = Limits::default();
let policy = Policy::default();
let small: Vec<u8> = (0..200u32).map(|i| (i % 40) as u8).collect();
let cid = crate::core::extent::ChunkId::of(&small);
let inl = crate::core::candidate::inline_candidate(&small, cid, &limits).unwrap();
let raw = crate::core::candidate::raw_candidate(&small, cid, &limits).unwrap();
assert!(inl.total(&policy) < raw.total(&policy));
let cands = [raw, inl];
let best = crate::core::candidate::pick_cheapest(&cands, &policy).unwrap();
assert!(matches!(best.representation, Representation::Inline { .. }));
}
#[test]
fn candidate_validation_rejects_wrong_bytes() {
let limits = Limits::default();
let a = vec![1u8; 256];
let b = vec![2u8; 256];
let cand =
crate::core::candidate::raw_candidate(&a, crate::core::extent::ChunkId::of(&a), &limits)
.unwrap();
let resolver = MemResolver::empty();
let res = crate::core::candidate::validate_candidate(&cand, &b, &resolver, &limits);
assert!(res.is_err());
}