@@ -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.
8484func (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+
125132func (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
320351func (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