Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2015 The LUCI Authors. All rights reserved. | |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | |
| 3 // that can be found in the LICENSE file. | |
| 4 | |
| 5 package memory | |
| 6 | |
| 7 import ( | |
| 8 "bytes" | |
| 9 | |
| 10 "github.com/luci/gae/service/datastore/serialize" | |
| 11 ) | |
| 12 | |
| 13 type iterDefinition struct { | |
| 14 // The collection to iterate over | |
| 15 c memCollection | |
| 16 | |
| 17 // The prefix to always assert for every row. A nil prefix matches every row. | |
| 18 prefix []byte | |
| 19 | |
| 20 // prefixLen is the number of prefix bytes that the caller cares about. It | |
| 21 // may be <= len(prefix). When doing a multiIterator, this number will b e used | |
| 22 // to determine the amount of suffix to transfer accross iterators. This is | |
| 23 // used specifically when using builtin indexes to service ancestor quer ies. | |
| 24 // The builtin index represents the ancestor key with prefix bytes, but in a | |
| 25 // multiIterator context, it wants the entire key to be included in the | |
| 26 // suffix. | |
| 27 prefixLen int | |
| 28 | |
| 29 // The start cursor. It's appended to prefix to find the first row. | |
| 30 start []byte | |
| 31 | |
| 32 // The end cursor. It's appended to prefix to find the last row (which i s not | |
| 33 // included in the interation result). If this is nil, then there's no e nd | |
| 34 // except the natural end of the collection. | |
| 35 end []byte | |
| 36 } | |
| 37 | |
| 38 func multiIterate(defs []*iterDefinition, cb func(suffix []byte) error) error { | |
| 39 if len(defs) == 0 { | |
| 40 return nil | |
| 41 } | |
| 42 | |
| 43 ts := make([]*iterator, len(defs)) | |
| 44 prefixLens := make([]int, len(defs)) | |
| 45 for i, def := range defs { | |
| 46 // bind i so that the defer below doesn't get goofed by the loop variable | |
| 47 i := i | |
| 48 ts[i] = def.mkIter() | |
| 49 prefixLens[i] = def.prefixLen | |
| 50 } | |
| 51 | |
| 52 suffix := []byte(nil) | |
| 53 skip := -1 | |
| 54 | |
| 55 MainLoop: | |
| 56 for { | |
| 57 for idx, it := range ts { | |
| 58 if skip >= 0 && skip == idx { | |
| 59 continue | |
| 60 } | |
| 61 | |
| 62 pfxLen := prefixLens[idx] | |
| 63 it.skip(serialize.Join(it.def.prefix[:pfxLen], suffix)) | |
|
iannucci
2017/01/07 19:06:17
:O
\o/
dnj
2017/01/08 03:38:14
:D Yeah I think we both disliked the complexity of
| |
| 64 ent := it.next() | |
| 65 if ent == nil { | |
| 66 // we hit the end of an iterator, we're now done with the whole | |
| 67 // query. | |
| 68 return nil | |
| 69 } | |
| 70 sfxRO := ent.key[pfxLen:] | |
| 71 | |
| 72 if bytes.Compare(sfxRO, suffix) > 0 { | |
| 73 // this row has a higher suffix than anything we 've seen before. Set | |
| 74 // ourself to be the skip, and resart this loop from the top. | |
| 75 suffix = append(suffix[:0], sfxRO...) | |
| 76 skip = idx | |
| 77 if idx != 0 { | |
| 78 // no point to restarting on the 0th ind ex | |
| 79 continue MainLoop | |
| 80 } | |
| 81 } | |
| 82 } | |
| 83 | |
| 84 if err := cb(suffix); err != nil { | |
| 85 return err | |
| 86 } | |
| 87 suffix = nil | |
| 88 skip = -1 | |
| 89 } | |
| 90 } | |
| 91 | |
| 92 type iterator struct { | |
| 93 def *iterDefinition | |
| 94 base memIterator | |
| 95 | |
| 96 start []byte | |
| 97 end []byte | |
| 98 lastKey []byte | |
| 99 } | |
| 100 | |
| 101 func (def *iterDefinition) mkIter() *iterator { | |
| 102 if !def.c.IsReadOnly() { | |
| 103 panic("attempting to make an iterator with r/w collection") | |
| 104 } | |
| 105 | |
| 106 it := iterator{ | |
| 107 def: def, | |
| 108 } | |
| 109 | |
| 110 // convert the suffixes from the iterDefinition into full rows for the | |
| 111 // underlying storage. | |
| 112 it.start = serialize.Join(def.prefix, def.start) | |
| 113 if def.end != nil { | |
| 114 it.end = serialize.Join(def.prefix, def.end) | |
| 115 } | |
| 116 return &it | |
| 117 } | |
| 118 | |
| 119 func (it *iterator) skip(targ []byte) { | |
| 120 if bytes.Compare(targ, it.start) < 0 { | |
| 121 targ = it.start | |
| 122 } | |
| 123 if it.base == nil || bytes.Compare(targ, it.lastKey) > 0 { | |
| 124 // If our skip target is >= our last key, then create a new Iter ator | |
| 125 // starting from that target. | |
| 126 it.base = it.def.c.Iterator(targ) | |
| 127 } | |
| 128 } | |
| 129 | |
| 130 func (it *iterator) next() *storeEntry { | |
| 131 if it.base == nil { | |
| 132 it.skip(nil) | |
| 133 } | |
| 134 | |
| 135 ent := it.base.Next() | |
| 136 switch { | |
| 137 case ent == nil: | |
| 138 return nil | |
| 139 | |
| 140 case !bytes.HasPrefix(ent.key, it.def.prefix): | |
| 141 // we're no longer in prefix, terminate | |
| 142 return nil | |
| 143 | |
| 144 case it.end != nil && bytes.Compare(ent.key, it.end) >= 0: | |
| 145 // we hit our cap, terminate. | |
| 146 return nil | |
| 147 | |
| 148 default: | |
| 149 it.lastKey = ent.key | |
| 150 return ent | |
| 151 } | |
| 152 } | |
| OLD | NEW |