#![forbid(unsafe_code)]
use crate::core::candidate::{Candidate, CandidateContext, Encoder};
use crate::core::cost::ByteSplit;
use crate::core::representation::{Edit, RangeChange, Representation, Residual};
#[derive(Debug, Default)]
pub struct BaseResidualEncoder;
impl Encoder for BaseResidualEncoder {
fn name(&self) -> &'static str {
"BASE_RESIDUAL"
}
fn encode(&self, input: &[u8], ctx: &CandidateContext<'_>) -> Vec<Candidate> {
if ctx.bases.is_empty() {
return Vec::new();
}
let mut out = Vec::new();
for base in ctx.bases {
if base.depth >= ctx.limits.max_reference_depth {
continue;
}
if base.bytes.len() != input.len() {
continue;
}
for residual in derive_residuals(input, &base.bytes, ctx.limits.max_fanout) {
if matches!(&residual, Residual::XorSparse { edits, .. } if edits.is_empty()) {
continue;
}
let rep = Representation::BaseResidual {
base: base.id,
base_len: base.bytes.len() as u64,
residual: residual.clone(),
len: input.len() as u64,
};
if rep.validate(ctx.limits).is_err() {
continue;
}
let split = ByteSplit {
residual: residual_data_bytes(&residual),
reference: 32,
..Default::default()
};
let mut cost = crate::core::cost::estimate(&rep, &split, 0);
cost.depth = cost.depth.saturating_add(base.depth);
out.push(Candidate {
representation: rep,
objects: Vec::new(),
cost,
content_id: ctx.content_id,
});
}
}
out
}
}
pub fn residual_data_bytes(residual: &Residual) -> u64 {
match residual {
Residual::XorSparse { edits, .. } => 5 * edits.len() as u64,
Residual::RangeReplace {
changes, literals, ..
} => 8 * changes.len() as u64 + literals.len() as u64,
Residual::RansCoded { .. } => 0,
}
}
pub fn derive_residuals(target: &[u8], base: &[u8], max_fanout: u32) -> Vec<Residual> {
if target.len() != base.len() {
return Vec::new();
}
let n = target.len();
if n == 0 {
return Vec::new();
}
let mut edits: Vec<Edit> = Vec::new();
for i in 0..n {
if target[i] != base[i] {
edits.push(Edit {
pos: i as u32,
val: target[i] ^ base[i],
});
}
}
let mut out: Vec<Residual> = Vec::new();
if edits.len() as u64 <= max_fanout as u64 {
out.push(Residual::XorSparse {
len: n as u64,
edits: edits.clone(),
});
}
let mut changes: Vec<RangeChange> = Vec::new();
let mut literals: Vec<u8> = Vec::new();
let mut i = 0usize;
while i < n {
if target[i] != base[i] {
let start = i as u32;
let mut end = i;
while end < n && target[end] != base[end] {
literals.push(target[end]);
end += 1;
}
changes.push(RangeChange {
start,
end: end as u32,
});
i = end;
} else {
i += 1;
}
}
if changes.len() as u64 <= max_fanout as u64 && !changes.is_empty() {
out.push(Residual::RangeReplace {
len: n as u64,
changes,
literals,
});
}
out.sort_by_key(|r| r.encoded_size());
out
}
pub fn diff_summary(target: &[u8], base: &[u8]) -> (usize, usize) {
if target.len() != base.len() {
return (usize::MAX, usize::MAX);
}
let mut positions = 0usize;
let mut runs = 0usize;
let mut in_run = false;
for i in 0..target.len() {
if target[i] != base[i] {
positions += 1;
if !in_run {
runs += 1;
in_run = true;
}
} else {
in_run = false;
}
}
(positions, runs)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn identical_targets() {
let a = b"hello world hello world";
let r = derive_residuals(a, a, 4096);
assert_eq!(r.len(), 1);
match &r[0] {
Residual::XorSparse { edits, .. } => assert!(edits.is_empty()),
other => panic!("expected xor, got {other:?}"),
}
}
#[test]
fn sparse_diffs() {
let base = vec![0u8; 100];
let mut target = base.clone();
target[10] = 1;
target[50] = 2;
let r = derive_residuals(&target, &base, 4096);
assert!(!r.is_empty());
match &r[0] {
Residual::XorSparse { edits, .. } => {
assert_eq!(edits.len(), 2);
assert_eq!(edits[0].pos, 10);
assert_eq!(edits[0].val, 1);
}
other => panic!("expected xor, got {other:?}"),
}
}
#[test]
fn dense_run_uses_range() {
let base = vec![0u8; 64];
let mut target = base.clone();
for slot in target.iter_mut().take(48).skip(16) {
*slot = 0xFF;
}
let r = derive_residuals(&target, &base, 4096);
assert!(r.iter().any(|x| matches!(x, Residual::RangeReplace { .. })));
assert!(matches!(r[0], Residual::RangeReplace { .. }));
}
#[test]
fn fanout_cap() {
let base = vec![0u8; 32];
let target: Vec<u8> = (0..32).map(|i| if i % 2 == 0 { 1 } else { 0 }).collect();
let r = derive_residuals(&target, &base, 4);
assert!(r.is_empty());
let target2 = vec![1u8; 32];
let r2 = derive_residuals(&target2, &base, 4);
assert!(!r2.is_empty());
assert!(matches!(r2[0], Residual::RangeReplace { .. }));
}
}