Index: impl/memory/memstore_iter.go |
diff --git a/impl/memory/memstore_iter.go b/impl/memory/memstore_iter.go |
new file mode 100644 |
index 0000000000000000000000000000000000000000..3dff0fbd22f89af1a273cad8dd28c724e19cb774 |
--- /dev/null |
+++ b/impl/memory/memstore_iter.go |
@@ -0,0 +1,152 @@ |
+// Copyright 2015 The LUCI Authors. All rights reserved. |
+// Use of this source code is governed under the Apache License, Version 2.0 |
+// that can be found in the LICENSE file. |
+ |
+package memory |
+ |
+import ( |
+ "bytes" |
+ |
+ "github.com/luci/gae/service/datastore/serialize" |
+) |
+ |
+type iterDefinition struct { |
+ // The collection to iterate over |
+ c memCollection |
+ |
+ // The prefix to always assert for every row. A nil prefix matches every row. |
+ prefix []byte |
+ |
+ // prefixLen is the number of prefix bytes that the caller cares about. It |
+ // may be <= len(prefix). When doing a multiIterator, this number will be used |
+ // to determine the amount of suffix to transfer accross iterators. This is |
+ // used specifically when using builtin indexes to service ancestor queries. |
+ // The builtin index represents the ancestor key with prefix bytes, but in a |
+ // multiIterator context, it wants the entire key to be included in the |
+ // suffix. |
+ prefixLen int |
+ |
+ // The start cursor. It's appended to prefix to find the first row. |
+ start []byte |
+ |
+ // The end cursor. It's appended to prefix to find the last row (which is not |
+ // included in the interation result). If this is nil, then there's no end |
+ // except the natural end of the collection. |
+ end []byte |
+} |
+ |
+func multiIterate(defs []*iterDefinition, cb func(suffix []byte) error) error { |
+ if len(defs) == 0 { |
+ return nil |
+ } |
+ |
+ ts := make([]*iterator, len(defs)) |
+ prefixLens := make([]int, len(defs)) |
+ for i, def := range defs { |
+ // bind i so that the defer below doesn't get goofed by the loop variable |
+ i := i |
+ ts[i] = def.mkIter() |
+ prefixLens[i] = def.prefixLen |
+ } |
+ |
+ suffix := []byte(nil) |
+ skip := -1 |
+ |
+MainLoop: |
+ for { |
+ for idx, it := range ts { |
+ if skip >= 0 && skip == idx { |
+ continue |
+ } |
+ |
+ pfxLen := prefixLens[idx] |
+ it.skip(serialize.Join(it.def.prefix[:pfxLen], suffix)) |
+ ent := it.next() |
+ if ent == nil { |
+ // we hit the end of an iterator, we're now done with the whole |
+ // query. |
+ return nil |
+ } |
+ sfxRO := ent.key[pfxLen:] |
+ |
+ if bytes.Compare(sfxRO, suffix) > 0 { |
+ // this row has a higher suffix than anything we've seen before. Set |
+ // ourself to be the skip, and resart this loop from the top. |
+ suffix = append(suffix[:0], sfxRO...) |
+ skip = idx |
+ if idx != 0 { |
+ // no point to restarting on the 0th index |
+ continue MainLoop |
+ } |
+ } |
+ } |
+ |
+ if err := cb(suffix); err != nil { |
+ return err |
+ } |
+ suffix = nil |
+ skip = -1 |
+ } |
+} |
+ |
+type iterator struct { |
+ def *iterDefinition |
+ base memIterator |
+ |
+ start []byte |
+ end []byte |
+ lastKey []byte |
+} |
+ |
+func (def *iterDefinition) mkIter() *iterator { |
+ if !def.c.IsReadOnly() { |
+ panic("attempting to make an iterator with r/w collection") |
+ } |
+ |
+ it := iterator{ |
+ def: def, |
+ } |
+ |
+ // convert the suffixes from the iterDefinition into full rows for the |
+ // underlying storage. |
+ it.start = serialize.Join(def.prefix, def.start) |
+ if def.end != nil { |
+ it.end = serialize.Join(def.prefix, def.end) |
+ } |
+ return &it |
+} |
+ |
+func (it *iterator) skip(targ []byte) { |
+ if bytes.Compare(targ, it.start) < 0 { |
+ targ = it.start |
+ } |
+ if it.base == nil || bytes.Compare(targ, it.lastKey) > 0 { |
+ // If our skip target is >= our last key, then create a new Iterator |
+ // starting from that target. |
+ it.base = it.def.c.Iterator(targ) |
+ } |
+} |
+ |
+func (it *iterator) next() *storeEntry { |
+ if it.base == nil { |
+ it.skip(nil) |
+ } |
+ |
+ ent := it.base.Next() |
+ switch { |
+ case ent == nil: |
+ return nil |
+ |
+ case !bytes.HasPrefix(ent.key, it.def.prefix): |
+ // we're no longer in prefix, terminate |
+ return nil |
+ |
+ case it.end != nil && bytes.Compare(ent.key, it.end) >= 0: |
+ // we hit our cap, terminate. |
+ return nil |
+ |
+ default: |
+ it.lastKey = ent.key |
+ return ent |
+ } |
+} |