diff options
Diffstat (limited to 'storage/filesystem/object.go')
-rw-r--r-- | storage/filesystem/object.go | 121 |
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, |