A delta: the commands that rebuild a new binary from a basis, and its librsync wire encoding.
Commands are {:literal, bytes} (append bytes) and
{:copy, offset, length} (append length bytes of the basis starting at
offset). Deltas computed by compute/2 never contain two adjacent
literals, never contain a copy that directly continues the previous copy,
and never contain zero-length commands.
Wire format: u32 RS_DELTA_MAGIC (0x72730236), then one command per
opcode byte, then an END byte (0x00). The opcode selects the command kind
and the widths of its big-endian arguments; see lib/rexd/delta/prototab.ex,
generated from librsync's prototab.c. Encoding follows librsync's
emit.c: literals of 1..64 bytes carry their length in the opcode, longer
literals and both copy arguments use the smallest width that fits.
decode/1 rejects what librsync's patcher treats as a corrupt stream:
zero-length literal or copy commands, and arguments of 2^63 or more
(librsync reads them as signed 64-bit integers). Whether copies fit the
basis is checked by Rexd.patch/3, the first point where the basis is
known.
Summary
Functions
Decodes a librsync delta.
Encodes the delta in librsync wire format. Zero-length commands are omitted.
The delta magic number.
Statistics computed from the commands of delta. The search counters are
nil.
Types
@type command() :: {:literal, binary()} | {:copy, non_neg_integer(), non_neg_integer()}
@type decode_error() :: :truncated_header | :truncated | :missing_end | :trailing_data | {:bad_magic, non_neg_integer()} | {:reserved_opcode, byte()} | {:zero_length, :literal | :copy} | {:argument_too_large, non_neg_integer()}
Reasons decode/1 can fail.
@type t() :: %Rexd.Delta{commands: [command()]}
Functions
@spec decode(binary()) :: {:ok, t()} | {:error, decode_error()}
Decodes a librsync delta.
Encodes the delta in librsync wire format. Zero-length commands are omitted.
@spec magic() :: non_neg_integer()
The delta magic number.
@spec stats(t()) :: Rexd.Delta.Stats.t()
Statistics computed from the commands of delta. The search counters are
nil.
iex> Rexd.Delta.stats(%Rexd.Delta{commands: [{:literal, "abc"}, {:copy, 0, 10}]})
%Rexd.Delta.Stats{literal_bytes: 3, literal_commands: 1, copy_bytes: 10, copy_commands: 1}