#![allow(non_snake_case)]
use std::cmp::Ordering;
use std::io;
use std::io::Write;
use crate::bsdf2_writer::{Bsdf2Writer, CompressionAlgorithm, ControlEntry};
pub fn diff<T: Write>(old: &[u8], new: &[u8], writer: &mut T) -> io::Result<()> {
bsdiff_internal(old, new, writer)
}
pub fn diff_bsdiff40<T: Write>(old: &[u8], new: &[u8], writer: &mut T) -> io::Result<()> {
let mut patch_writer = Bsdf2Writer::new_legacy();
bsdiff_with_writer(old, new, &mut patch_writer)?;
patch_writer.close(writer)
}
pub fn diff_bsdf2<T: Write>(
old: &[u8],
new: &[u8],
writer: &mut T,
ctrl_alg: CompressionAlgorithm,
diff_alg: CompressionAlgorithm,
extra_alg: CompressionAlgorithm,
) -> io::Result<()> {
let mut patch_writer = Bsdf2Writer::new(ctrl_alg, diff_alg, extra_alg);
bsdiff_with_writer(old, new, &mut patch_writer)?;
patch_writer.close(writer)
}
pub fn diff_bsdf2_uniform<T: Write>(
old: &[u8],
new: &[u8],
writer: &mut T,
alg: CompressionAlgorithm,
) -> io::Result<()> {
diff_bsdf2(old, new, writer, alg, alg, alg)
}
#[inline(always)]
fn usz(i: isize) -> usize {
debug_assert!(i >= 0);
i as usize
}
struct SplitParams {
start: usize,
len: usize,
}
#[inline]
fn split_internal(
I: &mut [isize],
V: &mut [isize],
start: usize,
len: usize,
h: usize,
) -> Option<SplitParams> {
if len < 16 {
let mut k = start;
while k < start + len {
let mut j = 1;
let mut x = V[usz(I[k] + h as isize)];
let mut i = 1;
while k + i < start + len {
let v = V[usz(I[k + i] + h as isize)];
if v < x {
x = v;
j = 0;
}
if v == x {
I.swap(k + j, k + i);
j += 1;
}
i += 1;
}
let kj = (k + j) as isize;
for &Ii in &I[k..k + j] {
V[usz(Ii)] = kj - 1;
}
if j == 1 {
I[k] = -1;
}
k += j;
}
None
} else {
let x = V[usz(I[start + len / 2] + h as isize)];
let mut jj = 0;
let mut kk = 0;
for &Ii in &I[start..start + len] {
let v = V[usz(Ii + h as isize)];
if v < x {
jj += 1;
}
if v == x {
kk += 1;
}
}
let jj = jj + start;
let kk = kk + jj;
let mut j = 0;
let mut k = 0;
let mut i = start;
while i < jj {
match V[usz(I[i] + h as isize)].cmp(&x) {
Ordering::Less => i += 1,
Ordering::Equal => {
I.swap(i, jj + j);
j += 1;
}
Ordering::Greater => {
I.swap(i, kk + k);
k += 1;
}
}
}
while jj + j < kk {
if V[usz(I[jj + j] + h as isize)] == x {
j += 1;
} else {
I.swap(jj + j, kk + k);
k += 1;
}
}
if jj > start {
split(I, V, start, jj - start, h);
}
let kk_minus_1 = (kk - 1) as isize;
for &Ii in &I[jj..kk] {
V[usz(Ii)] = kk_minus_1;
}
if jj == kk - 1 {
I[jj] = -1;
}
if start + len > kk {
Some(SplitParams {
start: kk,
len: start + len - kk,
})
} else {
None
}
}
}
fn split(I: &mut [isize], V: &mut [isize], start: usize, len: usize, h: usize) {
let mut ret = Some(SplitParams { start, len });
while let Some(params) = ret {
ret = split_internal(I, V, params.start, params.len, h);
}
}
fn qsufsort(I: &mut [isize], V: &mut [isize], old: &[u8]) {
let mut buckets: [isize; 256] = [0; 256];
for &o in old {
buckets[o as usize] += 1;
}
for i in 1..256 {
buckets[i] += buckets[i - 1];
}
for i in (1..256).rev() {
buckets[i] = buckets[i - 1];
}
buckets[0] = 0;
for (i, &old_byte) in old.iter().enumerate() {
buckets[old_byte as usize] += 1;
I[usz(buckets[old_byte as usize])] = i as isize;
}
I[0] = old.len() as isize;
for (i, &old_byte) in old.iter().enumerate() {
V[i] = buckets[old_byte as usize];
}
V[old.len()] = 0;
for i in 1..256 {
if buckets[i] == buckets[i - 1] + 1 {
I[usz(buckets[i])] = -1;
}
}
I[0] = -1;
let mut h = 1;
while I[0] != -(old.len() as isize + 1) {
let mut len = 0;
let mut i = 0;
while i < old.len() as isize + 1 {
if I[usz(i)] < 0 {
len -= I[usz(i)];
i = i - I[usz(i)];
} else {
if len != 0 {
I[usz(i - len)] = -len;
}
len = V[usz(I[usz(i)])] + 1 - i;
split(I, V, usz(i), usz(len), h);
i += len;
len = 0;
}
}
if len != 0 {
I[usz(i - len)] = -len;
}
h += h;
}
for (i, &v) in V[0..=old.len()].iter().enumerate() {
I[usz(v)] = i as isize;
}
}
#[inline]
fn matchlen(old: &[u8], new: &[u8]) -> usize {
old.iter()
.zip(new)
.take_while(|(a, b)| a == b)
.count()
}
fn search(I: &[isize], old: &[u8], new: &[u8]) -> (isize, usize) {
if I.len() < 3 {
let x = matchlen(&old[usz(I[0])..], new);
let y = matchlen(&old[usz(I[I.len() - 1])..], new);
if x > y {
(I[0], x)
} else {
(I[I.len() - 1], y)
}
} else {
let mid = (I.len() - 1) / 2;
let left = &old[usz(I[mid])..];
let right = new;
let len_to_check = left.len().min(right.len());
if left[..len_to_check] < right[..len_to_check] {
search(&I[mid..], old, new)
} else {
search(&I[..=mid], old, new)
}
}
}
#[inline]
fn offtout(x: isize, buf: &mut [u8]) {
let x64 = x as i64;
if x64 >= 0 {
buf.copy_from_slice(&x64.to_le_bytes());
} else {
let tmp = (-x64) as u64 | (1u64 << 63);
buf.copy_from_slice(&tmp.to_le_bytes());
}
}
fn bsdiff_internal(old: &[u8], new: &[u8], writer: &mut dyn Write) -> io::Result<()> {
let mut I = vec![0; old.len() + 1];
let mut V = vec![0; old.len() + 1];
qsufsort(&mut I, &mut V, old);
let mut buffer = Vec::with_capacity(1024);
let mut scan = 0;
let mut len = 0usize;
let mut pos = 0usize;
let mut lastscan = 0;
let mut lastpos = 0;
let mut lastoffset = 0isize;
while scan < new.len() {
let mut oldscore = 0;
scan += len;
let mut scsc = scan;
while scan < new.len() {
let (p, l) = search(&I[..=old.len()], old, &new[scan..]);
pos = usz(p);
len = l;
while scsc < scan + len {
if scsc as isize + lastoffset < old.len() as _
&& (old[usz(scsc as isize + lastoffset)] == new[scsc])
{
oldscore += 1;
}
scsc += 1;
}
if len == oldscore && (len != 0) || len > oldscore + 8 {
break;
}
if scan as isize + lastoffset < old.len() as _
&& (old[usz(scan as isize + lastoffset)] == new[scan])
{
oldscore -= 1;
}
scan += 1;
}
if !(len != oldscore || scan == new.len()) {
continue;
}
let mut s = 0;
let mut Sf = 0;
let mut lenf = 0usize;
let mut i = 0usize;
while lastscan + i < scan && (lastpos + i < old.len() as _) {
if old[lastpos + i] == new[lastscan + i] {
s += 1;
}
i += 1;
if s * 2 - i as isize <= Sf * 2 - lenf as isize {
continue;
}
Sf = s;
lenf = i;
}
let mut lenb = 0;
if scan < new.len() {
let mut s = 0isize;
let mut Sb = 0;
let mut i = 1;
while scan >= lastscan + i && (pos >= i) {
if old[pos - i] == new[scan - i] {
s += 1;
}
if s * 2 - i as isize > Sb * 2 - lenb as isize {
Sb = s;
lenb = i;
}
i += 1;
}
}
if lastscan + lenf > scan - lenb {
let overlap = lastscan + lenf - (scan - lenb);
let mut s = 0;
let mut Ss = 0;
let mut lens = 0;
for i in 0..overlap {
if new[lastscan + lenf - overlap + i] == old[lastpos + lenf - overlap + i] {
s += 1;
}
if new[scan - lenb + i] == old[pos - lenb + i] {
s -= 1;
}
if s > Ss {
Ss = s;
lens = i + 1;
}
}
lenf = lenf + lens - overlap;
lenb -= lens;
}
let mut buf: [u8; 24] = [0; 24];
offtout(lenf as _, &mut buf[..8]);
offtout(
scan as isize - lenb as isize - (lastscan + lenf) as isize,
&mut buf[8..16],
);
offtout(
pos as isize - lenb as isize - (lastpos + lenf) as isize,
&mut buf[16..24],
);
writer.write_all(&buf[..24])?;
buffer.clear();
buffer.extend(
new[lastscan..lastscan + lenf]
.iter()
.zip(&old[lastpos..lastpos + lenf])
.map(|(n, o)| n.wrapping_sub(*o)),
);
writer.write_all(&buffer)?;
let write_len = scan - lenb - (lastscan + lenf);
let write_start = lastscan + lenf;
writer.write_all(&new[write_start..write_start + write_len])?;
lastscan = scan - lenb;
lastpos = pos - lenb;
lastoffset = pos as isize - scan as isize;
}
Ok(())
}
fn bsdiff_with_writer(old: &[u8], new: &[u8], writer: &mut Bsdf2Writer) -> io::Result<()> {
let mut I = vec![0; old.len() + 1];
let mut V = vec![0; old.len() + 1];
qsufsort(&mut I, &mut V, old);
let mut buffer = Vec::with_capacity(1024);
let mut scan = 0;
let mut len = 0usize;
let mut pos = 0usize;
let mut lastscan = 0;
let mut lastpos = 0;
let mut lastoffset = 0isize;
while scan < new.len() {
let mut oldscore = 0;
scan += len;
let mut scsc = scan;
while scan < new.len() {
let (p, l) = search(&I[..=old.len()], old, &new[scan..]);
pos = usz(p);
len = l;
while scsc < scan + len {
if scsc as isize + lastoffset < old.len() as _
&& (old[usz(scsc as isize + lastoffset)] == new[scsc])
{
oldscore += 1;
}
scsc += 1;
}
if len == oldscore && (len != 0) || len > oldscore + 8 {
break;
}
if scan as isize + lastoffset < old.len() as _
&& (old[usz(scan as isize + lastoffset)] == new[scan])
{
oldscore -= 1;
}
scan += 1;
}
if !(len != oldscore || scan == new.len()) {
continue;
}
let mut s = 0;
let mut Sf = 0;
let mut lenf = 0usize;
let mut i = 0usize;
while lastscan + i < scan && (lastpos + i < old.len() as _) {
if old[lastpos + i] == new[lastscan + i] {
s += 1;
}
i += 1;
if s * 2 - i as isize <= Sf * 2 - lenf as isize {
continue;
}
Sf = s;
lenf = i;
}
let mut lenb = 0;
if scan < new.len() {
let mut s = 0isize;
let mut Sb = 0;
let mut i = 1;
while scan >= lastscan + i && (pos >= i) {
if old[pos - i] == new[scan - i] {
s += 1;
}
if s * 2 - i as isize > Sb * 2 - lenb as isize {
Sb = s;
lenb = i;
}
i += 1;
}
}
if lastscan + lenf > scan - lenb {
let overlap = lastscan + lenf - (scan - lenb);
let mut s = 0;
let mut Ss = 0;
let mut lens = 0;
for i in 0..overlap {
if new[lastscan + lenf - overlap + i] == old[lastpos + lenf - overlap + i] {
s += 1;
}
if new[scan - lenb + i] == old[pos - lenb + i] {
s -= 1;
}
if s > Ss {
Ss = s;
lens = i + 1;
}
}
lenf = lenf + lens - overlap;
lenb -= lens;
}
let entry = ControlEntry {
diff_size: lenf as i64,
extra_size: (scan as isize - lenb as isize - (lastscan + lenf) as isize) as i64,
offset_increment: (pos as isize - lenb as isize - (lastpos + lenf) as isize) as i64,
};
writer.add_control_entry(entry)?;
buffer.clear();
buffer.extend(
new[lastscan..lastscan + lenf]
.iter()
.zip(&old[lastpos..lastpos + lenf])
.map(|(n, o)| n.wrapping_sub(*o)),
);
writer.write_diff_stream(&buffer)?;
let write_len = scan - lenb - (lastscan + lenf);
let write_start = lastscan + lenf;
writer.write_extra_stream(&new[write_start..write_start + write_len])?;
lastscan = scan - lenb;
lastpos = pos - lenb;
lastoffset = pos as isize - scan as isize;
}
Ok(())
}