@@ -76,10 +76,10 @@ func (p *Packfile) Get(h plumbing.Hash) (plumbing.EncodedObject, error) {
76
76
return nil , err
77
77
}
78
78
79
- return p .GetByOffset (offset )
79
+ return p .objectAtOffset (offset , h )
80
80
}
81
81
82
- // GetByOffset retrieves the encoded object from the packfile with the given
82
+ // GetByOffset retrieves the encoded object from the packfile at the given
83
83
// offset.
84
84
func (p * Packfile ) GetByOffset (o int64 ) (plumbing.EncodedObject , error ) {
85
85
hash , err := p .FindHash (o )
@@ -89,7 +89,7 @@ func (p *Packfile) GetByOffset(o int64) (plumbing.EncodedObject, error) {
89
89
}
90
90
}
91
91
92
- return p .objectAtOffset (o )
92
+ return p .objectAtOffset (o , hash )
93
93
}
94
94
95
95
// GetSizeByOffset retrieves the size of the encoded object from the
@@ -122,6 +122,13 @@ func (p *Packfile) nextObjectHeader() (*ObjectHeader, error) {
122
122
return h , err
123
123
}
124
124
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
+
125
132
func (p * Packfile ) getObjectSize (h * ObjectHeader ) (int64 , error ) {
126
133
switch h .Type {
127
134
case plumbing .CommitObject , plumbing .TreeObject , plumbing .BlobObject , plumbing .TagObject :
@@ -135,10 +142,7 @@ func (p *Packfile) getObjectSize(h *ObjectHeader) (int64, error) {
135
142
return 0 , err
136
143
}
137
144
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
142
146
default :
143
147
return 0 , ErrInvalidObject .AddDetails ("type %q" , h .Type )
144
148
}
@@ -179,7 +183,7 @@ func (p *Packfile) getObjectType(h *ObjectHeader) (typ plumbing.ObjectType, err
179
183
return
180
184
}
181
185
182
- func (p * Packfile ) objectAtOffset (offset int64 ) (plumbing.EncodedObject , error ) {
186
+ func (p * Packfile ) objectAtOffset (offset int64 , hash plumbing. Hash ) (plumbing.EncodedObject , error ) {
183
187
h , err := p .objectHeaderAtOffset (offset )
184
188
if err != nil {
185
189
if err == io .EOF || isInvalid (err ) {
@@ -194,21 +198,42 @@ func (p *Packfile) objectAtOffset(offset int64) (plumbing.EncodedObject, error)
194
198
return p .getNextObject (h )
195
199
}
196
200
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
+ }
203
210
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 )
208
220
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
+ }
212
237
}
213
238
214
239
typ , err := p .getObjectType (h )
@@ -300,6 +325,13 @@ func (p *Packfile) fillREFDeltaObjectContent(obj plumbing.EncodedObject, ref plu
300
325
if err != nil {
301
326
return err
302
327
}
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
303
335
304
336
base , ok := p .cacheGet (ref )
305
337
if ! ok {
@@ -312,18 +344,24 @@ func (p *Packfile) fillREFDeltaObjectContent(obj plumbing.EncodedObject, ref plu
312
344
obj .SetType (base .Type ())
313
345
err = ApplyDelta (obj , base , buf .Bytes ())
314
346
p .cachePut (obj )
315
- bufPool .Put (buf )
316
347
317
348
return err
318
349
}
319
350
320
351
func (p * Packfile ) fillOFSDeltaObjectContent (obj plumbing.EncodedObject , offset int64 ) error {
321
352
buf := bytes .NewBuffer (nil )
353
+ buf .Reset ()
322
354
_ , _ , err := p .s .NextObject (buf )
323
355
if err != nil {
324
356
return err
325
357
}
358
+ defer bufPool .Put (buf )
326
359
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
327
365
var base plumbing.EncodedObject
328
366
var ok bool
329
367
hash , err := p .FindHash (offset )
@@ -332,7 +370,7 @@ func (p *Packfile) fillOFSDeltaObjectContent(obj plumbing.EncodedObject, offset
332
370
}
333
371
334
372
if ! ok {
335
- base , err = p .GetByOffset (offset )
373
+ base , err = p .objectAtOffset (offset , hash )
336
374
if err != nil {
337
375
return err
338
376
}
0 commit comments