use serde::{Deserialize, Serialize};
pub const DELTA_CHAIN_CAP: usize = 32;
pub const DELTA_RATIO_THRESHOLD: f64 = 0.5;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct StageDelta {
pub base_stage_id: String,
pub chain_length: usize,
pub common_prefix: usize,
pub common_suffix: usize,
pub middle_hex: String,
}
pub fn splice(base: &[u8], new: &[u8]) -> (usize, usize, Vec<u8>) {
let prefix_len = common_prefix_len(base, new);
let max_suffix = std::cmp::min(
base.len().saturating_sub(prefix_len),
new.len().saturating_sub(prefix_len),
);
let suffix_len = common_suffix_len(
&base[base.len() - max_suffix..],
&new[new.len() - max_suffix..],
);
let middle = new[prefix_len..new.len() - suffix_len].to_vec();
(prefix_len, suffix_len, middle)
}
pub fn apply(base: &[u8], delta: &StageDelta) -> Result<Vec<u8>, DeltaError> {
let middle = hex::decode(&delta.middle_hex)
.map_err(|e| DeltaError::InvalidHex(e.to_string()))?;
if delta.common_prefix + delta.common_suffix > base.len() {
return Err(DeltaError::OverlappingSplice {
base_len: base.len(),
prefix: delta.common_prefix,
suffix: delta.common_suffix,
});
}
let prefix = &base[..delta.common_prefix];
let suffix_start = base.len() - delta.common_suffix;
let suffix = &base[suffix_start..];
let mut out = Vec::with_capacity(prefix.len() + middle.len() + suffix.len());
out.extend_from_slice(prefix);
out.extend(middle);
out.extend_from_slice(suffix);
Ok(out)
}
pub fn is_worth_encoding(middle_len: usize, new_len: usize, chain_length: usize) -> bool {
if chain_length > DELTA_CHAIN_CAP {
return false;
}
if new_len == 0 {
return false;
}
(middle_len as f64) / (new_len as f64) < DELTA_RATIO_THRESHOLD
}
fn common_prefix_len(a: &[u8], b: &[u8]) -> usize {
a.iter().zip(b.iter()).take_while(|(x, y)| x == y).count()
}
fn common_suffix_len(a: &[u8], b: &[u8]) -> usize {
a.iter()
.rev()
.zip(b.iter().rev())
.take_while(|(x, y)| x == y)
.count()
}
#[derive(Debug, thiserror::Error)]
pub enum DeltaError {
#[error("delta middle is not valid hex: {0}")]
InvalidHex(String),
#[error("delta splice overflows base: base_len={base_len}, prefix={prefix}, suffix={suffix}")]
OverlappingSplice {
base_len: usize,
prefix: usize,
suffix: usize,
},
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn splice_then_apply_round_trips() {
let base = b"hello, the quick brown fox jumps over the lazy dog";
let new = b"hello, the quick green fox jumps over the lazy dog";
let (p, s, m) = splice(base, new);
let delta = StageDelta {
base_stage_id: "x".into(),
chain_length: 1,
common_prefix: p,
common_suffix: s,
middle_hex: hex::encode(&m),
};
let reconstructed = apply(base, &delta).unwrap();
assert_eq!(reconstructed, new);
}
#[test]
fn identical_bytes_yield_empty_middle() {
let base = b"unchanged";
let new = b"unchanged";
let (p, s, m) = splice(base, new);
assert_eq!(p, 9);
assert_eq!(s, 0);
assert!(m.is_empty(), "no middle when bytes are identical");
let delta = StageDelta {
base_stage_id: "x".into(),
chain_length: 1,
common_prefix: p,
common_suffix: s,
middle_hex: hex::encode(&m),
};
assert_eq!(apply(base, &delta).unwrap(), base);
}
#[test]
fn pure_insertion_is_pure_middle() {
let base = b"abXY";
let new = b"abZZZZXY";
let (p, s, m) = splice(base, new);
assert_eq!(p, 2);
assert_eq!(s, 2);
assert_eq!(m, b"ZZZZ");
}
#[test]
fn pure_deletion_yields_empty_middle() {
let base = b"abZZZZXY";
let new = b"abXY";
let (p, s, m) = splice(base, new);
assert_eq!(p, 2);
assert_eq!(s, 2);
assert!(m.is_empty());
let delta = StageDelta {
base_stage_id: "x".into(),
chain_length: 1,
common_prefix: p,
common_suffix: s,
middle_hex: hex::encode(&m),
};
assert_eq!(apply(base, &delta).unwrap(), new);
}
#[test]
fn is_worth_encoding_respects_threshold() {
assert!(is_worth_encoding(30, 100, 1));
assert!(!is_worth_encoding(60, 100, 1));
assert!(!is_worth_encoding(1, 100, DELTA_CHAIN_CAP + 1));
}
#[test]
fn apply_refuses_overlapping_splice() {
let base = b"short";
let delta = StageDelta {
base_stage_id: "x".into(),
chain_length: 1,
common_prefix: 4,
common_suffix: 4, middle_hex: String::new(),
};
assert!(apply(base, &delta).is_err());
}
}