| Index: impl/memory/datastore_index.go | 
| diff --git a/impl/memory/datastore_index.go b/impl/memory/datastore_index.go | 
| index 37200d9c22fde7df6a3d3a14795f615ec3c947a9..59b9c405d3a82bde2decf1b3a3896bc808e0a5be 100644 | 
| --- a/impl/memory/datastore_index.go | 
| +++ b/impl/memory/datastore_index.go | 
| @@ -14,8 +14,6 @@ import ( | 
| "github.com/luci/gkvlite" | 
| ) | 
|  | 
| -var indexCreationDeterministic = false | 
| - | 
| type qIndexSlice []*ds.IndexDefinition | 
|  | 
| func (s qIndexSlice) Len() int           { return len(s) } | 
| @@ -39,15 +37,15 @@ func defaultIndicies(kind string, pmap ds.PropertyMap) []*ds.IndexDefinition { | 
| ret = append(ret, &ds.IndexDefinition{Kind: kind, SortBy: []ds.IndexColumn{{Property: name}}}) | 
| ret = append(ret, &ds.IndexDefinition{Kind: kind, SortBy: []ds.IndexColumn{{Property: name, Direction: ds.DESCENDING}}}) | 
| } | 
| -	if indexCreationDeterministic { | 
| +	if serializationDeterministic { | 
| sort.Sort(ret) | 
| } | 
| return ret | 
| } | 
|  | 
| func indexEntriesWithBuiltins(k ds.Key, pm ds.PropertyMap, complexIdxs []*ds.IndexDefinition) *memStore { | 
| -	sip := partiallySerialize(pm) | 
| -	return sip.indexEntries(k, append(defaultIndicies(k.Kind(), pm), complexIdxs...)) | 
| +	sip := partiallySerialize(k, pm) | 
| +	return sip.indexEntries(k.Namespace(), append(defaultIndicies(k.Kind(), pm), complexIdxs...)) | 
| } | 
|  | 
| // serializedPvals is all of the serialized DSProperty values in qASC order. | 
| @@ -58,26 +56,24 @@ func (s serializedPvals) Swap(i, j int)      { s[i], s[j] = s[j], s[i] } | 
| func (s serializedPvals) Less(i, j int) bool { return bytes.Compare(s[i], s[j]) < 0 } | 
|  | 
| // prop name -> [<serialized DSProperty>, ...] | 
| +// includes special values '__key__' and '__ancestor__' which contains all of | 
| +// the ancestor entries for this key. | 
| type serializedIndexablePmap map[string]serializedPvals | 
|  | 
| -func partiallySerialize(pm ds.PropertyMap) (ret serializedIndexablePmap) { | 
| -	if len(pm) == 0 { | 
| -		return | 
| +func partiallySerialize(k ds.Key, pm ds.PropertyMap) (ret serializedIndexablePmap) { | 
| +	ret = make(serializedIndexablePmap, len(pm)+2) | 
| +	ret["__key__"] = [][]byte{serialize.ToBytes(ds.MkProperty(k))} | 
| +	for k != nil { | 
| +		ret["__ancestor__"] = append(ret["__ancestor__"], serialize.ToBytes(ds.MkProperty(k))) | 
| +		k = k.Parent() | 
| } | 
| - | 
| -	buf := &bytes.Buffer{} | 
| -	ret = make(serializedIndexablePmap, len(pm)) | 
| for k, vals := range pm { | 
| newVals := make(serializedPvals, 0, len(vals)) | 
| for _, v := range vals { | 
| if v.IndexSetting() == ds.NoIndex { | 
| continue | 
| } | 
| -			buf.Reset() | 
| -			serialize.WriteProperty(buf, serialize.WithoutContext, v) | 
| -			newVal := make([]byte, buf.Len()) | 
| -			copy(newVal, buf.Bytes()) | 
| -			newVals = append(newVals, newVal) | 
| +			newVals = append(newVals, serialize.ToBytes(v)) | 
| } | 
| if len(newVals) > 0 { | 
| sort.Sort(newVals) | 
| @@ -95,7 +91,7 @@ type indexRowGen struct { | 
| } | 
|  | 
| // permute calls cb for each index row, in the sorted order of the rows. | 
| -func (s indexRowGen) permute(cb func([]byte)) { | 
| +func (s indexRowGen) permute(collSetFn func(k, v []byte)) { | 
| iVec := make([]int, len(s.propVec)) | 
| iVecLim := make([]int, len(s.propVec)) | 
|  | 
| @@ -137,18 +133,13 @@ func (s indexRowGen) permute(cb func([]byte)) { | 
| for pvalSliceIdx, pvalIdx := range iVec { | 
| bufsiz += len(s.propVec[pvalSliceIdx][pvalIdx]) | 
| } | 
| -		buf := bytes.NewBuffer(make([]byte, 0, bufsiz)) | 
| +		buf := serialize.Invertible(bytes.NewBuffer(make([]byte, 0, bufsiz))) | 
| for pvalSliceIdx, pvalIdx := range iVec { | 
| data := s.propVec[pvalSliceIdx][pvalIdx] | 
| -			if s.orders[pvalSliceIdx] == ds.ASCENDING { | 
| -				buf.Write(data) | 
| -			} else { | 
| -				for _, b := range data { | 
| -					buf.WriteByte(b ^ 0xFF) | 
| -				} | 
| -			} | 
| +			buf.SetInvert(s.orders[pvalSliceIdx] == ds.DESCENDING) | 
| +			buf.Write(data) | 
| } | 
| -		cb(buf.Bytes()) | 
| +		collSetFn(buf.Bytes(), []byte{}) | 
| if !incPos() { | 
| break | 
| } | 
| @@ -162,13 +153,10 @@ type matcher struct { | 
| // matcher.match checks to see if the mapped, serialized property values | 
| // match the index. If they do, it returns a indexRowGen. Do not write or modify | 
| // the data in the indexRowGen. | 
| -func (m *matcher) match(idx *ds.IndexDefinition, sip serializedIndexablePmap) (indexRowGen, bool) { | 
| +func (m *matcher) match(sortBy []ds.IndexColumn, sip serializedIndexablePmap) (indexRowGen, bool) { | 
| m.buf.propVec = m.buf.propVec[:0] | 
| m.buf.orders = m.buf.orders[:0] | 
| -	for _, sb := range idx.SortBy { | 
| -		if sb.Property == "__key__" { | 
| -			panic("don't know how to build compound index on __key__") | 
| -		} | 
| +	for _, sb := range sortBy { | 
| if pv, ok := sip[sb.Property]; ok { | 
| m.buf.propVec = append(m.buf.propVec, pv) | 
| m.buf.orders = append(m.buf.orders, sb.Direction) | 
| @@ -179,41 +167,17 @@ func (m *matcher) match(idx *ds.IndexDefinition, sip serializedIndexablePmap) (i | 
| return m.buf, true | 
| } | 
|  | 
| -func (sip serializedIndexablePmap) indexEntries(k ds.Key, idxs []*ds.IndexDefinition) *memStore { | 
| +func (sip serializedIndexablePmap) indexEntries(ns string, idxs []*ds.IndexDefinition) *memStore { | 
| ret := newMemStore() | 
| idxColl := ret.SetCollection("idx", nil) | 
| -	// getIdxEnts retrieves an index collection or adds it if it's not there. | 
| -	getIdxEnts := func(qi *ds.IndexDefinition) *memCollection { | 
| -		b := serialize.ToBytes(*qi) | 
| -		idxColl.Set(b, []byte{}) | 
| -		return ret.SetCollection(fmt.Sprintf("idx:%s:%s", k.Namespace(), b), nil) | 
| -	} | 
| - | 
| -	keyData := serialize.ToBytes(k) | 
| - | 
| -	walkPermutations := func(prefix []byte, irg indexRowGen, ents *memCollection) { | 
| -		irg.permute(func(data []byte) { | 
| -			buf := bytes.NewBuffer(make([]byte, 0, len(prefix)+len(data)+len(keyData))) | 
| -			buf.Write(prefix) | 
| -			buf.Write(data) | 
| -			buf.Write(keyData) | 
| -			ents.Set(buf.Bytes(), []byte{}) | 
| -		}) | 
| -	} | 
|  | 
| mtch := matcher{} | 
| for _, idx := range idxs { | 
| -		if irg, ok := mtch.match(idx, sip); ok { | 
| -			idxEnts := getIdxEnts(idx) | 
| -			if len(irg.propVec) == 0 { | 
| -				idxEnts.Set(keyData, []byte{}) // propless index, e.g. kind -> key = nil | 
| -			} else if idx.Ancestor { | 
| -				for ancKey := k; ancKey != nil; ancKey = ancKey.Parent() { | 
| -					walkPermutations(serialize.ToBytes(ancKey), irg, idxEnts) | 
| -				} | 
| -			} else { | 
| -				walkPermutations(nil, irg, idxEnts) | 
| -			} | 
| +		if irg, ok := mtch.match(idx.NormalizeOrder(), sip); ok { | 
| +			idxBin := serialize.ToBytes(*idx) | 
| +			idxColl.Set(idxBin, []byte{}) | 
| +			coll := ret.SetCollection(fmt.Sprintf("idx:%s:%s", ns, idxBin), nil) | 
| +			irg.permute(coll.Set) | 
| } | 
| } | 
|  | 
| @@ -229,9 +193,7 @@ func getCompIdxs(idxColl *memCollection) []*ds.IndexDefinition { | 
| return false | 
| } | 
| qi, err := serialize.ReadIndexDefinition(bytes.NewBuffer(i.Key)) | 
| -		if err != nil { | 
| -			panic(err) // memory corruption | 
| -		} | 
| +		memoryCorruption(err) | 
| compIdx = append(compIdx, &qi) | 
| return true | 
| }) | 
| @@ -284,7 +246,7 @@ func mergeIndexes(ns string, store, oldIdx, newIdx *memStore) { | 
| } | 
| }) | 
| default: | 
| -			panic("impossible") | 
| +			impossible(fmt.Errorf("both values from gkvCollide were nil?")) | 
| } | 
| // TODO(riannucci): remove entries from idxColl and remove index collections | 
| // when there are no index entries for that index any more. | 
| @@ -294,15 +256,13 @@ func mergeIndexes(ns string, store, oldIdx, newIdx *memStore) { | 
| func addIndex(store *memStore, ns string, compIdx []*ds.IndexDefinition) { | 
| store.GetCollection("ents:"+ns).VisitItemsAscend(nil, true, func(i *gkvlite.Item) bool { | 
| pm, err := rpmWoCtx(i.Val, ns) | 
| -		if err != nil { | 
| -			panic(err) // memory corruption | 
| -		} | 
| +		memoryCorruption(err) | 
| + | 
| k, err := serialize.ReadKey(bytes.NewBuffer(i.Key), serialize.WithoutContext, globalAppID, ns) | 
| -		if err != nil { | 
| -			panic(err) | 
| -		} | 
| -		sip := partiallySerialize(pm) | 
| -		mergeIndexes(ns, store, newMemStore(), sip.indexEntries(k, compIdx)) | 
| +		memoryCorruption(err) | 
| + | 
| +		sip := partiallySerialize(k, pm) | 
| +		mergeIndexes(ns, store, newMemStore(), sip.indexEntries(k.Namespace(), compIdx)) | 
| return true | 
| }) | 
| } | 
|  |