bsdiff_android/
diff.rs

1#![allow(non_snake_case)]
2/*-
3 * Copyright 2003-2005 Colin Percival
4 * Copyright 2012 Matthew Endsley
5 * Modified 2017 Pieter-Jan Briers
6 * Modified 2025 - Performance optimizations
7 * All rights reserved
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted providing that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31use std::cmp::Ordering;
32use std::io;
33use std::io::Write;
34
35use crate::bsdf2_writer::{Bsdf2Writer, CompressionAlgorithm, ControlEntry};
36
37/// Diff an "old" and a "new" file, returning a legacy BSDIFF40 patch.
38/// 
39/// This maintains backward compatibility - generates classic BZ2-compressed patches
40/// that are identical to the original bsdiff implementation.
41pub fn diff<T: Write>(old: &[u8], new: &[u8], writer: &mut T) -> io::Result<()> {
42    bsdiff_internal(old, new, writer)
43}
44
45/// Generate a legacy BSDIFF40 patch (BZ2 compressed)
46pub fn diff_bsdiff40<T: Write>(old: &[u8], new: &[u8], writer: &mut T) -> io::Result<()> {
47    let mut patch_writer = Bsdf2Writer::new_legacy();
48    bsdiff_with_writer(old, new, &mut patch_writer)?;
49    patch_writer.close(writer)
50}
51
52/// Generate a BSDF2 patch with specified compression algorithms
53pub fn diff_bsdf2<T: Write>(
54    old: &[u8],
55    new: &[u8],
56    writer: &mut T,
57    ctrl_alg: CompressionAlgorithm,
58    diff_alg: CompressionAlgorithm,
59    extra_alg: CompressionAlgorithm,
60) -> io::Result<()> {
61    let mut patch_writer = Bsdf2Writer::new(ctrl_alg, diff_alg, extra_alg);
62    bsdiff_with_writer(old, new, &mut patch_writer)?;
63    patch_writer.close(writer)
64}
65
66/// Generate a BSDF2 patch with all streams using the same compression
67pub fn diff_bsdf2_uniform<T: Write>(
68    old: &[u8],
69    new: &[u8],
70    writer: &mut T,
71    alg: CompressionAlgorithm,
72) -> io::Result<()> {
73    diff_bsdf2(old, new, writer, alg, alg, alg)
74}
75
76#[inline(always)]
77fn usz(i: isize) -> usize {
78    debug_assert!(i >= 0);
79    i as usize
80}
81
82struct SplitParams {
83    start: usize,
84    len: usize,
85}
86
87#[inline]
88fn split_internal(
89    I: &mut [isize],
90    V: &mut [isize],
91    start: usize,
92    len: usize,
93    h: usize,
94) -> Option<SplitParams> {
95    if len < 16 {
96        let mut k = start;
97        while k < start + len {
98            let mut j = 1;
99            let mut x = V[usz(I[k] + h as isize)];
100            let mut i = 1;
101            while k + i < start + len {
102                let v = V[usz(I[k + i] + h as isize)];
103                if v < x {
104                    x = v;
105                    j = 0;
106                }
107                if v == x {
108                    I.swap(k + j, k + i);
109                    j += 1;
110                }
111                i += 1;
112            }
113            let kj = (k + j) as isize;
114            for &Ii in &I[k..k + j] {
115                V[usz(Ii)] = kj - 1;
116            }
117            if j == 1 {
118                I[k] = -1;
119            }
120            k += j;
121        }
122        None
123    } else {
124        let x = V[usz(I[start + len / 2] + h as isize)];
125        
126        let mut jj = 0;
127        let mut kk = 0;
128        for &Ii in &I[start..start + len] {
129            let v = V[usz(Ii + h as isize)];
130            if v < x {
131                jj += 1;
132            }
133            if v == x {
134                kk += 1;
135            }
136        }
137        let jj = jj + start;
138        let kk = kk + jj;
139        
140        let mut j = 0;
141        let mut k = 0;
142        let mut i = start;
143        while i < jj {
144            match V[usz(I[i] + h as isize)].cmp(&x) {
145                Ordering::Less => i += 1,
146                Ordering::Equal => {
147                    I.swap(i, jj + j);
148                    j += 1;
149                }
150                Ordering::Greater => {
151                    I.swap(i, kk + k);
152                    k += 1;
153                }
154            }
155        }
156        
157        while jj + j < kk {
158            if V[usz(I[jj + j] + h as isize)] == x {
159                j += 1;
160            } else {
161                I.swap(jj + j, kk + k);
162                k += 1;
163            }
164        }
165        
166        if jj > start {
167            split(I, V, start, jj - start, h);
168        }
169        
170        let kk_minus_1 = (kk - 1) as isize;
171        for &Ii in &I[jj..kk] {
172            V[usz(Ii)] = kk_minus_1;
173        }
174        if jj == kk - 1 {
175            I[jj] = -1;
176        }
177
178        if start + len > kk {
179            Some(SplitParams {
180                start: kk,
181                len: start + len - kk,
182            })
183        } else {
184            None
185        }
186    }
187}
188
189fn split(I: &mut [isize], V: &mut [isize], start: usize, len: usize, h: usize) {
190    let mut ret = Some(SplitParams { start, len });
191    while let Some(params) = ret {
192        ret = split_internal(I, V, params.start, params.len, h);
193    }
194}
195
196fn qsufsort(I: &mut [isize], V: &mut [isize], old: &[u8]) {
197    let mut buckets: [isize; 256] = [0; 256];
198    
199    for &o in old {
200        buckets[o as usize] += 1;
201    }
202    
203    for i in 1..256 {
204        buckets[i] += buckets[i - 1];
205    }
206    
207    for i in (1..256).rev() {
208        buckets[i] = buckets[i - 1];
209    }
210    buckets[0] = 0;
211    
212    for (i, &old_byte) in old.iter().enumerate() {
213        buckets[old_byte as usize] += 1;
214        I[usz(buckets[old_byte as usize])] = i as isize;
215    }
216    
217    I[0] = old.len() as isize;
218    
219    for (i, &old_byte) in old.iter().enumerate() {
220        V[i] = buckets[old_byte as usize];
221    }
222    V[old.len()] = 0;
223    
224    for i in 1..256 {
225        if buckets[i] == buckets[i - 1] + 1 {
226            I[usz(buckets[i])] = -1;
227        }
228    }
229    I[0] = -1;
230    
231    let mut h = 1;
232    while I[0] != -(old.len() as isize + 1) {
233        let mut len = 0;
234        let mut i = 0;
235        while i < old.len() as isize + 1 {
236            if I[usz(i)] < 0 {
237                len -= I[usz(i)];
238                i = i - I[usz(i)];
239            } else {
240                if len != 0 {
241                    I[usz(i - len)] = -len;
242                }
243                len = V[usz(I[usz(i)])] + 1 - i;
244                split(I, V, usz(i), usz(len), h);
245                i += len;
246                len = 0;
247            }
248        }
249        if len != 0 {
250            I[usz(i - len)] = -len;
251        }
252        h += h;
253    }
254    
255    for (i, &v) in V[0..=old.len()].iter().enumerate() {
256        I[usz(v)] = i as isize;
257    }
258}
259
260#[inline]
261fn matchlen(old: &[u8], new: &[u8]) -> usize {
262    old.iter()
263        .zip(new)
264        .take_while(|(a, b)| a == b)
265        .count()
266}
267
268fn search(I: &[isize], old: &[u8], new: &[u8]) -> (isize, usize) {
269    if I.len() < 3 {
270        let x = matchlen(&old[usz(I[0])..], new);
271        let y = matchlen(&old[usz(I[I.len() - 1])..], new);
272        if x > y {
273            (I[0], x)
274        } else {
275            (I[I.len() - 1], y)
276        }
277    } else {
278        let mid = (I.len() - 1) / 2;
279        let left = &old[usz(I[mid])..];
280        let right = new;
281        let len_to_check = left.len().min(right.len());
282        
283        if left[..len_to_check] < right[..len_to_check] {
284            search(&I[mid..], old, new)
285        } else {
286            search(&I[..=mid], old, new)
287        }
288    }
289}
290
291/// Encode signed integer in bspatch sign-magnitude format
292#[inline]
293fn offtout(x: isize, buf: &mut [u8]) {
294    let x64 = x as i64;
295    if x64 >= 0 {
296        buf.copy_from_slice(&x64.to_le_bytes());
297    } else {
298        let tmp = (-x64) as u64 | (1u64 << 63);
299        buf.copy_from_slice(&tmp.to_le_bytes());
300    }
301}
302
303/// Original bsdiff algorithm that writes directly (for backward compatibility)
304fn bsdiff_internal(old: &[u8], new: &[u8], writer: &mut dyn Write) -> io::Result<()> {
305    let mut I = vec![0; old.len() + 1];
306    let mut V = vec![0; old.len() + 1];
307    
308    qsufsort(&mut I, &mut V, old);
309
310    let mut buffer = Vec::with_capacity(1024);
311
312    let mut scan = 0;
313    let mut len = 0usize;
314    let mut pos = 0usize;
315    let mut lastscan = 0;
316    let mut lastpos = 0;
317    let mut lastoffset = 0isize;
318    
319    while scan < new.len() {
320        let mut oldscore = 0;
321        scan += len;
322        let mut scsc = scan;
323        
324        while scan < new.len() {
325            let (p, l) = search(&I[..=old.len()], old, &new[scan..]);
326            pos = usz(p);
327            len = l;
328            
329            while scsc < scan + len {
330                if scsc as isize + lastoffset < old.len() as _
331                    && (old[usz(scsc as isize + lastoffset)] == new[scsc])
332                {
333                    oldscore += 1;
334                }
335                scsc += 1;
336            }
337            
338            if len == oldscore && (len != 0) || len > oldscore + 8 {
339                break;
340            }
341            
342            if scan as isize + lastoffset < old.len() as _
343                && (old[usz(scan as isize + lastoffset)] == new[scan])
344            {
345                oldscore -= 1;
346            }
347            scan += 1;
348        }
349        
350        if !(len != oldscore || scan == new.len()) {
351            continue;
352        }
353        
354        let mut s = 0;
355        let mut Sf = 0;
356        let mut lenf = 0usize;
357        let mut i = 0usize;
358        while lastscan + i < scan && (lastpos + i < old.len() as _) {
359            if old[lastpos + i] == new[lastscan + i] {
360                s += 1;
361            }
362            i += 1;
363            if s * 2 - i as isize <= Sf * 2 - lenf as isize {
364                continue;
365            }
366            Sf = s;
367            lenf = i;
368        }
369        
370        let mut lenb = 0;
371        if scan < new.len() {
372            let mut s = 0isize;
373            let mut Sb = 0;
374            let mut i = 1;
375            while scan >= lastscan + i && (pos >= i) {
376                if old[pos - i] == new[scan - i] {
377                    s += 1;
378                }
379                if s * 2 - i as isize > Sb * 2 - lenb as isize {
380                    Sb = s;
381                    lenb = i;
382                }
383                i += 1;
384            }
385        }
386        
387        if lastscan + lenf > scan - lenb {
388            let overlap = lastscan + lenf - (scan - lenb);
389            let mut s = 0;
390            let mut Ss = 0;
391            let mut lens = 0;
392            for i in 0..overlap {
393                if new[lastscan + lenf - overlap + i] == old[lastpos + lenf - overlap + i] {
394                    s += 1;
395                }
396                if new[scan - lenb + i] == old[pos - lenb + i] {
397                    s -= 1;
398                }
399                if s > Ss {
400                    Ss = s;
401                    lens = i + 1;
402                }
403            }
404            lenf = lenf + lens - overlap;
405            lenb -= lens;
406        }
407        
408        // Write control tuple (original format)
409        let mut buf: [u8; 24] = [0; 24];
410        offtout(lenf as _, &mut buf[..8]);
411        offtout(
412            scan as isize - lenb as isize - (lastscan + lenf) as isize,
413            &mut buf[8..16],
414        );
415        offtout(
416            pos as isize - lenb as isize - (lastpos + lenf) as isize,
417            &mut buf[16..24],
418        );
419        writer.write_all(&buf[..24])?;
420
421        // Write diff data
422        buffer.clear();
423        buffer.extend(
424            new[lastscan..lastscan + lenf]
425                .iter()
426                .zip(&old[lastpos..lastpos + lenf])
427                .map(|(n, o)| n.wrapping_sub(*o)),
428        );
429        writer.write_all(&buffer)?;
430
431        // Write extra data
432        let write_len = scan - lenb - (lastscan + lenf);
433        let write_start = lastscan + lenf;
434        writer.write_all(&new[write_start..write_start + write_len])?;
435
436        lastscan = scan - lenb;
437        lastpos = pos - lenb;
438        lastoffset = pos as isize - scan as isize;
439    }
440
441    Ok(())
442}
443
444/// Bsdiff algorithm using Bsdf2Writer (for BSDF2 format)
445fn bsdiff_with_writer(old: &[u8], new: &[u8], writer: &mut Bsdf2Writer) -> io::Result<()> {
446    let mut I = vec![0; old.len() + 1];
447    let mut V = vec![0; old.len() + 1];
448    
449    qsufsort(&mut I, &mut V, old);
450
451    let mut buffer = Vec::with_capacity(1024);
452
453    let mut scan = 0;
454    let mut len = 0usize;
455    let mut pos = 0usize;
456    let mut lastscan = 0;
457    let mut lastpos = 0;
458    let mut lastoffset = 0isize;
459    
460    while scan < new.len() {
461        let mut oldscore = 0;
462        scan += len;
463        let mut scsc = scan;
464        
465        while scan < new.len() {
466            let (p, l) = search(&I[..=old.len()], old, &new[scan..]);
467            pos = usz(p);
468            len = l;
469            
470            while scsc < scan + len {
471                if scsc as isize + lastoffset < old.len() as _
472                    && (old[usz(scsc as isize + lastoffset)] == new[scsc])
473                {
474                    oldscore += 1;
475                }
476                scsc += 1;
477            }
478            
479            if len == oldscore && (len != 0) || len > oldscore + 8 {
480                break;
481            }
482            
483            if scan as isize + lastoffset < old.len() as _
484                && (old[usz(scan as isize + lastoffset)] == new[scan])
485            {
486                oldscore -= 1;
487            }
488            scan += 1;
489        }
490        
491        if !(len != oldscore || scan == new.len()) {
492            continue;
493        }
494        
495        let mut s = 0;
496        let mut Sf = 0;
497        let mut lenf = 0usize;
498        let mut i = 0usize;
499        while lastscan + i < scan && (lastpos + i < old.len() as _) {
500            if old[lastpos + i] == new[lastscan + i] {
501                s += 1;
502            }
503            i += 1;
504            if s * 2 - i as isize <= Sf * 2 - lenf as isize {
505                continue;
506            }
507            Sf = s;
508            lenf = i;
509        }
510        
511        let mut lenb = 0;
512        if scan < new.len() {
513            let mut s = 0isize;
514            let mut Sb = 0;
515            let mut i = 1;
516            while scan >= lastscan + i && (pos >= i) {
517                if old[pos - i] == new[scan - i] {
518                    s += 1;
519                }
520                if s * 2 - i as isize > Sb * 2 - lenb as isize {
521                    Sb = s;
522                    lenb = i;
523                }
524                i += 1;
525            }
526        }
527        
528        if lastscan + lenf > scan - lenb {
529            let overlap = lastscan + lenf - (scan - lenb);
530            let mut s = 0;
531            let mut Ss = 0;
532            let mut lens = 0;
533            for i in 0..overlap {
534                if new[lastscan + lenf - overlap + i] == old[lastpos + lenf - overlap + i] {
535                    s += 1;
536                }
537                if new[scan - lenb + i] == old[pos - lenb + i] {
538                    s -= 1;
539                }
540                if s > Ss {
541                    Ss = s;
542                    lens = i + 1;
543                }
544            }
545            lenf = lenf + lens - overlap;
546            lenb -= lens;
547        }
548        
549        // Add control entry
550        let entry = ControlEntry {
551            diff_size: lenf as i64,
552            extra_size: (scan as isize - lenb as isize - (lastscan + lenf) as isize) as i64,
553            offset_increment: (pos as isize - lenb as isize - (lastpos + lenf) as isize) as i64,
554        };
555        writer.add_control_entry(entry)?;
556
557        // Write diff data
558        buffer.clear();
559        buffer.extend(
560            new[lastscan..lastscan + lenf]
561                .iter()
562                .zip(&old[lastpos..lastpos + lenf])
563                .map(|(n, o)| n.wrapping_sub(*o)),
564        );
565        writer.write_diff_stream(&buffer)?;
566
567        // Write extra data
568        let write_len = scan - lenb - (lastscan + lenf);
569        let write_start = lastscan + lenf;
570        writer.write_extra_stream(&new[write_start..write_start + write_len])?;
571
572        lastscan = scan - lenb;
573        lastpos = pos - lenb;
574        lastoffset = pos as isize - scan as isize;
575    }
576
577    Ok(())
578}