diff --git a/lib/go/camli/index/enumstat.go b/lib/go/camli/index/enumstat.go index d051b8285..c5b1c49a1 100644 --- a/lib/go/camli/index/enumstat.go +++ b/lib/go/camli/index/enumstat.go @@ -18,14 +18,29 @@ package index import ( "os" + "strconv" + "strings" "camli/blobref" ) func (ix *Index) EnumerateBlobs(dest chan<- blobref.SizedBlobRef, after string, limit uint, waitSeconds int) os.Error { defer close(dest) - panic("TODO") - return nil + it := ix.s.Find("have:"+after) + n := uint(0) + for n < limit && it.Next() { + k := it.Key() + if !strings.HasPrefix(k, "have:") { + break + } + n++ + br := blobref.Parse(k[len("have:"):]) + size, err := strconv.Atoi64(it.Value()) + if br != nil && err == nil { + dest <- blobref.SizedBlobRef{br, size} + } + } + return it.Close() } func (ix *Index) StatBlobs(dest chan<- blobref.SizedBlobRef, blobs []*blobref.BlobRef, waitSeconds int) os.Error { diff --git a/lib/go/camli/index/index.go b/lib/go/camli/index/index.go index 62752f3f5..d87c0015a 100644 --- a/lib/go/camli/index/index.go +++ b/lib/go/camli/index/index.go @@ -31,6 +31,42 @@ type IndexStorage interface { BeginBatch() BatchMutation CommitBatch(b BatchMutation) os.Error + + // Find returns an iterator positioned before the first key/value pair + // whose key is 'greater than or equal to' the given key. There may be no + // such pair, in which case the iterator will return false on Next. + // + // Any error encountered will be implicitly returned via the iterator. An + // error-iterator will yield no key/value pairs and closing that iterator + // will return that error. + Find(key string) Iterator +} + +// Iterator iterates over an index Storage's key/value pairs in key order. +// +// An iterator must be closed after use, but it is not necessary to read an +// iterator until exhaustion. +// +// An iterator is not necessarily goroutine-safe, but it is safe to use +// multiple iterators concurrently, with each in a dedicated goroutine. +type Iterator interface { + // Next moves the iterator to the next key/value pair. + // It returns whether the iterator is exhausted. + Next() bool + + // Key returns the key of the current key/value pair. + // Only valid after a call to Next returns true. + Key() string + + // Value returns the value of the current key/value pair. + // Only valid after a call to Next returns true. + Value() string + + // Close closes the iterator and returns any accumulated error. Exhausting + // all the key/value pairs in a table is not considered to be an error. + // It is valid to call Close multiple times. Other methods should not be + // called after the iterator has been closed. + Close() os.Error } type BatchMutation interface { diff --git a/lib/go/camli/index/memindex.go b/lib/go/camli/index/memindex.go index a036d00e8..56e93cdfa 100644 --- a/lib/go/camli/index/memindex.go +++ b/lib/go/camli/index/memindex.go @@ -38,6 +38,28 @@ type memKeys struct { db db.DB } +// stringIterator converts from leveldb's db.Iterator interface, which +// operates on []byte, to Camlistore's index.Iterator, which operates +// on string. +type stringIterator struct { + db.Iterator +} + +func (s stringIterator) Key() string { + return string(s.Iterator.Key()) +} + +func (s stringIterator) Value() string { + return string(s.Iterator.Value()) +} + +func (mk *memKeys) Find(key string) Iterator { + mk.mu.Lock() + defer mk.mu.Unlock() + dit := mk.db.Find([]byte(key)) + return stringIterator{dit} +} + func (mk *memKeys) Set(key, value string) os.Error { mk.mu.Lock() defer mk.mu.Unlock()