Skip to content
This repository has been archived by the owner on Sep 11, 2020. It is now read-only.

Commit

Permalink
feat: avoid memory allocation on ApplyDelta, PatchDelta
Browse files Browse the repository at this point in the history
  • Loading branch information
orisano committed Jun 28, 2019
1 parent eaa9311 commit 64f2fb7
Showing 1 changed file with 30 additions and 15 deletions.
45 changes: 30 additions & 15 deletions plumbing/format/packfile/patch_delta.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package packfile
import (
"bytes"
"errors"
"io"

"gopkg.in/src-d/go-git.v4/plumbing"
)
Expand Down Expand Up @@ -35,14 +36,20 @@ func ApplyDelta(target, base plumbing.EncodedObject, delta []byte) error {
}
src := buf.Bytes()

dst, err := PatchDelta(src, delta)
dst := bufPool.Get().(*bytes.Buffer)
defer bufPool.Put(dst)
dst.Reset()
err = patchDelta(dst, src, delta)
if err != nil {
return err
}

target.SetSize(int64(len(dst)))

_, err = w.Write(dst)
target.SetSize(int64(dst.Len()))

b := byteSlicePool.Get().([]byte)
_, err = io.CopyBuffer(w, dst, b)
byteSlicePool.Put(b)
return err
}

Expand All @@ -55,23 +62,31 @@ var (
// An error will be returned if delta is corrupted (ErrDeltaLen) or an action command
// is not copy from source or copy from delta (ErrDeltaCmd).
func PatchDelta(src, delta []byte) ([]byte, error) {
b := &bytes.Buffer{}
if err := patchDelta(b, src, delta); err != nil {
return nil, err
}
return b.Bytes(), nil
}

func patchDelta(dst *bytes.Buffer, src, delta []byte) error {
if len(delta) < deltaSizeMin {
return nil, ErrInvalidDelta
return ErrInvalidDelta
}

srcSz, delta := decodeLEB128(delta)
if srcSz != uint(len(src)) {
return nil, ErrInvalidDelta
return ErrInvalidDelta
}

targetSz, delta := decodeLEB128(delta)
remainingTargetSz := targetSz

var cmd byte
dest := make([]byte, 0, targetSz)
dst.Grow(int(targetSz))
for {
if len(delta) == 0 {
return nil, ErrInvalidDelta
return ErrInvalidDelta
}

cmd = delta[0]
Expand All @@ -81,43 +96,43 @@ func PatchDelta(src, delta []byte) ([]byte, error) {
var err error
offset, delta, err = decodeOffset(cmd, delta)
if err != nil {
return nil, err
return err
}

sz, delta, err = decodeSize(cmd, delta)
if err != nil {
return nil, err
return err
}

if invalidSize(sz, targetSz) ||
invalidOffsetSize(offset, sz, srcSz) {
break
}
dest = append(dest, src[offset:offset+sz]...)
dst.Write(src[offset:offset+sz])
remainingTargetSz -= sz
} else if isCopyFromDelta(cmd) {
sz := uint(cmd) // cmd is the size itself
if invalidSize(sz, targetSz) {
return nil, ErrInvalidDelta
return ErrInvalidDelta
}

if uint(len(delta)) < sz {
return nil, ErrInvalidDelta
return ErrInvalidDelta
}

dest = append(dest, delta[0:sz]...)
dst.Write(delta[0:sz])
remainingTargetSz -= sz
delta = delta[sz:]
} else {
return nil, ErrDeltaCmd
return ErrDeltaCmd
}

if remainingTargetSz <= 0 {
break
}
}

return dest, nil
return nil
}

// Decodes a number encoded as an unsigned LEB128 at the start of some
Expand Down

0 comments on commit 64f2fb7

Please sign in to comment.