diff options
-rw-r--r-- | commit.go | 57 | ||||
-rw-r--r-- | commit_test.go | 3 | ||||
-rw-r--r-- | core/object.go | 98 | ||||
-rw-r--r-- | repository.go | 12 | ||||
-rw-r--r-- | tree.go | 17 |
5 files changed, 122 insertions, 65 deletions
@@ -34,16 +34,7 @@ func (c *Commit) Tree() *Tree { } func (c *Commit) Parents() *CommitIter { - i := NewCommitIter(c.r) - go func() { - defer i.Close() - for _, hash := range c.parents { - obj, _ := c.r.Storage.Get(hash) - i.Add(obj) - } - }() - - return i + return NewCommitIter(c.r, core.NewObjectLookupIter(c.r.Storage, c.parents)) } // NumParents returns the number of parents in a commit. @@ -106,52 +97,24 @@ func (c *Commit) String() string { } type CommitIter struct { - iter + core.ObjectIter + r *Repository } -func NewCommitIter(r *Repository) *CommitIter { - return &CommitIter{newIter(r)} +func NewCommitIter(r *Repository, iter core.ObjectIter) *CommitIter { + return &CommitIter{iter, r} } -func (i *CommitIter) Next() (*Commit, error) { - obj := <-i.ch - if obj == nil { - return nil, io.EOF +func (iter *CommitIter) Next() (*Commit, error) { + obj, err := iter.ObjectIter.Next() + if err != nil { + return nil, err } - commit := &Commit{r: i.r} + commit := &Commit{r: iter.r} return commit, commit.Decode(obj) } -type iter struct { - ch chan core.Object - r *Repository - - IsClosed bool -} - -func newIter(r *Repository) iter { - ch := make(chan core.Object, 1) - return iter{ch: ch, r: r} -} - -func (i *iter) Add(o core.Object) { - if i.IsClosed { - return - } - - i.ch <- o -} - -func (i *iter) Close() { - if i.IsClosed { - return - } - - defer func() { i.IsClosed = true }() - close(i.ch) -} - type commitSorterer struct { l []*Commit } diff --git a/commit_test.go b/commit_test.go index 67b9e77..1202d59 100644 --- a/commit_test.go +++ b/commit_test.go @@ -40,11 +40,14 @@ func (s *SuiteCommit) SetUpSuite(c *C) { } } +// FIXME: Test the new CommitIter +/* func (s *SuiteCommit) TestIterClose(c *C) { i := &iter{ch: make(chan core.Object, 1)} i.Close() i.Close() } +*/ var fileTests = []struct { repo string // the repo name as in localRepos diff --git a/core/object.go b/core/object.go index 4e8a587..205de95 100644 --- a/core/object.go +++ b/core/object.go @@ -21,6 +21,7 @@ type ObjectStorage interface { New() Object Set(Object) Hash Get(Hash) (Object, bool) + Iter(ObjectType) ObjectIter } // ObjectType internal object type's @@ -52,6 +53,82 @@ func (t ObjectType) Bytes() []byte { return []byte(t.String()) } +// ObjectIter is a generic closable interface for iterating over objects. +type ObjectIter interface { + Next() (Object, error) + Close() +} + +// ObjectLookupIter yields a series of objects by retrieving each one from +// object storage. +type ObjectLookupIter struct { + storage ObjectStorage + series []Hash + pos int +} + +// NewObjectLookupIter returns an object iterator given an object storage and +// a slice of object hashes. +func NewObjectLookupIter(storage ObjectStorage, series []Hash) *ObjectLookupIter { + return &ObjectLookupIter{ + storage: storage, + series: series, + } +} + +// Next returns the next object from the iterator. If the iterator has reached +// the end it will return io.EOF as an error. If the object is retreieved +// successfully error will be nil. +func (iter *ObjectLookupIter) Next() (Object, error) { + if iter.pos >= len(iter.series) { + return nil, io.EOF + } + hash := iter.series[iter.pos] + obj, ok := iter.storage.Get(hash) + if !ok { + // FIXME: Consider making ObjectStorage.Get return an actual error that we + // could pass back here. + return nil, io.EOF + } + iter.pos++ + return obj, nil +} + +// Close releases any resources used by the iterator. +func (iter *ObjectLookupIter) Close() { + iter.pos = len(iter.series) +} + +// ObjectSliceIter yields a series of objects from a slice of objects. +type ObjectSliceIter struct { + series []Object + pos int +} + +// NewObjectSliceIter returns an object iterator for the given slice of objects. +func NewObjectSliceIter(series []Object) *ObjectSliceIter { + return &ObjectSliceIter{ + series: series, + } +} + +// Next returns the next object from the iterator. If the iterator has reached +// the end it will return io.EOF as an error. If the object is retreieved +// successfully error will be nil. +func (iter *ObjectSliceIter) Next() (Object, error) { + if iter.pos >= len(iter.series) { + return nil, io.EOF + } + obj := iter.series[iter.pos] + iter.pos++ + return obj, nil +} + +// Close releases any resources used by the iterator. +func (iter *ObjectSliceIter) Close() { + iter.pos = len(iter.series) +} + type RAWObject struct { b []byte t ObjectType @@ -111,3 +188,24 @@ func (o *RAWObjectStorage) Get(h Hash) (Object, bool) { return obj, ok } + +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/repository.go b/repository.go index 5deddfb..d0bc103 100644 --- a/repository.go +++ b/repository.go @@ -19,7 +19,7 @@ const ( type Repository struct { Remotes map[string]*Remote - Storage *core.RAWObjectStorage + Storage core.ObjectStorage URL string } @@ -100,15 +100,7 @@ func (r *Repository) Commit(h core.Hash) (*Commit, error) { // Commits decode the objects into commits func (r *Repository) Commits() *CommitIter { - i := NewCommitIter(r) - go func() { - defer i.Close() - for _, obj := range r.Storage.Commits { - i.Add(obj) - } - }() - - return i + return NewCommitIter(r, r.Storage.Iter(core.CommitObject)) } // Tree return the tree with the given hash @@ -187,19 +187,20 @@ func (t *Tree) Decode(o core.Object) error { } type TreeIter struct { - iter + core.ObjectIter + r *Repository } -func NewTreeIter(r *Repository) *TreeIter { - return &TreeIter{newIter(r)} +func NewTreeIter(r *Repository, iter core.ObjectIter) *TreeIter { + return &TreeIter{iter, r} } -func (i *TreeIter) Next() (*Tree, error) { - obj := <-i.ch - if obj == nil { - return nil, io.EOF +func (iter *TreeIter) Next() (*Tree, error) { + obj, err := iter.ObjectIter.Next() + if err != nil { + return nil, err } - tree := &Tree{r: i.r} + tree := &Tree{r: iter.r} return tree, tree.Decode(obj) } |