summaryrefslogtreecommitdiff
path: root/internal/gitdiff/apply_binary.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/gitdiff/apply_binary.go')
-rw-r--r--internal/gitdiff/apply_binary.go206
1 files changed, 0 insertions, 206 deletions
diff --git a/internal/gitdiff/apply_binary.go b/internal/gitdiff/apply_binary.go
deleted file mode 100644
index b34772d..0000000
--- a/internal/gitdiff/apply_binary.go
+++ /dev/null
@@ -1,206 +0,0 @@
-package gitdiff
-
-import (
- "errors"
- "fmt"
- "io"
-)
-
-// BinaryApplier applies binary changes described in a fragment to source data.
-// The applier must be closed after use.
-type BinaryApplier struct {
- dst io.Writer
- src io.ReaderAt
-
- closed bool
- dirty bool
-}
-
-// NewBinaryApplier creates an BinaryApplier that reads data from src and
-// writes modified data to dst.
-func NewBinaryApplier(dst io.Writer, src io.ReaderAt) *BinaryApplier {
- a := BinaryApplier{
- dst: dst,
- src: src,
- }
- return &a
-}
-
-// ApplyFragment applies the changes in the fragment f and writes the result to
-// dst. ApplyFragment can be called at most once.
-//
-// If an error occurs while applying, ApplyFragment returns an *ApplyError that
-// annotates the error with additional information. If the error is because of
-// a conflict between a fragment and the source, the wrapped error will be a
-// *Conflict.
-func (a *BinaryApplier) ApplyFragment(f *BinaryFragment) error {
- if f == nil {
- return applyError(errors.New("nil fragment"))
- }
- if a.closed {
- return applyError(errApplierClosed)
- }
- if a.dirty {
- return applyError(errApplyInProgress)
- }
-
- // mark an apply as in progress, even if it fails before making changes
- a.dirty = true
-
- switch f.Method {
- case BinaryPatchLiteral:
- if _, err := a.dst.Write(f.Data); err != nil {
- return applyError(err)
- }
- case BinaryPatchDelta:
- if err := applyBinaryDeltaFragment(a.dst, a.src, f.Data); err != nil {
- return applyError(err)
- }
- default:
- return applyError(fmt.Errorf("unsupported binary patch method: %v", f.Method))
- }
- return nil
-}
-
-// Close writes any data following the last applied fragment and prevents
-// future calls to ApplyFragment.
-func (a *BinaryApplier) Close() (err error) {
- if a.closed {
- return nil
- }
-
- a.closed = true
- if !a.dirty {
- _, err = copyFrom(a.dst, a.src, 0)
- } else {
- // do nothing, applying a binary fragment copies all data
- }
- return err
-}
-
-func applyBinaryDeltaFragment(dst io.Writer, src io.ReaderAt, frag []byte) error {
- srcSize, delta := readBinaryDeltaSize(frag)
- if err := checkBinarySrcSize(src, srcSize); err != nil {
- return err
- }
-
- dstSize, delta := readBinaryDeltaSize(delta)
-
- for len(delta) > 0 {
- op := delta[0]
- if op == 0 {
- return errors.New("invalid delta opcode 0")
- }
-
- var n int64
- var err error
- switch op & 0x80 {
- case 0x80:
- n, delta, err = applyBinaryDeltaCopy(dst, op, delta[1:], src)
- case 0x00:
- n, delta, err = applyBinaryDeltaAdd(dst, op, delta[1:])
- }
- if err != nil {
- return err
- }
- dstSize -= n
- }
-
- if dstSize != 0 {
- return errors.New("corrupt binary delta: insufficient or extra data")
- }
- return nil
-}
-
-// readBinaryDeltaSize reads a variable length size from a delta-encoded binary
-// fragment, returing the size and the unused data. Data is encoded as:
-//
-// [[1xxxxxxx]...] [0xxxxxxx]
-//
-// in little-endian order, with 7 bits of the value per byte.
-func readBinaryDeltaSize(d []byte) (size int64, rest []byte) {
- shift := uint(0)
- for i, b := range d {
- size |= int64(b&0x7F) << shift
- shift += 7
- if b <= 0x7F {
- return size, d[i+1:]
- }
- }
- return size, nil
-}
-
-// applyBinaryDeltaAdd applies an add opcode in a delta-encoded binary
-// fragment, returning the amount of data written and the usused part of the
-// fragment. An add operation takes the form:
-//
-// [0xxxxxx][[data1]...]
-//
-// where the lower seven bits of the opcode is the number of data bytes
-// following the opcode. See also pack-format.txt in the Git source.
-func applyBinaryDeltaAdd(w io.Writer, op byte, delta []byte) (n int64, rest []byte, err error) {
- size := int(op)
- if len(delta) < size {
- return 0, delta, errors.New("corrupt binary delta: incomplete add")
- }
- _, err = w.Write(delta[:size])
- return int64(size), delta[size:], err
-}
-
-// applyBinaryDeltaCopy applies a copy opcode in a delta-encoded binary
-// fragment, returing the amount of data written and the unused part of the
-// fragment. A copy operation takes the form:
-//
-// [1xxxxxxx][offset1][offset2][offset3][offset4][size1][size2][size3]
-//
-// where the lower seven bits of the opcode determine which non-zero offset and
-// size bytes are present in little-endian order: if bit 0 is set, offset1 is
-// present, etc. If no offset or size bytes are present, offset is 0 and size
-// is 0x10000. See also pack-format.txt in the Git source.
-func applyBinaryDeltaCopy(w io.Writer, op byte, delta []byte, src io.ReaderAt) (n int64, rest []byte, err error) {
- const defaultSize = 0x10000
-
- unpack := func(start, bits uint) (v int64) {
- for i := uint(0); i < bits; i++ {
- mask := byte(1 << (i + start))
- if op&mask > 0 {
- if len(delta) == 0 {
- err = errors.New("corrupt binary delta: incomplete copy")
- return
- }
- v |= int64(delta[0]) << (8 * i)
- delta = delta[1:]
- }
- }
- return
- }
-
- offset := unpack(0, 4)
- size := unpack(4, 3)
- if err != nil {
- return 0, delta, err
- }
- if size == 0 {
- size = defaultSize
- }
-
- // TODO(bkeyes): consider pooling these buffers
- b := make([]byte, size)
- if _, err := src.ReadAt(b, offset); err != nil {
- return 0, delta, err
- }
-
- _, err = w.Write(b)
- return size, delta, err
-}
-
-func checkBinarySrcSize(r io.ReaderAt, size int64) error {
- ok, err := isLen(r, size)
- if err != nil {
- return err
- }
- if !ok {
- return &Conflict{"fragment src size does not match actual src size"}
- }
- return nil
-}