pub mod candidates;
pub mod court;
#[cfg(feature = "dsfb-search")]
pub mod governor;
use crate::accounting::CostBreakdown;
use crate::encode::candidates::{Candidate, CandidateKind};
use crate::error::{Error, Result};
use crate::integrity::{sha256, to_hex};
use crate::limits::Limits;
#[derive(Debug, Clone)]
pub struct EncodeReport {
pub kind: CandidateKind,
pub source_len: u64,
pub encoded_len: u64,
pub cost: CostBreakdown,
pub sha256_hex: String,
pub candidates_evaluated: u32,
pub graph_ops: usize,
}
impl EncodeReport {
pub fn compression_ratio(&self) -> f64 {
if self.encoded_len == 0 {
return 0.0;
}
self.source_len as f64 / self.encoded_len as f64
}
}
pub fn encode(input: &[u8], limits: Limits) -> Result<(Vec<u8>, EncodeReport)> {
encode_with(input, limits, None)
}
pub fn encode_with(
input: &[u8],
limits: Limits,
force: Option<CandidateKind>,
) -> Result<(Vec<u8>, EncodeReport)> {
encode_impl(input, limits, force, false)
}
#[cfg(feature = "field")]
pub(crate) fn encode_with_observation_index(
input: &[u8],
limits: Limits,
kind: CandidateKind,
) -> Result<(Vec<u8>, EncodeReport)> {
encode_impl(input, limits, Some(kind), true)
}
fn encode_impl(
input: &[u8],
limits: Limits,
force: Option<CandidateKind>,
enrich_index: bool,
) -> Result<(Vec<u8>, EncodeReport)> {
let mut court = court::Court::new();
if let Some(kind) = force {
let candidate = candidates::propose_forced(input, limits, kind)?.ok_or_else(|| {
Error::usage(format!("candidate {kind:?} is not proposed for this input"))
})?;
if enrich_index {
let enriched = Candidate {
kind,
descriptor: candidate.descriptor.with_observation_index(limits),
};
if court.offer(input, enriched, limits).is_err() {
let plain = candidates::propose_forced(input, limits, kind)?.ok_or_else(|| {
Error::usage(format!("candidate {kind:?} is not proposed for this input"))
})?;
court = court::Court::new();
court.offer(input, plain, limits)?;
}
} else {
court.offer(input, candidate, limits)?;
}
} else {
candidates::propose_each(input, limits, |c| court.offer(input, c, limits))?;
}
let result = court.finish()?;
let report = EncodeReport {
kind: result.kind,
source_len: input.len() as u64,
encoded_len: result.bytes.len() as u64,
cost: result.cost,
sha256_hex: to_hex(&sha256(input)),
candidates_evaluated: result.candidates_evaluated,
graph_ops: result.graph_ops,
};
Ok((result.bytes, report))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn encode_decode_identity() {
let input: Vec<u8> = (0..=255u8).cycle().take(10_000).collect();
let (bytes, report) = encode(&input, Limits::DEFAULT).unwrap();
assert_eq!(report.source_len, input.len() as u64);
assert_eq!(report.encoded_len, bytes.len() as u64);
let (out, _) = crate::materialize::decode_to_bytes(&bytes, Limits::DEFAULT).unwrap();
assert_eq!(out, input);
}
#[test]
fn deterministic_output() {
let input = b"determinism check".to_vec();
let (a, _) = encode(&input, Limits::DEFAULT).unwrap();
let (b, _) = encode(&input, Limits::DEFAULT).unwrap();
assert_eq!(a, b);
}
#[test]
fn encode_with_none_matches_encode() {
let input = b"the court must not care how it was invoked".repeat(40);
let (auto, auto_report) = encode(&input, Limits::DEFAULT).unwrap();
let (explicit, explicit_report) = encode_with(&input, Limits::DEFAULT, None).unwrap();
assert_eq!(auto, explicit, "None must equal the unforced court");
assert_eq!(auto_report.kind, explicit_report.kind);
assert_eq!(auto_report.encoded_len, explicit_report.encoded_len);
assert_eq!(
auto_report.candidates_evaluated,
explicit_report.candidates_evaluated
);
}
#[cfg(feature = "field")]
#[test]
fn forced_encode_omits_index_and_enrichment_is_byte_identical() {
let input = b"the enriched authority must be byte-identical".repeat(64);
let (plain, _) = encode_with(&input, Limits::DEFAULT, Some(CandidateKind::Raw)).unwrap();
let parsed = crate::container::Descriptor::parse(&plain, Limits::DEFAULT).unwrap();
assert!(parsed.descriptor.observation_index.is_none());
let after = crate::field::ingest::with_observation_index(&plain, Limits::DEFAULT).unwrap();
let (before, report) =
encode_with_observation_index(&input, Limits::DEFAULT, CandidateKind::Raw).unwrap();
assert_eq!(
before, after,
"the one-pass enriched authority must equal the two-pass bytes"
);
assert_eq!(report.candidates_evaluated, 1);
assert_eq!(report.kind, CandidateKind::Raw);
let enriched = crate::container::Descriptor::parse(&before, Limits::DEFAULT).unwrap();
assert!(enriched.descriptor.observation_index.is_some());
let (out, _) = crate::materialize::decode_to_bytes(&before, Limits::DEFAULT).unwrap();
assert_eq!(out, input);
}
#[test]
fn encode_with_force_raw_is_exact() {
let input = vec![0u8; 8192];
let (_, auto_report) = encode(&input, Limits::DEFAULT).unwrap();
assert_ne!(auto_report.kind, CandidateKind::Raw);
let (bytes, report) =
encode_with(&input, Limits::DEFAULT, Some(CandidateKind::Raw)).unwrap();
assert_eq!(report.kind, CandidateKind::Raw);
assert_eq!(report.candidates_evaluated, 1);
let (out, _) = crate::materialize::decode_to_bytes(&bytes, Limits::DEFAULT).unwrap();
assert_eq!(out, input);
}
#[cfg(feature = "rans")]
#[test]
fn encode_with_force_byte_rans_is_exact() {
let input = b"The quick brown fox jumps over the lazy dog. ".repeat(800);
let (bytes, report) =
encode_with(&input, Limits::DEFAULT, Some(CandidateKind::ByteRans)).unwrap();
assert_eq!(report.kind, CandidateKind::ByteRans);
assert_eq!(report.candidates_evaluated, 1);
let (out, _) = crate::materialize::decode_to_bytes(&bytes, Limits::DEFAULT).unwrap();
assert_eq!(out, input);
}
#[test]
fn encode_with_unproposed_kind_errors() {
let input = b"definitely not a pdf, just text".to_vec();
let err = encode_with(&input, Limits::DEFAULT, Some(CandidateKind::PdfChannels))
.expect_err("PDF_CHANNELS is not proposed for a non-PDF");
assert_eq!(err.class(), crate::error::ErrorClass::Usage);
assert!(
err.message().contains("is not proposed"),
"unexpected message: {}",
err.message()
);
}
}