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

Commit ac0f634

Browse files
committed
plumbing: packfile, apply small object reading optimization also for delta objects
1 parent aa6f288 commit ac0f634

File tree

1 file changed

+61
-23
lines changed

1 file changed

+61
-23
lines changed

plumbing/format/packfile/packfile.go

Lines changed: 61 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,10 @@ func (p *Packfile) Get(h plumbing.Hash) (plumbing.EncodedObject, error) {
7676
return nil, err
7777
}
7878

79-
return p.GetByOffset(offset)
79+
return p.objectAtOffset(offset, h)
8080
}
8181

82-
// GetByOffset retrieves the encoded object from the packfile with the given
82+
// GetByOffset retrieves the encoded object from the packfile at the given
8383
// offset.
8484
func (p *Packfile) GetByOffset(o int64) (plumbing.EncodedObject, error) {
8585
hash, err := p.FindHash(o)
@@ -89,7 +89,7 @@ func (p *Packfile) GetByOffset(o int64) (plumbing.EncodedObject, error) {
8989
}
9090
}
9191

92-
return p.objectAtOffset(o)
92+
return p.objectAtOffset(o, hash)
9393
}
9494

9595
// GetSizeByOffset retrieves the size of the encoded object from the
@@ -122,6 +122,13 @@ func (p *Packfile) nextObjectHeader() (*ObjectHeader, error) {
122122
return h, err
123123
}
124124

125+
func (p *Packfile) getDeltaObjectSize(buf *bytes.Buffer) int64 {
126+
delta := buf.Bytes()
127+
_, delta = decodeLEB128(delta) // skip src size
128+
sz, _ := decodeLEB128(delta)
129+
return int64(sz)
130+
}
131+
125132
func (p *Packfile) getObjectSize(h *ObjectHeader) (int64, error) {
126133
switch h.Type {
127134
case plumbing.CommitObject, plumbing.TreeObject, plumbing.BlobObject, plumbing.TagObject:
@@ -135,10 +142,7 @@ func (p *Packfile) getObjectSize(h *ObjectHeader) (int64, error) {
135142
return 0, err
136143
}
137144

138-
delta := buf.Bytes()
139-
_, delta = decodeLEB128(delta) // skip src size
140-
sz, _ := decodeLEB128(delta)
141-
return int64(sz), nil
145+
return p.getDeltaObjectSize(buf), nil
142146
default:
143147
return 0, ErrInvalidObject.AddDetails("type %q", h.Type)
144148
}
@@ -179,7 +183,7 @@ func (p *Packfile) getObjectType(h *ObjectHeader) (typ plumbing.ObjectType, err
179183
return
180184
}
181185

182-
func (p *Packfile) objectAtOffset(offset int64) (plumbing.EncodedObject, error) {
186+
func (p *Packfile) objectAtOffset(offset int64, hash plumbing.Hash) (plumbing.EncodedObject, error) {
183187
h, err := p.objectHeaderAtOffset(offset)
184188
if err != nil {
185189
if err == io.EOF || isInvalid(err) {
@@ -194,21 +198,42 @@ func (p *Packfile) objectAtOffset(offset int64) (plumbing.EncodedObject, error)
194198
return p.getNextObject(h)
195199
}
196200

197-
// If the object is not a delta and it's small enough then read it
198-
// completely into memory now since it is already read from disk
199-
// into buffer anyway.
200-
if h.Length <= smallObjectThreshold && h.Type != plumbing.OFSDeltaObject && h.Type != plumbing.REFDeltaObject {
201-
return p.getNextObject(h)
202-
}
201+
// If the object is small enough then read it completely into memory now since
202+
// it is already read from disk into buffer anyway. For delta objects we want
203+
// to perform the optimization too, but we have to be careful about applying
204+
// small deltas on big objects.
205+
var size int64
206+
if h.Length <= smallObjectThreshold {
207+
if h.Type != plumbing.OFSDeltaObject && h.Type != plumbing.REFDeltaObject {
208+
return p.getNextObject(h)
209+
}
203210

204-
hash, err := p.FindHash(h.Offset)
205-
if err != nil {
206-
return nil, err
207-
}
211+
// For delta objects we read the delta data and create a special object
212+
// that will hold them in memory and resolve them lazily to the referenced
213+
// object.
214+
buf := bufPool.Get().(*bytes.Buffer)
215+
buf.Reset()
216+
if _, _, err := p.s.NextObject(buf); err != nil {
217+
return nil, err
218+
}
219+
defer bufPool.Put(buf)
208220

209-
size, err := p.getObjectSize(h)
210-
if err != nil {
211-
return nil, err
221+
size = p.getDeltaObjectSize(buf)
222+
if size <= smallObjectThreshold {
223+
var obj = new(plumbing.MemoryObject)
224+
obj.SetSize(size)
225+
if h.Type == plumbing.REFDeltaObject {
226+
err = p.fillREFDeltaObjectContentWithBuffer(obj, h.Reference, buf)
227+
} else {
228+
err = p.fillOFSDeltaObjectContentWithBuffer(obj, h.OffsetReference, buf)
229+
}
230+
return obj, nil
231+
}
232+
} else {
233+
size, err = p.getObjectSize(h)
234+
if err != nil {
235+
return nil, err
236+
}
212237
}
213238

214239
typ, err := p.getObjectType(h)
@@ -300,6 +325,13 @@ func (p *Packfile) fillREFDeltaObjectContent(obj plumbing.EncodedObject, ref plu
300325
if err != nil {
301326
return err
302327
}
328+
defer bufPool.Put(buf)
329+
330+
return p.fillREFDeltaObjectContentWithBuffer(obj, ref, buf)
331+
}
332+
333+
func (p *Packfile) fillREFDeltaObjectContentWithBuffer(obj plumbing.EncodedObject, ref plumbing.Hash, buf *bytes.Buffer) error {
334+
var err error
303335

304336
base, ok := p.cacheGet(ref)
305337
if !ok {
@@ -312,18 +344,24 @@ func (p *Packfile) fillREFDeltaObjectContent(obj plumbing.EncodedObject, ref plu
312344
obj.SetType(base.Type())
313345
err = ApplyDelta(obj, base, buf.Bytes())
314346
p.cachePut(obj)
315-
bufPool.Put(buf)
316347

317348
return err
318349
}
319350

320351
func (p *Packfile) fillOFSDeltaObjectContent(obj plumbing.EncodedObject, offset int64) error {
321352
buf := bytes.NewBuffer(nil)
353+
buf.Reset()
322354
_, _, err := p.s.NextObject(buf)
323355
if err != nil {
324356
return err
325357
}
358+
defer bufPool.Put(buf)
326359

360+
return p.fillOFSDeltaObjectContentWithBuffer(obj, offset, buf)
361+
}
362+
363+
func (p *Packfile) fillOFSDeltaObjectContentWithBuffer(obj plumbing.EncodedObject, offset int64, buf *bytes.Buffer) error {
364+
var err error
327365
var base plumbing.EncodedObject
328366
var ok bool
329367
hash, err := p.FindHash(offset)
@@ -332,7 +370,7 @@ func (p *Packfile) fillOFSDeltaObjectContent(obj plumbing.EncodedObject, offset
332370
}
333371

334372
if !ok {
335-
base, err = p.GetByOffset(offset)
373+
base, err = p.objectAtOffset(offset, hash)
336374
if err != nil {
337375
return err
338376
}

0 commit comments

Comments
 (0)