Skip to content

Commit d456ce9

Browse files
committed
feat: avoid memory allocation on ApplyDelta, PatchDelta
Signed-off-by: Nao YONASHIRO <[email protected]>
1 parent c1086ea commit d456ce9

File tree

1 file changed

+30
-15
lines changed

1 file changed

+30
-15
lines changed

plumbing/format/packfile/patch_delta.go

+30-15
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package packfile
33
import (
44
"bytes"
55
"errors"
6+
"io"
67

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

38-
dst, err := PatchDelta(src, delta)
39+
dst := bufPool.Get().(*bytes.Buffer)
40+
defer bufPool.Put(dst)
41+
dst.Reset()
42+
err = patchDelta(dst, src, delta)
3943
if err != nil {
4044
return err
4145
}
4246

43-
target.SetSize(int64(len(dst)))
4447

45-
_, err = w.Write(dst)
48+
target.SetSize(int64(dst.Len()))
49+
50+
b := byteSlicePool.Get().([]byte)
51+
_, err = io.CopyBuffer(w, dst, b)
52+
byteSlicePool.Put(b)
4653
return err
4754
}
4855

@@ -55,23 +62,31 @@ var (
5562
// An error will be returned if delta is corrupted (ErrDeltaLen) or an action command
5663
// is not copy from source or copy from delta (ErrDeltaCmd).
5764
func PatchDelta(src, delta []byte) ([]byte, error) {
65+
b := &bytes.Buffer{}
66+
if err := patchDelta(b, src, delta); err != nil {
67+
return nil, err
68+
}
69+
return b.Bytes(), nil
70+
}
71+
72+
func patchDelta(dst *bytes.Buffer, src, delta []byte) error {
5873
if len(delta) < deltaSizeMin {
59-
return nil, ErrInvalidDelta
74+
return ErrInvalidDelta
6075
}
6176

6277
srcSz, delta := decodeLEB128(delta)
6378
if srcSz != uint(len(src)) {
64-
return nil, ErrInvalidDelta
79+
return ErrInvalidDelta
6580
}
6681

6782
targetSz, delta := decodeLEB128(delta)
6883
remainingTargetSz := targetSz
6984

7085
var cmd byte
71-
dest := make([]byte, 0, targetSz)
86+
dst.Grow(int(targetSz))
7287
for {
7388
if len(delta) == 0 {
74-
return nil, ErrInvalidDelta
89+
return ErrInvalidDelta
7590
}
7691

7792
cmd = delta[0]
@@ -81,43 +96,43 @@ func PatchDelta(src, delta []byte) ([]byte, error) {
8196
var err error
8297
offset, delta, err = decodeOffset(cmd, delta)
8398
if err != nil {
84-
return nil, err
99+
return err
85100
}
86101

87102
sz, delta, err = decodeSize(cmd, delta)
88103
if err != nil {
89-
return nil, err
104+
return err
90105
}
91106

92107
if invalidSize(sz, targetSz) ||
93108
invalidOffsetSize(offset, sz, srcSz) {
94109
break
95110
}
96-
dest = append(dest, src[offset:offset+sz]...)
111+
dst.Write(src[offset:offset+sz])
97112
remainingTargetSz -= sz
98113
} else if isCopyFromDelta(cmd) {
99114
sz := uint(cmd) // cmd is the size itself
100115
if invalidSize(sz, targetSz) {
101-
return nil, ErrInvalidDelta
116+
return ErrInvalidDelta
102117
}
103118

104119
if uint(len(delta)) < sz {
105-
return nil, ErrInvalidDelta
120+
return ErrInvalidDelta
106121
}
107122

108-
dest = append(dest, delta[0:sz]...)
123+
dst.Write(delta[0:sz])
109124
remainingTargetSz -= sz
110125
delta = delta[sz:]
111126
} else {
112-
return nil, ErrDeltaCmd
127+
return ErrDeltaCmd
113128
}
114129

115130
if remainingTargetSz <= 0 {
116131
break
117132
}
118133
}
119134

120-
return dest, nil
135+
return nil
121136
}
122137

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

0 commit comments

Comments
 (0)