diff options
author | Máximo Cuadros <mcuadros@gmail.com> | 2016-02-17 00:24:31 +0100 |
---|---|---|
committer | Máximo Cuadros <mcuadros@gmail.com> | 2016-02-17 11:46:00 +0100 |
commit | a964e32d92c53a47ce7c46d589a18c62133b8c50 (patch) | |
tree | 4c094cd6afb95cb2832f866b9411276418435819 | |
parent | 1ac00554c3b5f88d2ddc2e28e7cfcdcad9d9f4bb (diff) | |
download | go-git-a964e32d92c53a47ce7c46d589a18c62133b8c50.tar.gz |
storages: memory object
-rw-r--r-- | commit.go | 4 | ||||
-rw-r--r-- | core/hash.go | 3 | ||||
-rw-r--r-- | core/object.go | 87 | ||||
-rw-r--r-- | formats/packfile/reader.go | 4 | ||||
-rw-r--r-- | formats/packfile/reader_test.go | 11 | ||||
-rw-r--r-- | objects.go | 7 | ||||
-rw-r--r-- | objects_test.go | 3 | ||||
-rw-r--r-- | remote_test.go | 4 | ||||
-rw-r--r-- | repository.go | 3 | ||||
-rw-r--r-- | storage/memory/object.go | 55 | ||||
-rw-r--r-- | storage/memory/object_test.go | 67 | ||||
-rw-r--r-- | storage/memory/storage.go | 83 | ||||
-rw-r--r-- | storage/memory/storage_test.go | 51 | ||||
-rw-r--r-- | tree.go | 4 |
14 files changed, 289 insertions, 97 deletions
@@ -51,6 +51,10 @@ func (c *Commit) File(path string) (file *File, err error) { // Decode transform an core.Object into a Blob struct func (c *Commit) Decode(o core.Object) error { + if o.Type() != core.CommitObject { + return ErrUnsupportedObject + } + c.Hash = o.Hash() r := bufio.NewReader(o.Reader()) diff --git a/core/hash.go b/core/hash.go index 0b6f274..9b30b6e 100644 --- a/core/hash.go +++ b/core/hash.go @@ -9,6 +9,9 @@ import ( // Hash SHA1 hased content type Hash [20]byte +// ZeroHash is Hash with value zero +var ZeroHash Hash + // ComputeHash compute the hash for a given ObjectType and content func ComputeHash(t ObjectType, content []byte) Hash { h := t.Bytes() diff --git a/core/object.go b/core/object.go index 857c6df..e64f650 100644 --- a/core/object.go +++ b/core/object.go @@ -2,7 +2,6 @@ package core import ( - "bytes" "errors" "io" ) @@ -13,11 +12,11 @@ var ( // Object is a generic representation of any git object type Object interface { + Hash() Hash Type() ObjectType SetType(ObjectType) Size() int64 SetSize(int64) - Hash() Hash Reader() io.Reader Writer() io.Writer } @@ -147,87 +146,3 @@ func (iter *ObjectSliceIter) Next() (Object, error) { func (iter *ObjectSliceIter) Close() { iter.pos = len(iter.series) } - -type RAWObject struct { - b []byte - t ObjectType - s int64 -} - -func (o *RAWObject) Type() ObjectType { return o.t } -func (o *RAWObject) SetType(t ObjectType) { o.t = t } -func (o *RAWObject) Size() int64 { return o.s } -func (o *RAWObject) SetSize(s int64) { o.s = s } -func (o *RAWObject) Reader() io.Reader { return bytes.NewBuffer(o.b) } -func (o *RAWObject) Hash() Hash { return ComputeHash(o.t, o.b) } -func (o *RAWObject) Writer() io.Writer { return o } -func (o *RAWObject) Write(p []byte) (n int, err error) { - o.b = append(o.b, p...) - return len(p), nil -} - -type RAWObjectStorage struct { - Objects map[Hash]Object - Commits map[Hash]Object - Trees map[Hash]Object - Blobs map[Hash]Object -} - -func NewRAWObjectStorage() *RAWObjectStorage { - return &RAWObjectStorage{ - Objects: make(map[Hash]Object, 0), - Commits: make(map[Hash]Object, 0), - Trees: make(map[Hash]Object, 0), - Blobs: make(map[Hash]Object, 0), - } -} - -func (o *RAWObjectStorage) New() (Object, error) { - return &RAWObject{}, nil -} - -func (o *RAWObjectStorage) Set(obj Object) (Hash, error) { - h := obj.Hash() - o.Objects[h] = obj - - switch obj.Type() { - case CommitObject: - o.Commits[h] = o.Objects[h] - case TreeObject: - o.Trees[h] = o.Objects[h] - case BlobObject: - o.Blobs[h] = o.Objects[h] - } - - return h, nil -} - -func (o *RAWObjectStorage) Get(h Hash) (Object, error) { - obj, ok := o.Objects[h] - if !ok { - return nil, ObjectNotFoundErr - } - - return obj, nil -} - -func (o *RAWObjectStorage) Iter(t ObjectType) ObjectIter { - var series []Object - switch t { - case CommitObject: - series = flattenObjectMap(o.Commits) - case TreeObject: - series = flattenObjectMap(o.Trees) - case BlobObject: - series = flattenObjectMap(o.Blobs) - } - return NewObjectSliceIter(series) -} - -func flattenObjectMap(m map[Hash]Object) []Object { - objects := make([]Object, 0, len(m)) - for _, obj := range m { - objects = append(objects, obj) - } - return objects -} diff --git a/formats/packfile/reader.go b/formats/packfile/reader.go index 91aef21..e981d72 100644 --- a/formats/packfile/reader.go +++ b/formats/packfile/reader.go @@ -125,7 +125,7 @@ func (r *Reader) readObjects(count uint32) error { // of which 12-20 % is _not_ zlib inflation (ie. is our code). for i := 0; i < int(count); i++ { start := r.r.position - obj, err := r.newRAWObject() + obj, err := r.newObject() if err != nil && err != io.EOF { return err } @@ -143,7 +143,7 @@ func (r *Reader) readObjects(count uint32) error { return nil } -func (r *Reader) newRAWObject() (core.Object, error) { +func (r *Reader) newObject() (core.Object, error) { raw, err := r.s.New() if err != nil { return nil, err diff --git a/formats/packfile/reader_test.go b/formats/packfile/reader_test.go index f460614..69069c8 100644 --- a/formats/packfile/reader_test.go +++ b/formats/packfile/reader_test.go @@ -10,6 +10,7 @@ import ( "time" "gopkg.in/src-d/go-git.v2/core" + "gopkg.in/src-d/go-git.v2/storages/memory" "github.com/dustin/go-humanize" . "gopkg.in/check.v1" @@ -29,7 +30,7 @@ func (s *ReaderSuite) TestReadPackfile(c *C) { r := NewReader(d) - storage := core.NewRAWObjectStorage() + storage := memory.NewObjectStorage() _, err := r.Read(storage) c.Assert(err, IsNil) @@ -63,7 +64,7 @@ func (s *ReaderSuite) testReadPackfileGitFixture(c *C, file string, f Format) { r := NewReader(d) r.Format = f - storage := core.NewRAWObjectStorage() + storage := memory.NewObjectStorage() _, err = r.Read(storage) c.Assert(err, IsNil) @@ -99,7 +100,7 @@ func (s *ReaderSuite) testReadPackfileGitFixture(c *C, file string, f Format) { }) } -func AssertObjects(c *C, s *core.RAWObjectStorage, expects []string) { +func AssertObjects(c *C, s *memory.ObjectStorage, expects []string) { c.Assert(len(expects), Equals, len(s.Objects)) for _, expected := range expects { obtained, err := s.Get(core.NewHash(expected)) @@ -174,14 +175,14 @@ func (s *ReaderSuite) _TestMemoryREF(c *C) { fmt.Println("time", time.Since(start)) } -func readFromFile(c *C, file string, f Format) *core.RAWObjectStorage { +func readFromFile(c *C, file string, f Format) *memory.ObjectStorage { d, err := os.Open(file) c.Assert(err, IsNil) r := NewReader(d) r.Format = f - storage := core.NewRAWObjectStorage() + storage := memory.NewObjectStorage() _, err = r.Read(storage) c.Assert(err, IsNil) @@ -1,6 +1,7 @@ package git import ( + "errors" "fmt" "io" "strconv" @@ -17,8 +18,14 @@ type Blob struct { obj core.Object } +var ErrUnsupportedObject = errors.New("unsupported object type") + // Decode transform an core.Object into a Blob struct func (b *Blob) Decode(o core.Object) error { + if o.Type() != core.BlobObject { + return ErrUnsupportedObject + } + b.Hash = o.Hash() b.Size = o.Size() b.obj = o diff --git a/objects_test.go b/objects_test.go index bc83cb7..683ab15 100644 --- a/objects_test.go +++ b/objects_test.go @@ -6,6 +6,7 @@ import ( . "gopkg.in/check.v1" "gopkg.in/src-d/go-git.v2/core" + "gopkg.in/src-d/go-git.v2/storages/memory" ) type ObjectsSuite struct { @@ -71,7 +72,7 @@ func (s *ObjectsSuite) TestParseTree(c *C) { } func (s *ObjectsSuite) TestBlobHash(c *C) { - o := &core.RAWObject{} + o := &memory.Object{} o.SetType(core.BlobObject) o.SetSize(3) o.Writer().Write([]byte{'F', 'O', 'O'}) diff --git a/remote_test.go b/remote_test.go index 7a40deb..14c7ec1 100644 --- a/remote_test.go +++ b/remote_test.go @@ -2,8 +2,8 @@ package git import ( "gopkg.in/src-d/go-git.v2/clients/http" - "gopkg.in/src-d/go-git.v2/core" "gopkg.in/src-d/go-git.v2/formats/packfile" + "gopkg.in/src-d/go-git.v2/storages/memory" . "gopkg.in/check.v1" ) @@ -57,7 +57,7 @@ func (s *SuiteRemote) TestFetchDefaultBranch(c *C) { pr := packfile.NewReader(reader) - storage := core.NewRAWObjectStorage() + storage := memory.NewObjectStorage() _, err = pr.Read(storage) c.Assert(err, IsNil) c.Assert(storage.Objects, HasLen, 28) diff --git a/repository.go b/repository.go index 2532c8d..c2665ff 100644 --- a/repository.go +++ b/repository.go @@ -7,6 +7,7 @@ import ( "gopkg.in/src-d/go-git.v2/clients/common" "gopkg.in/src-d/go-git.v2/core" "gopkg.in/src-d/go-git.v2/formats/packfile" + "gopkg.in/src-d/go-git.v2/storages/memory" ) var ( @@ -49,7 +50,7 @@ func NewRepository(url string, auth common.AuthMethod) (*Repository, error) { func NewPlainRepository() *Repository { return &Repository{ Remotes: map[string]*Remote{}, - Storage: core.NewRAWObjectStorage(), + Storage: memory.NewObjectStorage(), } } diff --git a/storage/memory/object.go b/storage/memory/object.go new file mode 100644 index 0000000..7fee252 --- /dev/null +++ b/storage/memory/object.go @@ -0,0 +1,55 @@ +package memory + +import ( + "bytes" + "io" + + "gopkg.in/src-d/go-git.v2/core" +) + +// Object on memory core.Object implementation +type Object struct { + t core.ObjectType + h core.Hash + content []byte + size int64 +} + +// Hash return the object Hash, the hash is calculated on-the-fly the first +// time is called, the subsequent calls the same Hash is returned even in the +// type or the content has changed. The Hash is only generated if the size of +// the content is exactly the Object.Size +func (o *Object) Hash() core.Hash { + if o.h == core.ZeroHash && int64(len(o.content)) == o.size { + o.h = core.ComputeHash(o.t, o.content) + } + + return o.h +} + +// Type return the core.ObjectType +func (o *Object) Type() core.ObjectType { return o.t } + +// SetType sets the core.ObjectType +func (o *Object) SetType(t core.ObjectType) { o.t = t } + +// Size return the size of the object +func (o *Object) Size() int64 { return o.size } + +// SetSize set the object size, the given size should be written afterwards +func (o *Object) SetSize(s int64) { o.size = s } + +// Reader returns a io.Reader used to read the object content +func (o *Object) Reader() io.Reader { + return bytes.NewBuffer(o.content) +} + +// Writer returns a io.Writed used to write the object content +func (o *Object) Writer() io.Writer { + return o +} + +func (o *Object) Write(p []byte) (n int, err error) { + o.content = append(o.content, p...) + return len(p), nil +} diff --git a/storage/memory/object_test.go b/storage/memory/object_test.go new file mode 100644 index 0000000..b606eb2 --- /dev/null +++ b/storage/memory/object_test.go @@ -0,0 +1,67 @@ +package memory + +import ( + "io/ioutil" + "testing" + + . "gopkg.in/check.v1" + "gopkg.in/src-d/go-git.v2/core" +) + +func Test(t *testing.T) { TestingT(t) } + +type ObjectSuite struct{} + +var _ = Suite(&ObjectSuite{}) + +func (s *ObjectSuite) TestHash(c *C) { + o := &Object{} + o.SetType(core.BlobObject) + o.SetSize(14) + + _, err := o.Write([]byte("Hello, World!\n")) + c.Assert(err, IsNil) + + c.Assert(o.Hash().String(), Equals, "8ab686eafeb1f44702738c8b0f24f2567c36da6d") + + o.SetType(core.CommitObject) + c.Assert(o.Hash().String(), Equals, "8ab686eafeb1f44702738c8b0f24f2567c36da6d") +} + +func (s *ObjectSuite) TestHashNotFilled(c *C) { + o := &Object{} + o.SetType(core.BlobObject) + o.SetSize(14) + + c.Assert(o.Hash(), Equals, core.ZeroHash) +} + +func (s *ObjectSuite) TestType(c *C) { + o := &Object{} + o.SetType(core.BlobObject) + c.Assert(o.Type(), Equals, core.BlobObject) +} + +func (s *ObjectSuite) TestSize(c *C) { + o := &Object{} + o.SetSize(42) + c.Assert(o.Size(), Equals, int64(42)) +} + +func (s *ObjectSuite) TestReader(c *C) { + o := &Object{content: []byte("foo")} + + b, err := ioutil.ReadAll(o.Reader()) + c.Assert(err, IsNil) + c.Assert(b, DeepEquals, []byte("foo")) +} + +func (s *ObjectSuite) TestWriter(c *C) { + o := &Object{} + + n, err := o.Writer().Write([]byte("foo")) + c.Assert(err, IsNil) + c.Assert(n, Equals, 3) + + c.Assert(o.content, DeepEquals, []byte("foo")) +} diff --git a/storage/memory/storage.go b/storage/memory/storage.go new file mode 100644 index 0000000..4c106cc --- /dev/null +++ b/storage/memory/storage.go @@ -0,0 +1,83 @@ +package memory + +import ( + "fmt" + + "gopkg.in/src-d/go-git.v2/core" +) + +var ErrUnsupportedObjectType = fmt.Errorf("unsupported object type") + +// ObjectStorage is the implementation of core.ObjectStorage for memory.Object +type ObjectStorage struct { + Objects map[core.Hash]core.Object + Commits map[core.Hash]core.Object + Trees map[core.Hash]core.Object + Blobs map[core.Hash]core.Object +} + +// NewObjectStorage returns a new empty ObjectStorage +func NewObjectStorage() *ObjectStorage { + return &ObjectStorage{ + Objects: make(map[core.Hash]core.Object, 0), + Commits: make(map[core.Hash]core.Object, 0), + Trees: make(map[core.Hash]core.Object, 0), + Blobs: make(map[core.Hash]core.Object, 0), + } +} + +// New returns a new empty memory.Object +func (o *ObjectStorage) New() (core.Object, error) { + return &Object{}, nil +} + +// Set stores an object, the object should be properly filled before set it. +func (o *ObjectStorage) Set(obj core.Object) (core.Hash, error) { + h := obj.Hash() + o.Objects[h] = obj + + switch obj.Type() { + case core.CommitObject: + o.Commits[h] = o.Objects[h] + case core.TreeObject: + o.Trees[h] = o.Objects[h] + case core.BlobObject: + o.Blobs[h] = o.Objects[h] + default: + return h, ErrUnsupportedObjectType + } + + return h, nil +} + +// Get returns a object with the given hash +func (o *ObjectStorage) Get(h core.Hash) (core.Object, error) { + obj, ok := o.Objects[h] + if !ok { + return nil, core.ObjectNotFoundErr + } + + return obj, nil +} + +// Iter returns a core.ObjectIter for the given core.ObjectTybe +func (o *ObjectStorage) Iter(t core.ObjectType) core.ObjectIter { + var series []core.Object + switch t { + case core.CommitObject: + series = flattenObjectMap(o.Commits) + case core.TreeObject: + series = flattenObjectMap(o.Trees) + case core.BlobObject: + series = flattenObjectMap(o.Blobs) + } + return core.NewObjectSliceIter(series) +} + +func flattenObjectMap(m map[core.Hash]core.Object) []core.Object { + objects := make([]core.Object, 0, len(m)) + for _, obj := range m { + objects = append(objects, obj) + } + return objects +} diff --git a/storage/memory/storage_test.go b/storage/memory/storage_test.go new file mode 100644 index 0000000..4059dfa --- /dev/null +++ b/storage/memory/storage_test.go @@ -0,0 +1,51 @@ +package memory + +import ( + . "gopkg.in/check.v1" + "gopkg.in/src-d/go-git.v2/core" +) + +type ObjectStorageSuite struct{} + +var _ = Suite(&ObjectStorageSuite{}) + +func (s *ObjectStorageSuite) TestNew(c *C) { + os := NewObjectStorage() + + o, err := os.New() + c.Assert(err, IsNil) + c.Assert(o.Size(), Equals, int64(0)) +} + +func (s *ObjectStorageSuite) TestSet(c *C) { + os := NewObjectStorage() + + o, err := os.New() + c.Assert(err, IsNil) + + o.SetType(core.CommitObject) + o.SetSize(3) + o.Writer().Write([]byte("foo")) + + h, err := os.Set(o) + c.Assert(h.String(), Equals, "bc9968d75e48de59f0870ffb71f5e160bbbdcf52") +} + +func (s *ObjectStorageSuite) TestGet(c *C) { + os := NewObjectStorage() + + o, err := os.New() + c.Assert(err, IsNil) + + o.SetType(core.CommitObject) + o.SetSize(3) + o.Writer().Write([]byte("foo")) + + h, err := os.Set(o) + c.Assert(err, IsNil) + + ro, err := os.Get(h) + c.Assert(err, IsNil) + + c.Assert(ro, DeepEquals, o) +} @@ -151,6 +151,10 @@ func (t *Tree) walkEntries(base string, ch chan *File) { // Decode transform an core.Object into a Tree struct func (t *Tree) Decode(o core.Object) error { + if o.Type() != core.TreeObject { + return ErrUnsupportedObject + } + t.Hash = o.Hash() if o.Size() == 0 { return nil |