Skip to main content

delta_kit/
lib.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2//! `delta-kit` — a binary-safe delta codec.
3//!
4//! `compute_delta` produces a compact delta between a base and a target
5//! byte string; `apply_delta` reconstructs the target from the base and
6//! the delta. The wire format is byte-compatible with the
7//! `suture-protocol` crate this codec was extracted from.
8//!
9//! # Strategies
10//!
11//! `compute_delta` picks the smallest applicable strategy:
12//!
13//! 1. **Binary (XOR + Zstd)** — if either input looks binary (contains a
14//!    zero byte in the first 8 KiB). Emits opcode `0x03` when the XOR
15//!    stream compresses smaller than the target; requires the `zstd`
16//!    feature.
17//! 2. **Rabin rolling hash** — when both inputs are at least
18//!    [`BLOCK_SIZE`] (4 KiB). Splits the base into fixed blocks, indexes
19//!    them by a Rabin rolling hash (base 257 over the Mersenne prime
20//!    2^61 − 1) plus an FNV-1a-style strong hash, then scans the target
21//!    with a rolling window emitting `Copy`/`Insert` instructions
22//!    (opcode `0x02`). Emits only if smaller than the target.
23//! 3. **Prefix/suffix trim** — small inputs. Finds the common prefix and
24//!    suffix and stores only the changed middle (opcode `0x01`).
25//! 4. **Full content** — fallback storing the whole target (opcode
26//!    `0x00`).
27//!
28//! # Wire format
29//!
30//! All integers are little-endian. The first byte selects the encoding:
31//!
32//! ```text
33//! 0x00 — full content
34//!   [1..]              entire target
35//!
36//! 0x01 — prefix/suffix patch
37//!   [1..9)    u64     prefix_len   (bytes reused from the base head)
38//!   [9..17)   u64     suffix_len   (bytes reused from the base tail)
39//!   [17..25)  u64     target_len
40//!   [25..]            changed middle bytes of the target
41//!
42//! 0x02 — rolling-hash instruction stream
43//!   [1..9)    u64     target_len
44//!   [9..13)   u32     instruction_count
45//!   then instruction_count records:
46//!     0x01 Copy   [1..9)   u64  base_offset
47//!                 [9..13)  u32  length            (13 bytes total)
48//!     0x02 Insert [1..5)   u32  length
49//!                 [5..5+n) bytes                    (5+n bytes total)
50//!
51//! 0x03 — binary XOR + Zstd   (requires the `zstd` feature)
52//!   [1..9)    u64     target_len
53//!   [9..25)           blake3(base)[..16]
54//!   [25..41)          blake3(target)[..16]
55//!   [41..]            Zstd frame over xor(base, target)
56//! ```
57//!
58//! # Hardened decoding
59//!
60//! The origin implementation silently repaired malformed deltas
61//! (identity-decoding truncated headers, skipping out-of-range copies,
62//! returning an empty vector on checksum mismatch). This codec returns
63//! [`DeltaError`] for those cases instead. Well-formed deltas — including
64//! everything `compute_delta` produces — decode identically, and the
65//! opcode stream is byte-for-byte the same. Consumers that must preserve
66//! the origin's observable lenient behavior can delegate to
67//! [`apply_delta_lenient`] instead.
68//!
69//! # Example
70//!
71//! ```
72//! let base = b"Hello, World!";
73//! let target = b"Hello, Rust!";
74//! let (_base_copy, delta) = delta_kit::compute_delta(base, target);
75//! assert_eq!(
76//!     delta_kit::apply_delta(base, &delta).expect("compute_delta output always applies"),
77//!     target.to_vec()
78//! );
79//! ```
80
81#![forbid(unsafe_code)]
82#![deny(missing_docs)]
83#![cfg_attr(not(feature = "std"), no_std)]
84
85extern crate alloc;
86
87mod delta;
88
89#[cfg(feature = "zstd")]
90pub use delta::compute_binary_delta;
91pub use delta::BLOCK_SIZE;
92pub use delta::{apply_delta, apply_delta_lenient, compute_delta};
93pub use delta::{DeltaError, MismatchSide};