Rexd.Stream (Rexd v1.0.0)

Copy Markdown View Source

Streaming signature, delta and patch.

Each function takes an enumerable of binaries, split at arbitrary boundaries, and returns a lazy enumerable of binaries. Concatenated, the output is the librsync wire format (signature/2, delta/2) or the rebuilt data (patch/3). Nothing is read until the result is consumed.

Memory

  • signature/2 holds less than one block of input.
  • delta/2 holds the signature, one block, the current input chunk (small chunks are grouped up to 64 KiB, larger ones are used as given), and the unmatched bytes not yet emitted. Those are emitted as a literal command once they reach 64 KiB at the end of an input chunk.
  • patch/3 holds a partial command header. Literal data is passed through as it arrives, and copies are read from the basis in pieces of at most 64 KiB.

Differences from the whole-binary functions

signature/2 produces exactly the bytes of Rexd.Signature.encode/1. delta/2 finds the same matches as Rexd.delta/2, but may split a long unmatched run into several literal commands, as librsync does; the rebuilt output is identical.

Errors

Invalid options raise ArgumentError when the function is called. Invalid input can only be detected while the stream is consumed, so it raises Rexd.StreamError then, with the same reason terms as the tuple-returning functions.

Summary

Types

Reads length bytes of the basis starting at offset.

Functions

Streams the encoded delta from the basis described by signature to the new data read from enumerable.

Streams the data rebuilt by applying the encoded delta read from enumerable to a basis.

Streams the encoded signature of the basis read from enumerable.

Types

basis_fun()

@type basis_fun() :: (non_neg_integer(), pos_integer() -> binary())

Reads length bytes of the basis starting at offset.

Functions

delta(signature, enumerable, opts \\ [])

Streams the encoded delta from the basis described by signature to the new data read from enumerable.

Options

  • :on_stats - a function called with Rexd.Delta.Stats once the last command has been produced, before the end marker is emitted.

    iex> basis = String.duplicate("0123456789", 50) iex> new = ["prefix ", basis, " suffix"] iex> sig = Rexd.signature(basis, block_len: 16) iex> {:ok, delta} = sig |> Rexd.Stream.delta(new) |> Enum.join() |> Rexd.Delta.decode() iex> Rexd.patch(basis, delta) == {:ok, IO.iodata_to_binary(new)} true

patch(basis_fun, enumerable, opts \\ [])

@spec patch(basis_fun(), Enumerable.t(), keyword()) :: Enumerable.t()

Streams the data rebuilt by applying the encoded delta read from enumerable to a basis.

The basis is read through basis_fun, called as basis_fun.(offset, length) with length of at most 64 KiB. It must return exactly length bytes, or a shorter binary when the range extends past the end of the basis. For a file, fn offset, length -> :file.pread(io_device, offset, length) end wrapped to return the data (or <<>> on :eof) is enough.

Takes the options of Rexd.patch/3. Raises Rexd.StreamError while consumed on an invalid delta, a copy past the end of the basis, or output beyond :max_size.

iex> basis = "the quick brown fox"
iex> sig = Rexd.signature(basis, block_len: 4)
iex> encoded = sig |> Rexd.Stream.delta(["the quick red fox"]) |> Enum.join()
iex> read = fn offset, length -> binary_part(basis, offset, min(length, byte_size(basis) - offset)) end
iex> read |> Rexd.Stream.patch([encoded]) |> Enum.join()
"the quick red fox"

signature(enumerable, opts \\ [])

@spec signature(Enumerable.t(), keyword()) :: Enumerable.t()

Streams the encoded signature of the basis read from enumerable.

Takes the options of Rexd.signature/2.

iex> basis = String.duplicate("abcdefgh", 100)
iex> streamed = ["abcdefgh" |> String.duplicate(40), String.duplicate("abcdefgh", 60)]
iex> streamed |> Rexd.Stream.signature(block_len: 64) |> Enum.join() ==
...>   basis |> Rexd.signature(block_len: 64) |> Rexd.Signature.encode() |> IO.iodata_to_binary()
true