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

Commit 1e1315d

Browse files
committed
object: get object size without reading whole object
Signed-off-by: Jeremy Stribling <strib@alum.mit.edu>
1 parent 0bfe038 commit 1e1315d

File tree

6 files changed

+137
-0
lines changed

6 files changed

+137
-0
lines changed

plumbing/format/packfile/packfile.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,22 @@ func (p *Packfile) GetByOffset(o int64) (plumbing.EncodedObject, error) {
9090
return p.nextObject()
9191
}
9292

93+
func (p *Packfile) GetSizeByOffset(o int64) (size int64, err error) {
94+
if _, err := p.s.SeekFromStart(o); err != nil {
95+
if err == io.EOF || isInvalid(err) {
96+
return 0, plumbing.ErrObjectNotFound
97+
}
98+
99+
return 0, err
100+
}
101+
102+
h, err := p.nextObjectHeader()
103+
if err != nil {
104+
return 0, err
105+
}
106+
return h.Length, nil
107+
}
108+
93109
func (p *Packfile) nextObjectHeader() (*ObjectHeader, error) {
94110
h, err := p.s.NextObjectHeader()
95111
p.s.pendingObject = nil

plumbing/storer/object.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ type EncodedObjectStorer interface {
4040
// HasEncodedObject returns ErrObjNotFound if the object doesn't
4141
// exist. If the object does exist, it returns nil.
4242
HasEncodedObject(plumbing.Hash) error
43+
// EncodedObjectSize returns the plaintext size of the encoded object.
44+
EncodedObjectSize(plumbing.Hash) (int64, error)
4345
}
4446

4547
// DeltaObjectStorer is an EncodedObjectStorer that can return delta

plumbing/storer/object_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,16 @@ func (o *MockObjectStorage) HasEncodedObject(h plumbing.Hash) error {
141141
return plumbing.ErrObjectNotFound
142142
}
143143

144+
func (o *MockObjectStorage) EncodedObjectSize(h plumbing.Hash) (
145+
size int64, err error) {
146+
for _, o := range o.db {
147+
if o.Hash() == h {
148+
return o.Size(), nil
149+
}
150+
}
151+
return 0, plumbing.ErrObjectNotFound
152+
}
153+
144154
func (o *MockObjectStorage) EncodedObject(t plumbing.ObjectType, h plumbing.Hash) (plumbing.EncodedObject, error) {
145155
for _, o := range o.db {
146156
if o.Hash() == h {

storage/filesystem/object.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,81 @@ func (s *ObjectStorage) HasEncodedObject(h plumbing.Hash) (err error) {
160160
return nil
161161
}
162162

163+
func (s *ObjectStorage) encodedObjectSizeFromUnpacked(h plumbing.Hash) (
164+
size int64, err error) {
165+
f, err := s.dir.Object(h)
166+
if err != nil {
167+
if os.IsNotExist(err) {
168+
return 0, plumbing.ErrObjectNotFound
169+
}
170+
171+
return 0, err
172+
}
173+
174+
r, err := objfile.NewReader(f)
175+
if err != nil {
176+
return 0, err
177+
}
178+
defer ioutil.CheckClose(r, &err)
179+
180+
_, size, err = r.Header()
181+
return size, err
182+
}
183+
184+
func (s *ObjectStorage) encodedObjectSizeFromPackfile(h plumbing.Hash) (
185+
size int64, err error) {
186+
if err := s.requireIndex(); err != nil {
187+
return 0, err
188+
}
189+
190+
pack, _, offset := s.findObjectInPackfile(h)
191+
if offset == -1 {
192+
return 0, plumbing.ErrObjectNotFound
193+
}
194+
195+
f, err := s.dir.ObjectPack(pack)
196+
if err != nil {
197+
return 0, err
198+
}
199+
defer ioutil.CheckClose(f, &err)
200+
201+
idx := s.index[pack]
202+
hash, err := idx.FindHash(offset)
203+
if err == nil {
204+
obj, ok := s.deltaBaseCache.Get(hash)
205+
if ok {
206+
return obj.Size(), nil
207+
}
208+
}
209+
210+
if err != nil && err != plumbing.ErrObjectNotFound {
211+
return 0, err
212+
}
213+
214+
var p *packfile.Packfile
215+
if s.deltaBaseCache != nil {
216+
p = packfile.NewPackfileWithCache(idx, s.dir.Fs(), f, s.deltaBaseCache)
217+
} else {
218+
p = packfile.NewPackfile(idx, s.dir.Fs(), f)
219+
}
220+
221+
return p.GetSizeByOffset(offset)
222+
}
223+
224+
// EncodedObjectSize returns the plaintext size of the given object,
225+
// without actually reading the full object data from storage.
226+
func (s *ObjectStorage) EncodedObjectSize(h plumbing.Hash) (
227+
size int64, err error) {
228+
size, err = s.encodedObjectSizeFromUnpacked(h)
229+
if err != nil && err != plumbing.ErrObjectNotFound {
230+
return 0, err
231+
} else if err == nil {
232+
return size, nil
233+
}
234+
235+
return s.encodedObjectSizeFromPackfile(h)
236+
}
237+
163238
// EncodedObject returns the object with the given hash, by searching for it in
164239
// the packfile and the git object directories.
165240
func (s *ObjectStorage) EncodedObject(t plumbing.ObjectType, h plumbing.Hash) (plumbing.EncodedObject, error) {

storage/filesystem/object_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,30 @@ func (s *FsSuite) TestGetFromPackfileKeepDescriptors(c *C) {
8383
})
8484
}
8585

86+
func (s *FsSuite) TestGetSizeOfObjectFile(c *C) {
87+
fs := fixtures.ByTag(".git").ByTag("unpacked").One().DotGit()
88+
o := NewObjectStorage(dotgit.New(fs), cache.NewObjectLRUDefault())
89+
90+
// Get the size of `tree_walker.go`.
91+
expected := plumbing.NewHash("cbd81c47be12341eb1185b379d1c82675aeded6a")
92+
size, err := o.EncodedObjectSize(expected)
93+
c.Assert(err, IsNil)
94+
c.Assert(size, Equals, int64(2412))
95+
}
96+
97+
func (s *FsSuite) TestGetSizeFromPackfile(c *C) {
98+
fixtures.Basic().ByTag(".git").Test(c, func(f *fixtures.Fixture) {
99+
fs := f.DotGit()
100+
o := NewObjectStorage(dotgit.New(fs), cache.NewObjectLRUDefault())
101+
102+
// Get the size of `binary.jpg`.
103+
expected := plumbing.NewHash("d5c0f4ab811897cadf03aec358ae60d21f91c50d")
104+
size, err := o.EncodedObjectSize(expected)
105+
c.Assert(err, IsNil)
106+
c.Assert(size, Equals, int64(76110))
107+
})
108+
}
109+
86110
func (s *FsSuite) TestGetFromPackfileMultiplePackfiles(c *C) {
87111
fs := fixtures.ByTag(".git").ByTag("multi-packfile").One().DotGit()
88112
o := NewObjectStorage(dotgit.New(fs), cache.NewObjectLRUDefault())

storage/memory/storage.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,16 @@ func (o *ObjectStorage) HasEncodedObject(h plumbing.Hash) (err error) {
122122
return nil
123123
}
124124

125+
func (o *ObjectStorage) EncodedObjectSize(h plumbing.Hash) (
126+
size int64, err error) {
127+
obj, ok := o.Objects[h]
128+
if !ok {
129+
return 0, plumbing.ErrObjectNotFound
130+
}
131+
132+
return obj.Size(), nil
133+
}
134+
125135
func (o *ObjectStorage) EncodedObject(t plumbing.ObjectType, h plumbing.Hash) (plumbing.EncodedObject, error) {
126136
obj, ok := o.Objects[h]
127137
if !ok || (plumbing.AnyObject != t && obj.Type() != t) {

0 commit comments

Comments
 (0)