diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/object.go | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/core/object.go b/core/object.go index 1034ab3..62cb469 100644 --- a/core/object.go +++ b/core/object.go @@ -2,9 +2,14 @@ package core import ( "bytes" + "errors" "io" ) +var ( + ObjectNotFoundErr = errors.New("object not found") +) + // Object is a generic representation of any git object type Object interface { Type() ObjectType @@ -21,6 +26,7 @@ type ObjectStorage interface { New() Object Set(Object) Hash Get(Hash) (Object, bool) + Iter(ObjectType) ObjectIter } // ObjectType internal object type's @@ -58,6 +64,92 @@ 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 implements ObjectIter. It iterates over a series of object +// hashes and yields their associated objects by retrieving each one from +// object storage. The retrievals are lazy and only occur when the iterator +// moves forward with a call to Next(). +// +// The ObjectLookupIter must be closed with a call to Close() when it is no +// longer needed. +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 can't be found in +// the object storage, it will return ObjectNotFoundErr 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, ObjectNotFoundErr + } + iter.pos++ + return obj, nil +} + +// Close releases any resources used by the iterator. +func (iter *ObjectLookupIter) Close() { + iter.pos = len(iter.series) +} + +// ObjectSliceIter implements ObjectIter. It iterates over a series of objects +// stored in a slice and yields each one in turn when Next() is called. +// +// The ObjectSliceIter must be closed with a call to Close() when it is no +// longer needed. +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 @@ -117,3 +209,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 +} |