Skip to content

Commit 883c226

Browse files
authored
Merge pull request go-git#1180 from orisano/feat-improve-patch-delta
feat: improve patch delta performance
2 parents 9feb9fe + d456ce9 commit 883c226

File tree

2 files changed

+89
-63
lines changed

2 files changed

+89
-63
lines changed

plumbing/format/packfile/parser.go

+53-46
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"errors"
66
"io"
7+
"io/ioutil"
78

89
"gopkg.in/src-d/go-git.v4/plumbing"
910
"gopkg.in/src-d/go-git.v4/plumbing/cache"
@@ -263,11 +264,14 @@ func (p *Parser) indexObjects() error {
263264
}
264265

265266
func (p *Parser) resolveDeltas() error {
267+
buf := &bytes.Buffer{}
266268
for _, obj := range p.oi {
267-
content, err := p.get(obj)
269+
buf.Reset()
270+
err := p.get(obj, buf)
268271
if err != nil {
269272
return err
270273
}
274+
content := buf.Bytes()
271275

272276
if err := p.onInflatedObjectHeader(obj.Type, obj.Length, obj.Offset); err != nil {
273277
return err
@@ -279,7 +283,7 @@ func (p *Parser) resolveDeltas() error {
279283

280284
if !obj.IsDelta() && len(obj.Children) > 0 {
281285
for _, child := range obj.Children {
282-
if _, err := p.resolveObject(child, content); err != nil {
286+
if err := p.resolveObject(ioutil.Discard, child, content); err != nil {
283287
return err
284288
}
285289
}
@@ -294,120 +298,123 @@ func (p *Parser) resolveDeltas() error {
294298
return nil
295299
}
296300

297-
func (p *Parser) get(o *objectInfo) (b []byte, err error) {
298-
var ok bool
301+
func (p *Parser) get(o *objectInfo, buf *bytes.Buffer) error {
299302
if !o.ExternalRef { // skip cache check for placeholder parents
300-
b, ok = p.cache.Get(o.Offset)
303+
b, ok := p.cache.Get(o.Offset)
304+
if ok {
305+
_, err := buf.Write(b)
306+
return err
307+
}
301308
}
302309

303310
// If it's not on the cache and is not a delta we can try to find it in the
304311
// storage, if there's one. External refs must enter here.
305-
if !ok && p.storage != nil && !o.Type.IsDelta() {
312+
if p.storage != nil && !o.Type.IsDelta() {
306313
e, err := p.storage.EncodedObject(plumbing.AnyObject, o.SHA1)
307314
if err != nil {
308-
return nil, err
315+
return err
309316
}
310317
o.Type = e.Type()
311318

312319
r, err := e.Reader()
313320
if err != nil {
314-
return nil, err
315-
}
316-
317-
b = make([]byte, e.Size())
318-
if _, err = r.Read(b); err != nil {
319-
return nil, err
321+
return err
320322
}
321-
}
322323

323-
if b != nil {
324-
return b, nil
324+
_, err = buf.ReadFrom(io.LimitReader(r, e.Size()))
325+
return err
325326
}
326327

327328
if o.ExternalRef {
328329
// we were not able to resolve a ref in a thin pack
329-
return nil, ErrReferenceDeltaNotFound
330+
return ErrReferenceDeltaNotFound
330331
}
331332

332-
var data []byte
333333
if o.DiskType.IsDelta() {
334-
base, err := p.get(o.Parent)
334+
b := bufPool.Get().(*bytes.Buffer)
335+
defer bufPool.Put(b)
336+
b.Reset()
337+
err := p.get(o.Parent, b)
335338
if err != nil {
336-
return nil, err
339+
return err
337340
}
341+
base := b.Bytes()
338342

339-
data, err = p.resolveObject(o, base)
343+
err = p.resolveObject(buf, o, base)
340344
if err != nil {
341-
return nil, err
345+
return err
342346
}
343347
} else {
344-
data, err = p.readData(o)
348+
err := p.readData(buf, o)
345349
if err != nil {
346-
return nil, err
350+
return err
347351
}
348352
}
349353

350354
if len(o.Children) > 0 {
355+
data := make([]byte, buf.Len())
356+
copy(data, buf.Bytes())
351357
p.cache.Put(o.Offset, data)
352358
}
353-
354-
return data, nil
359+
return nil
355360
}
356361

357362
func (p *Parser) resolveObject(
363+
w io.Writer,
358364
o *objectInfo,
359365
base []byte,
360-
) ([]byte, error) {
366+
) error {
361367
if !o.DiskType.IsDelta() {
362-
return nil, nil
368+
return nil
363369
}
364-
365-
data, err := p.readData(o)
370+
buf := bufPool.Get().(*bytes.Buffer)
371+
defer bufPool.Put(buf)
372+
buf.Reset()
373+
err := p.readData(buf, o)
366374
if err != nil {
367-
return nil, err
375+
return err
368376
}
377+
data := buf.Bytes()
369378

370379
data, err = applyPatchBase(o, data, base)
371380
if err != nil {
372-
return nil, err
381+
return err
373382
}
374383

375384
if p.storage != nil {
376385
obj := new(plumbing.MemoryObject)
377386
obj.SetSize(o.Size())
378387
obj.SetType(o.Type)
379388
if _, err := obj.Write(data); err != nil {
380-
return nil, err
389+
return err
381390
}
382391

383392
if _, err := p.storage.SetEncodedObject(obj); err != nil {
384-
return nil, err
393+
return err
385394
}
386395
}
387-
388-
return data, nil
396+
_, err = w.Write(data)
397+
return err
389398
}
390399

391-
func (p *Parser) readData(o *objectInfo) ([]byte, error) {
400+
func (p *Parser) readData(w io.Writer, o *objectInfo) error {
392401
if !p.scanner.IsSeekable && o.DiskType.IsDelta() {
393402
data, ok := p.deltas[o.Offset]
394403
if !ok {
395-
return nil, ErrDeltaNotCached
404+
return ErrDeltaNotCached
396405
}
397-
398-
return data, nil
406+
_, err := w.Write(data)
407+
return err
399408
}
400409

401410
if _, err := p.scanner.SeekObjectHeader(o.Offset); err != nil {
402-
return nil, err
411+
return err
403412
}
404413

405-
buf := new(bytes.Buffer)
406-
if _, _, err := p.scanner.NextObject(buf); err != nil {
407-
return nil, err
414+
if _, _, err := p.scanner.NextObject(w); err != nil {
415+
return err
408416
}
409-
410-
return buf.Bytes(), nil
417+
return nil
411418
}
412419

413420
func applyPatchBase(ota *objectInfo, data, base []byte) ([]byte, error) {

plumbing/format/packfile/patch_delta.go

+36-17
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package packfile
22

33
import (
4+
"bytes"
45
"errors"
5-
"io/ioutil"
6+
"io"
67

78
"gopkg.in/src-d/go-git.v4/plumbing"
89
)
@@ -26,19 +27,29 @@ func ApplyDelta(target, base plumbing.EncodedObject, delta []byte) error {
2627
return err
2728
}
2829

29-
src, err := ioutil.ReadAll(r)
30+
buf := bufPool.Get().(*bytes.Buffer)
31+
defer bufPool.Put(buf)
32+
buf.Reset()
33+
_, err = buf.ReadFrom(r)
3034
if err != nil {
3135
return err
3236
}
37+
src := buf.Bytes()
3338

34-
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)
3543
if err != nil {
3644
return err
3745
}
3846

39-
target.SetSize(int64(len(dst)))
4047

41-
_, 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)
4253
return err
4354
}
4455

@@ -51,23 +62,31 @@ var (
5162
// An error will be returned if delta is corrupted (ErrDeltaLen) or an action command
5263
// is not copy from source or copy from delta (ErrDeltaCmd).
5364
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 {
5473
if len(delta) < deltaSizeMin {
55-
return nil, ErrInvalidDelta
74+
return ErrInvalidDelta
5675
}
5776

5877
srcSz, delta := decodeLEB128(delta)
5978
if srcSz != uint(len(src)) {
60-
return nil, ErrInvalidDelta
79+
return ErrInvalidDelta
6180
}
6281

6382
targetSz, delta := decodeLEB128(delta)
6483
remainingTargetSz := targetSz
6584

6685
var cmd byte
67-
dest := make([]byte, 0, targetSz)
86+
dst.Grow(int(targetSz))
6887
for {
6988
if len(delta) == 0 {
70-
return nil, ErrInvalidDelta
89+
return ErrInvalidDelta
7190
}
7291

7392
cmd = delta[0]
@@ -77,43 +96,43 @@ func PatchDelta(src, delta []byte) ([]byte, error) {
7796
var err error
7897
offset, delta, err = decodeOffset(cmd, delta)
7998
if err != nil {
80-
return nil, err
99+
return err
81100
}
82101

83102
sz, delta, err = decodeSize(cmd, delta)
84103
if err != nil {
85-
return nil, err
104+
return err
86105
}
87106

88107
if invalidSize(sz, targetSz) ||
89108
invalidOffsetSize(offset, sz, srcSz) {
90109
break
91110
}
92-
dest = append(dest, src[offset:offset+sz]...)
111+
dst.Write(src[offset:offset+sz])
93112
remainingTargetSz -= sz
94113
} else if isCopyFromDelta(cmd) {
95114
sz := uint(cmd) // cmd is the size itself
96115
if invalidSize(sz, targetSz) {
97-
return nil, ErrInvalidDelta
116+
return ErrInvalidDelta
98117
}
99118

100119
if uint(len(delta)) < sz {
101-
return nil, ErrInvalidDelta
120+
return ErrInvalidDelta
102121
}
103122

104-
dest = append(dest, delta[0:sz]...)
123+
dst.Write(delta[0:sz])
105124
remainingTargetSz -= sz
106125
delta = delta[sz:]
107126
} else {
108-
return nil, ErrDeltaCmd
127+
return ErrDeltaCmd
109128
}
110129

111130
if remainingTargetSz <= 0 {
112131
break
113132
}
114133
}
115134

116-
return dest, nil
135+
return nil
117136
}
118137

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

0 commit comments

Comments
 (0)