#[derive(Debug, Clone, PartialEq)]
pub enum TokenRef {
Literal(String),
Reference { offset: usize, length: usize },
}
#[derive(Debug, Clone)]
pub struct TokenCompressor {
pub window_size: usize,
pub lookahead_size: usize,
}
impl Default for TokenCompressor {
fn default() -> Self {
Self {
window_size: 256,
lookahead_size: 32,
}
}
}
impl TokenCompressor {
pub fn new(window_size: usize, lookahead_size: usize) -> Self {
Self { window_size, lookahead_size }
}
pub fn compress(&self, tokens: &[String]) -> Vec<TokenRef> {
const MIN_MATCH: usize = 3;
let mut output = Vec::new();
let mut pos = 0;
while pos < tokens.len() {
let win_start = pos.saturating_sub(self.window_size);
let lookahead_end = (pos + self.lookahead_size).min(tokens.len());
let mut best_offset = 0usize;
let mut best_length = 0usize;
for win_pos in win_start..pos {
let mut length = 0;
while pos + length < lookahead_end
&& tokens[win_pos + length] == tokens[pos + length]
{
length += 1;
if win_pos + length >= pos && length >= pos - win_pos {
break;
}
}
if length > best_length {
best_length = length;
best_offset = pos - win_pos;
}
}
if best_length >= MIN_MATCH {
output.push(TokenRef::Reference {
offset: best_offset,
length: best_length,
});
pos += best_length;
} else {
output.push(TokenRef::Literal(tokens[pos].clone()));
pos += 1;
}
}
output
}
pub fn decompress(&self, refs: &[TokenRef]) -> Vec<String> {
let mut output: Vec<String> = Vec::new();
for token_ref in refs {
match token_ref {
TokenRef::Literal(s) => output.push(s.clone()),
TokenRef::Reference { offset, length } => {
let start = output.len().saturating_sub(*offset);
for i in 0..*length {
let idx = start + i;
if idx < output.len() {
let s = output[idx].clone();
output.push(s);
}
}
}
}
}
output
}
}
pub fn compress(tokens: &[String]) -> Vec<TokenRef> {
TokenCompressor::default().compress(tokens)
}
pub fn decompress(refs: &[TokenRef]) -> Vec<String> {
TokenCompressor::default().decompress(refs)
}
#[derive(Debug, Clone)]
pub struct CompressionStats {
pub original_count: usize,
pub compressed_count: usize,
pub ratio: f64,
pub literal_count: usize,
pub reference_count: usize,
}
pub fn stats(original: &[String], compressed: &[TokenRef]) -> CompressionStats {
let original_count = original.len();
let compressed_count = compressed.len();
let literal_count = compressed.iter().filter(|r| matches!(r, TokenRef::Literal(_))).count();
let reference_count = compressed_count - literal_count;
let ratio = if compressed_count == 0 {
1.0
} else {
original_count as f64 / compressed_count as f64
};
CompressionStats {
original_count,
compressed_count,
ratio,
literal_count,
reference_count,
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct DeltaPatch {
pub insertions: Vec<(usize, String)>,
pub deletions: Vec<usize>,
}
#[derive(Debug, Default)]
pub struct DeltaEncoder;
impl DeltaEncoder {
pub fn new() -> Self {
Self
}
pub fn encode_delta(&self, prev: &[String], curr: &[String]) -> DeltaPatch {
let m = prev.len();
let n = curr.len();
let mut dp = vec![vec![0usize; n + 1]; m + 1];
for i in 1..=m {
for j in 1..=n {
if prev[i - 1] == curr[j - 1] {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = dp[i - 1][j].max(dp[i][j - 1]);
}
}
}
let mut in_prev_lcs = vec![false; m];
let mut in_curr_lcs = vec![false; n];
let (mut i, mut j) = (m, n);
while i > 0 && j > 0 {
if prev[i - 1] == curr[j - 1] {
in_prev_lcs[i - 1] = true;
in_curr_lcs[j - 1] = true;
i -= 1;
j -= 1;
} else if dp[i - 1][j] >= dp[i][j - 1] {
i -= 1;
} else {
j -= 1;
}
}
let deletions: Vec<usize> = in_prev_lcs
.iter()
.enumerate()
.filter(|(_, &in_lcs)| !in_lcs)
.map(|(idx, _)| idx)
.collect();
let mut insertions = Vec::new();
let mut result_pos = 0usize;
let mut prev_lcs_iter = in_prev_lcs.iter().filter(|&&x| x);
for (j_idx, &in_lcs) in in_curr_lcs.iter().enumerate() {
if in_lcs {
let _ = prev_lcs_iter.next();
result_pos += 1;
} else {
insertions.push((result_pos, curr[j_idx].clone()));
result_pos += 1;
}
}
DeltaPatch { insertions, deletions }
}
pub fn apply_patch(&self, base: &[String], patch: &DeltaPatch) -> Vec<String> {
let del_set: std::collections::HashSet<usize> = patch.deletions.iter().cloned().collect();
let mut result: Vec<String> = base
.iter()
.enumerate()
.filter(|(i, _)| !del_set.contains(i))
.map(|(_, s)| s.clone())
.collect();
for (pos, token) in &patch.insertions {
let idx = (*pos).min(result.len());
result.insert(idx, token.clone());
}
result
}
}
pub fn encode_delta(prev: &[String], curr: &[String]) -> DeltaPatch {
DeltaEncoder::new().encode_delta(prev, curr)
}
pub fn apply_patch(base: &[String], patch: &DeltaPatch) -> Vec<String> {
DeltaEncoder::new().apply_patch(base, patch)
}
#[cfg(test)]
mod tests {
use super::*;
fn s(v: &[&str]) -> Vec<String> {
v.iter().map(|&x| x.to_string()).collect()
}
#[test]
fn roundtrip_empty() {
let tokens: Vec<String> = vec![];
let compressed = compress(&tokens);
let decompressed = decompress(&compressed);
assert_eq!(tokens, decompressed);
}
#[test]
fn roundtrip_no_repetition() {
let tokens = s(&["the", "quick", "brown", "fox", "jumps"]);
let compressed = compress(&tokens);
let decompressed = decompress(&compressed);
assert_eq!(tokens, decompressed);
}
#[test]
fn roundtrip_repetitive() {
let pattern = s(&["hello", "world", "foo"]);
let tokens: Vec<String> = pattern.iter().cloned().cycle().take(30).collect();
let compressed = compress(&tokens);
let decompressed = decompress(&compressed);
assert_eq!(tokens, decompressed);
}
#[test]
fn compression_ratio_repetitive() {
let pattern = s(&["alpha", "beta", "gamma"]);
let tokens: Vec<String> = pattern.iter().cloned().cycle().take(60).collect();
let compressed = compress(&tokens);
let st = stats(&tokens, &compressed);
assert!(
st.ratio > 1.5,
"expected ratio > 1.5, got {}",
st.ratio
);
}
#[test]
fn delta_roundtrip_identical() {
let base = s(&["a", "b", "c"]);
let patch = encode_delta(&base, &base);
let result = apply_patch(&base, &patch);
assert_eq!(result, base);
}
#[test]
fn delta_roundtrip_insertions() {
let prev = s(&["a", "b", "c"]);
let curr = s(&["a", "x", "b", "c"]);
let patch = encode_delta(&prev, &curr);
let result = apply_patch(&prev, &patch);
assert_eq!(result, curr);
}
#[test]
fn delta_roundtrip_deletions() {
let prev = s(&["a", "b", "c", "d"]);
let curr = s(&["a", "c", "d"]);
let patch = encode_delta(&prev, &curr);
let result = apply_patch(&prev, &patch);
assert_eq!(result, curr);
}
#[test]
fn stats_counts() {
let tokens = s(&["a", "b", "a", "b", "a", "b", "a", "b"]);
let compressed = compress(&tokens);
let st = stats(&tokens, &compressed);
assert_eq!(st.original_count, tokens.len());
assert_eq!(st.compressed_count, compressed.len());
assert_eq!(st.literal_count + st.reference_count, st.compressed_count);
}
}