1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
//! A rust port of [ddelta], which is a streaming and more efficient version of [bsdiff]. The
//! output created by this program is sometimes (when using [`generate`]) compatible with the
//! original C tool, [ddelta], but not with [bsdiff]. This library may use up to 5 times the old
//! file size + the new file size, (5 × min(o, 2^31-1) + min(n, 2^31-1)), up to 12GiB. To control
//! this, see the `chunk_sizes` parameter of [`generate_chunked`].
//!
//! **Note**: the patches created by program should be compressed. If not compressed, the output may
//! actually be larger than just including the new file. You might want to feed the patch file
//! directly to an [encoder][XzEncoder], and read via a
//! [decoder implementing a compression algorithm][XzDecoder] to not require much disk space.
//! Additionally, no checksum is performed, so you should strongly consider doing a checksum of at
//! least either the old or new file once written.
//!
//! ## Features
//!
//! This crate optionally supports compiling the c library, divsufsort, which is enabled by default.
//! A Rust port is available; however, it has worse performance than the C version. If you'd like
//! to use the Rust version instead, for example if you don't have a C compiler installed, add
//! `default-features = false` to your Cargo.toml, i.e.
//!
//! ```toml
//! [dependencies]
//! ddelta = { version = "0.1.0", default-features = false }
//! ```
//!
//! [ddelta]: https://github.com/julian-klode/ddelta
//! [bsdiff]: http://www.daemonology.net/bsdiff/
//! [XzEncoder]: https://docs.rs/xz2/*/xz2/write/struct.XzEncoder.html
//! [XzDecoder]: https://docs.rs/xz2/*/xz2/read/struct.XzDecoder.html

use byteorder::BigEndian;
use zerocopy::{AsBytes, FromBytes, Unaligned, I64, U64};

use anyhow::Result;
#[cfg(feature = "diff")]
pub use diff::{generate, generate_chunked};
pub use patch::{apply, apply_chunked};

const DDELTA_MAGIC: &[u8; 8] = b"DDELTA40";

#[cfg(feature = "diff")]
mod diff;
mod patch;

/// The current state of the generator.
///
/// Passed to a callback periodically to give feedback, such as updating a progress bar.
#[derive(Eq, PartialEq, Copy, Clone, Hash, Debug)]
#[cfg(feature = "diff")]
pub enum State {
    /// The new or old file is currently being read. This is currently only used in
    /// [`generate_chunked`].
    Reading,
    /// The internal algorithm, divsufsort, is currently being run.
    Sorting,
    /// The generator is currently working its way through the data. The number represents how much
    /// of the new file has been worked through. In other words, if calculating a percentage, divide
    /// this number by the size of the new file.
    Working(u64),
}

#[derive(Debug, Copy, Clone, FromBytes, AsBytes, Unaligned)]
#[repr(C)]
struct PatchHeader {
    magic: [u8; 8],
    new_file_size: U64<BigEndian>,
}

#[derive(Debug, Copy, Clone, FromBytes, AsBytes, Unaligned)]
#[repr(C)]
struct EntryHeader {
    diff: U64<BigEndian>,
    extra: U64<BigEndian>,
    seek: I64<BigEndian>,
}