@@ -76,20 +76,18 @@ 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 )
86
- if err == nil {
87
- if obj , ok := p .deltaBaseCache .Get (hash ); ok {
88
- return obj , nil
89
- }
86
+ if err != nil {
87
+ return nil , err
90
88
}
91
89
92
- return p .objectAtOffset (o )
90
+ return p .objectAtOffset (o , hash )
93
91
}
94
92
95
93
// GetSizeByOffset retrieves the size of the encoded object from the
@@ -122,6 +120,13 @@ func (p *Packfile) nextObjectHeader() (*ObjectHeader, error) {
122
120
return h , err
123
121
}
124
122
123
+ func (p * Packfile ) getDeltaObjectSize (buf * bytes.Buffer ) int64 {
124
+ delta := buf .Bytes ()
125
+ _ , delta = decodeLEB128 (delta ) // skip src size
126
+ sz , _ := decodeLEB128 (delta )
127
+ return int64 (sz )
128
+ }
129
+
125
130
func (p * Packfile ) getObjectSize (h * ObjectHeader ) (int64 , error ) {
126
131
switch h .Type {
127
132
case plumbing .CommitObject , plumbing .TreeObject , plumbing .BlobObject , plumbing .TagObject :
@@ -135,10 +140,7 @@ func (p *Packfile) getObjectSize(h *ObjectHeader) (int64, error) {
135
140
return 0 , err
136
141
}
137
142
138
- delta := buf .Bytes ()
139
- _ , delta = decodeLEB128 (delta ) // skip src size
140
- sz , _ := decodeLEB128 (delta )
141
- return int64 (sz ), nil
143
+ return p .getDeltaObjectSize (buf ), nil
142
144
default :
143
145
return 0 , ErrInvalidObject .AddDetails ("type %q" , h .Type )
144
146
}
@@ -176,10 +178,16 @@ func (p *Packfile) getObjectType(h *ObjectHeader) (typ plumbing.ObjectType, err
176
178
err = ErrInvalidObject .AddDetails ("type %q" , h .Type )
177
179
}
178
180
181
+ p .offsetToType [h .Offset ] = typ
182
+
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 ) {
187
+ if obj , ok := p .cacheGet (hash ); ok {
188
+ return obj , nil
189
+ }
190
+
183
191
h , err := p .objectHeaderAtOffset (offset )
184
192
if err != nil {
185
193
if err == io .EOF || isInvalid (err ) {
@@ -188,27 +196,54 @@ func (p *Packfile) objectAtOffset(offset int64) (plumbing.EncodedObject, error)
188
196
return nil , err
189
197
}
190
198
199
+ return p .getNextObject (h , hash )
200
+ }
201
+
202
+ func (p * Packfile ) getNextObject (h * ObjectHeader , hash plumbing.Hash ) (plumbing.EncodedObject , error ) {
203
+ var err error
204
+
191
205
// If we have no filesystem, we will return a MemoryObject instead
192
206
// of an FSObject.
193
207
if p .fs == nil {
194
- return p .getNextObject (h )
208
+ return p .getNextMemoryObject (h )
195
209
}
196
210
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
- }
211
+ // If the object is small enough then read it completely into memory now since
212
+ // it is already read from disk into buffer anyway. For delta objects we want
213
+ // to perform the optimization too, but we have to be careful about applying
214
+ // small deltas on big objects.
215
+ var size int64
216
+ if h .Length <= smallObjectThreshold {
217
+ if h .Type != plumbing .OFSDeltaObject && h .Type != plumbing .REFDeltaObject {
218
+ return p .getNextMemoryObject (h )
219
+ }
203
220
204
- hash , err := p .FindHash (h .Offset )
205
- if err != nil {
206
- return nil , err
207
- }
221
+ // For delta objects we read the delta data and apply the small object
222
+ // optimization only if the expanded version of the object still meets
223
+ // the small object threshold condition.
224
+ buf := bufPool .Get ().(* bytes.Buffer )
225
+ buf .Reset ()
226
+ if _ , _ , err := p .s .NextObject (buf ); err != nil {
227
+ return nil , err
228
+ }
229
+ defer bufPool .Put (buf )
208
230
209
- size , err := p .getObjectSize (h )
210
- if err != nil {
211
- return nil , err
231
+ size = p .getDeltaObjectSize (buf )
232
+ if size <= smallObjectThreshold {
233
+ var obj = new (plumbing.MemoryObject )
234
+ obj .SetSize (size )
235
+ if h .Type == plumbing .REFDeltaObject {
236
+ err = p .fillREFDeltaObjectContentWithBuffer (obj , h .Reference , buf )
237
+ } else {
238
+ err = p .fillOFSDeltaObjectContentWithBuffer (obj , h .OffsetReference , buf )
239
+ }
240
+ return obj , err
241
+ }
242
+ } else {
243
+ size , err = p .getObjectSize (h )
244
+ if err != nil {
245
+ return nil , err
246
+ }
212
247
}
213
248
214
249
typ , err := p .getObjectType (h )
@@ -231,33 +266,22 @@ func (p *Packfile) objectAtOffset(offset int64) (plumbing.EncodedObject, error)
231
266
}
232
267
233
268
func (p * Packfile ) getObjectContent (offset int64 ) (io.ReadCloser , error ) {
234
- ref , err := p .FindHash (offset )
235
- if err == nil {
236
- obj , ok := p .cacheGet (ref )
237
- if ok {
238
- reader , err := obj .Reader ()
239
- if err != nil {
240
- return nil , err
241
- }
242
-
243
- return reader , nil
244
- }
245
- }
246
-
247
269
h , err := p .objectHeaderAtOffset (offset )
248
270
if err != nil {
249
271
return nil , err
250
272
}
251
273
252
- obj , err := p .getNextObject (h )
274
+ // getObjectContent is called from FSObject, so we have to explicitly
275
+ // get memory object here to avoid recursive cycle
276
+ obj , err := p .getNextMemoryObject (h )
253
277
if err != nil {
254
278
return nil , err
255
279
}
256
280
257
281
return obj .Reader ()
258
282
}
259
283
260
- func (p * Packfile ) getNextObject (h * ObjectHeader ) (plumbing.EncodedObject , error ) {
284
+ func (p * Packfile ) getNextMemoryObject (h * ObjectHeader ) (plumbing.EncodedObject , error ) {
261
285
var obj = new (plumbing.MemoryObject )
262
286
obj .SetSize (h .Length )
263
287
obj .SetType (h .Type )
@@ -278,6 +302,8 @@ func (p *Packfile) getNextObject(h *ObjectHeader) (plumbing.EncodedObject, error
278
302
return nil , err
279
303
}
280
304
305
+ p .offsetToType [h .Offset ] = obj .Type ()
306
+
281
307
return obj , nil
282
308
}
283
309
@@ -300,6 +326,13 @@ func (p *Packfile) fillREFDeltaObjectContent(obj plumbing.EncodedObject, ref plu
300
326
if err != nil {
301
327
return err
302
328
}
329
+ defer bufPool .Put (buf )
330
+
331
+ return p .fillREFDeltaObjectContentWithBuffer (obj , ref , buf )
332
+ }
333
+
334
+ func (p * Packfile ) fillREFDeltaObjectContentWithBuffer (obj plumbing.EncodedObject , ref plumbing.Hash , buf * bytes.Buffer ) error {
335
+ var err error
303
336
304
337
base , ok := p .cacheGet (ref )
305
338
if ! ok {
@@ -312,30 +345,31 @@ func (p *Packfile) fillREFDeltaObjectContent(obj plumbing.EncodedObject, ref plu
312
345
obj .SetType (base .Type ())
313
346
err = ApplyDelta (obj , base , buf .Bytes ())
314
347
p .cachePut (obj )
315
- bufPool .Put (buf )
316
348
317
349
return err
318
350
}
319
351
320
352
func (p * Packfile ) fillOFSDeltaObjectContent (obj plumbing.EncodedObject , offset int64 ) error {
321
- buf := bytes .NewBuffer (nil )
353
+ buf := bufPool .Get ().(* bytes.Buffer )
354
+ buf .Reset ()
322
355
_ , _ , err := p .s .NextObject (buf )
323
356
if err != nil {
324
357
return err
325
358
}
359
+ defer bufPool .Put (buf )
326
360
327
- var base plumbing.EncodedObject
328
- var ok bool
361
+ return p .fillOFSDeltaObjectContentWithBuffer (obj , offset , buf )
362
+ }
363
+
364
+ func (p * Packfile ) fillOFSDeltaObjectContentWithBuffer (obj plumbing.EncodedObject , offset int64 , buf * bytes.Buffer ) error {
329
365
hash , err := p .FindHash (offset )
330
- if err = = nil {
331
- base , ok = p . cacheGet ( hash )
366
+ if err ! = nil {
367
+ return err
332
368
}
333
369
334
- if ! ok {
335
- base , err = p .GetByOffset (offset )
336
- if err != nil {
337
- return err
338
- }
370
+ base , err := p .objectAtOffset (offset , hash )
371
+ if err != nil {
372
+ return err
339
373
}
340
374
341
375
obj .SetType (base .Type ())
@@ -442,14 +476,50 @@ func (i *objectIter) Next() (plumbing.EncodedObject, error) {
442
476
return nil , err
443
477
}
444
478
445
- obj , err := i .p .GetByOffset (int64 (e .Offset ))
479
+ if i .typ != plumbing .AnyObject {
480
+ if typ , ok := i .p .offsetToType [int64 (e .Offset )]; ok {
481
+ if typ != i .typ {
482
+ continue
483
+ }
484
+ } else if obj , ok := i .p .cacheGet (e .Hash ); ok {
485
+ if obj .Type () != i .typ {
486
+ i .p .offsetToType [int64 (e .Offset )] = obj .Type ()
487
+ continue
488
+ }
489
+ return obj , nil
490
+ } else {
491
+ h , err := i .p .objectHeaderAtOffset (int64 (e .Offset ))
492
+ if err != nil {
493
+ return nil , err
494
+ }
495
+
496
+ if h .Type == plumbing .REFDeltaObject || h .Type == plumbing .OFSDeltaObject {
497
+ typ , err := i .p .getObjectType (h )
498
+ if err != nil {
499
+ return nil , err
500
+ }
501
+ if typ != i .typ {
502
+ i .p .offsetToType [int64 (e .Offset )] = typ
503
+ continue
504
+ }
505
+ // getObjectType will seek in the file so we cannot use getNextObject safely
506
+ return i .p .objectAtOffset (int64 (e .Offset ), e .Hash )
507
+ } else {
508
+ if h .Type != i .typ {
509
+ i .p .offsetToType [int64 (e .Offset )] = h .Type
510
+ continue
511
+ }
512
+ return i .p .getNextObject (h , e .Hash )
513
+ }
514
+ }
515
+ }
516
+
517
+ obj , err := i .p .objectAtOffset (int64 (e .Offset ), e .Hash )
446
518
if err != nil {
447
519
return nil , err
448
520
}
449
521
450
- if i .typ == plumbing .AnyObject || obj .Type () == i .typ {
451
- return obj , nil
452
- }
522
+ return obj , nil
453
523
}
454
524
}
455
525
0 commit comments