aboutsummaryrefslogtreecommitdiffstats
path: root/storage/filesystem/object.go
diff options
context:
space:
mode:
Diffstat (limited to 'storage/filesystem/object.go')
-rw-r--r--storage/filesystem/object.go121
1 files changed, 110 insertions, 11 deletions
diff --git a/storage/filesystem/object.go b/storage/filesystem/object.go
index e235b33..5073a38 100644
--- a/storage/filesystem/object.go
+++ b/storage/filesystem/object.go
@@ -5,6 +5,7 @@ import (
"os"
"gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/cache"
"gopkg.in/src-d/go-git.v4/plumbing/format/idxfile"
"gopkg.in/src-d/go-git.v4/plumbing/format/objfile"
"gopkg.in/src-d/go-git.v4/plumbing/format/packfile"
@@ -16,14 +17,20 @@ import (
"gopkg.in/src-d/go-billy.v3"
)
+const DefaultMaxDeltaBaseCacheSize = 92 * cache.MiByte
+
type ObjectStorage struct {
+ // DeltaBaseCache is an object cache uses to cache delta's bases when
+ DeltaBaseCache cache.Object
+
dir *dotgit.DotGit
index map[plumbing.Hash]*packfile.Index
}
func newObjectStorage(dir *dotgit.DotGit) (ObjectStorage, error) {
s := ObjectStorage{
- dir: dir,
+ DeltaBaseCache: cache.NewObjectLRU(DefaultMaxDeltaBaseCacheSize),
+ dir: dir,
}
return s, nil
@@ -123,7 +130,27 @@ func (s *ObjectStorage) SetEncodedObject(o plumbing.EncodedObject) (plumbing.Has
func (s *ObjectStorage) EncodedObject(t plumbing.ObjectType, h plumbing.Hash) (plumbing.EncodedObject, error) {
obj, err := s.getFromUnpacked(h)
if err == plumbing.ErrObjectNotFound {
- obj, err = s.getFromPackfile(h)
+ obj, err = s.getFromPackfile(h, false)
+ }
+
+ if err != nil {
+ return nil, err
+ }
+
+ if plumbing.AnyObject != t && obj.Type() != t {
+ return nil, plumbing.ErrObjectNotFound
+ }
+
+ return obj, nil
+}
+
+// DeltaObject returns the object with the given hash, by searching for
+// it in the packfile and the git object directories.
+func (s *ObjectStorage) DeltaObject(t plumbing.ObjectType,
+ h plumbing.Hash) (plumbing.EncodedObject, error) {
+ obj, err := s.getFromUnpacked(h)
+ if err == plumbing.ErrObjectNotFound {
+ obj, err = s.getFromPackfile(h, true)
}
if err != nil {
@@ -175,12 +202,14 @@ func (s *ObjectStorage) getFromUnpacked(h plumbing.Hash) (obj plumbing.EncodedOb
// Get returns the object with the given hash, by searching for it in
// the packfile.
-func (s *ObjectStorage) getFromPackfile(h plumbing.Hash) (plumbing.EncodedObject, error) {
+func (s *ObjectStorage) getFromPackfile(h plumbing.Hash, canBeDelta bool) (
+ plumbing.EncodedObject, error) {
+
if err := s.requireIndex(); err != nil {
return nil, err
}
- pack, offset := s.findObjectInPackfile(h)
+ pack, hash, offset := s.findObjectInPackfile(h)
if offset == -1 {
return nil, plumbing.ErrObjectNotFound
}
@@ -192,25 +221,94 @@ func (s *ObjectStorage) getFromPackfile(h plumbing.Hash) (plumbing.EncodedObject
defer ioutil.CheckClose(f, &err)
+ idx := s.index[pack]
+ if canBeDelta {
+ return s.decodeDeltaObjectAt(f, idx, offset, hash)
+ }
+
+ return s.decodeObjectAt(f, idx, offset)
+}
+
+func (s *ObjectStorage) decodeObjectAt(
+ f billy.File,
+ idx *packfile.Index,
+ offset int64) (plumbing.EncodedObject, error) {
+ if _, err := f.Seek(0, io.SeekStart); err != nil {
+ return nil, err
+ }
+
p := packfile.NewScanner(f)
+
d, err := packfile.NewDecoder(p, memory.NewStorage())
if err != nil {
return nil, err
}
- d.SetIndex(s.index[pack])
+ d.SetIndex(idx)
+ d.DeltaBaseCache = s.DeltaBaseCache
obj, err := d.DecodeObjectAt(offset)
return obj, err
}
-func (s *ObjectStorage) findObjectInPackfile(h plumbing.Hash) (plumbing.Hash, int64) {
+func (s *ObjectStorage) decodeDeltaObjectAt(
+ f billy.File,
+ idx *packfile.Index,
+ offset int64,
+ hash plumbing.Hash) (plumbing.EncodedObject, error) {
+ if _, err := f.Seek(0, io.SeekStart); err != nil {
+ return nil, err
+ }
+
+ p := packfile.NewScanner(f)
+ if _, err := p.SeekFromStart(offset); err != nil {
+ return nil, err
+ }
+
+ header, err := p.NextObjectHeader()
+ if err != nil {
+ return nil, err
+ }
+
+ var (
+ base plumbing.Hash
+ )
+
+ switch header.Type {
+ case plumbing.REFDeltaObject:
+ base = header.Reference
+ case plumbing.OFSDeltaObject:
+ e, ok := idx.LookupOffset(uint64(header.OffsetReference))
+ if !ok {
+ return nil, plumbing.ErrObjectNotFound
+ }
+
+ base = e.Hash
+ default:
+ return s.decodeObjectAt(f, idx, offset)
+ }
+
+ obj := &plumbing.MemoryObject{}
+ obj.SetType(header.Type)
+ w, err := obj.Writer()
+ if err != nil {
+ return nil, err
+ }
+
+ if _, _, err := p.NextObject(w); err != nil {
+ return nil, err
+ }
+
+ return newDeltaObject(obj, hash, base, header.Length), nil
+}
+
+func (s *ObjectStorage) findObjectInPackfile(h plumbing.Hash) (plumbing.Hash, plumbing.Hash, int64) {
for packfile, index := range s.index {
if e, ok := index.LookupHash(h); ok {
- return packfile, int64(e.Offset)
+ return packfile, e.Hash, int64(e.Offset)
}
}
- return plumbing.ZeroHash, -1
+ return plumbing.ZeroHash, plumbing.ZeroHash, -1
}
// IterEncodedObjects returns an iterator for all the objects in the packfile
@@ -254,7 +352,7 @@ func (s *ObjectStorage) buildPackfileIters(t plumbing.ObjectType, seen map[plumb
return nil, err
}
- iter, err := newPackfileIter(pack, t, seen, s.index[h])
+ iter, err := newPackfileIter(pack, t, seen, s.index[h], s.DeltaBaseCache)
if err != nil {
return nil, err
}
@@ -276,11 +374,11 @@ type packfileIter struct {
}
func NewPackfileIter(f billy.File, t plumbing.ObjectType) (storer.EncodedObjectIter, error) {
- return newPackfileIter(f, t, make(map[plumbing.Hash]bool), nil)
+ return newPackfileIter(f, t, make(map[plumbing.Hash]bool), nil, nil)
}
func newPackfileIter(f billy.File, t plumbing.ObjectType, seen map[plumbing.Hash]bool,
- index *packfile.Index) (storer.EncodedObjectIter, error) {
+ index *packfile.Index, cache cache.Object) (storer.EncodedObjectIter, error) {
s := packfile.NewScanner(f)
_, total, err := s.Header()
if err != nil {
@@ -293,6 +391,7 @@ func newPackfileIter(f billy.File, t plumbing.ObjectType, seen map[plumbing.Hash
}
d.SetIndex(index)
+ d.DeltaBaseCache = cache
return &packfileIter{
f: f,