Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(63)

Unified Diff: go/src/infra/gae/libs/gae/memory/plist.go

Issue 1222903002: Refactor current GAE abstraction library to be free of the SDK* (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: more fixes Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: go/src/infra/gae/libs/gae/memory/plist.go
diff --git a/go/src/infra/gae/libs/gae/memory/plist.go b/go/src/infra/gae/libs/gae/memory/plist.go
new file mode 100644
index 0000000000000000000000000000000000000000..09cfec514c826f817a271b01f121500a6147b22a
--- /dev/null
+++ b/go/src/infra/gae/libs/gae/memory/plist.go
@@ -0,0 +1,298 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package memory
+
+import (
+ "bytes"
+ "fmt"
+ "sort"
+
+ "infra/gae/libs/gae"
+ "infra/gae/libs/gae/helper"
+
+ "github.com/luci/gkvlite"
+)
+
+var indexCreationDeterministic = false
+
+type qIndexSlice []*qIndex
+
+func (s qIndexSlice) Len() int { return len(s) }
+func (s qIndexSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s qIndexSlice) Less(i, j int) bool { return s[i].Less(s[j]) }
+
+func defaultIndicies(kind string, pmap gae.DSPropertyMap) []*qIndex {
+ ret := make(qIndexSlice, 0, 2*len(pmap)+1)
+ ret = append(ret, &qIndex{kind, false, nil})
+ for name, pvals := range pmap {
+ needsIndex := false
+ for _, v := range pvals {
+ if !v.NoIndex() {
+ needsIndex = true
+ break
+ }
+ }
+ if !needsIndex {
+ continue
+ }
+ ret = append(ret, &qIndex{kind, false, []qSortBy{{name, qASC}}})
+ ret = append(ret, &qIndex{kind, false, []qSortBy{{name, qDEC}}})
+ }
+ if indexCreationDeterministic {
+ sort.Sort(ret)
+ }
+ return ret
+}
+
+func indexEntriesWithBuiltins(k gae.DSKey, pm gae.DSPropertyMap, complexIdxs []*qIndex) *memStore {
+ sip := partiallySerialize(pm)
+ return sip.indexEntries(k, append(defaultIndicies(k.Kind(), pm), complexIdxs...))
+}
+
+// serializedPvals is all of the serialized DSProperty values in qASC order.
+type serializedPvals [][]byte
+
+func (s serializedPvals) Len() int { return len(s) }
+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>, ...]
+type serializedIndexablePmap map[string]serializedPvals
+
+func partiallySerialize(pm gae.DSPropertyMap) (ret serializedIndexablePmap) {
+ if len(pm) == 0 {
+ return
+ }
+
+ 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.NoIndex() {
+ continue
+ }
+ buf.Reset()
+ helper.WriteDSProperty(buf, v, helper.WithoutContext)
+ newVal := make([]byte, buf.Len())
+ copy(newVal, buf.Bytes())
+ newVals = append(newVals, newVal)
+ }
+ if len(newVals) > 0 {
+ sort.Sort(newVals)
+ ret[k] = newVals
+ }
+ }
+ return
+}
+
+// indexRowGen contains enough information to generate all of the index rows which
+// correspond with a propertyList and a qIndex.
+type indexRowGen struct {
+ propVec []serializedPvals
+ orders []qDirection
+}
+
+// permute calls cb for each index row, in the sorted order of the rows.
+func (s indexRowGen) permute(cb func([]byte)) {
+ iVec := make([]int, len(s.propVec))
+ iVecLim := make([]int, len(s.propVec))
+
+ incPos := func() bool {
+ for i := len(iVec) - 1; i >= 0; i-- {
+ var done bool
+ var newVal int
+ if s.orders[i] == qASC {
+ newVal = (iVec[i] + 1) % iVecLim[i]
+ done = newVal != 0
+ } else {
+ newVal = (iVec[i] - 1)
+ if newVal < 0 {
+ newVal = iVecLim[i] - 1
+ } else {
+ done = true
+ }
+ }
+ iVec[i] = newVal
+ if done {
+ return true
+ }
+ }
+ return false
+ }
+
+ for i, sps := range s.propVec {
+ iVecLim[i] = len(sps)
+ }
+
+ for i := range iVec {
+ if s.orders[i] == qDEC {
+ iVec[i] = iVecLim[i] - 1
+ }
+ }
+
+ for {
+ bufsiz := 0
+ for pvalSliceIdx, pvalIdx := range iVec {
+ bufsiz += len(s.propVec[pvalSliceIdx][pvalIdx])
+ }
+ buf := bytes.NewBuffer(make([]byte, 0, bufsiz))
+ for pvalSliceIdx, pvalIdx := range iVec {
+ data := s.propVec[pvalSliceIdx][pvalIdx]
+ if s.orders[pvalSliceIdx] == qASC {
+ buf.Write(data)
+ } else {
+ for _, b := range data {
+ buf.WriteByte(b ^ 0xFF)
+ }
+ }
+ }
+ cb(buf.Bytes())
+ if !incPos() {
+ break
+ }
+ }
+}
+
+type matcher struct {
+ buf indexRowGen
+}
+
+// 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 *qIndex, sip serializedIndexablePmap) (indexRowGen, bool) {
+ m.buf.propVec = m.buf.propVec[:0]
+ m.buf.orders = m.buf.orders[:0]
+ for _, sb := range idx.sortby {
+ if pv, ok := sip[sb.prop]; ok {
+ m.buf.propVec = append(m.buf.propVec, pv)
+ m.buf.orders = append(m.buf.orders, sb.dir)
+ } else {
+ return indexRowGen{}, false
+ }
+ }
+ return m.buf, true
+}
+
+func (sip serializedIndexablePmap) indexEntries(k gae.DSKey, idxs []*qIndex) *memStore {
+ ret := newMemStore()
+ idxColl := ret.SetCollection("idx", nil)
+ // getIdxEnts retrieves an index collection or adds it if it's not there.
+ getIdxEnts := func(qi *qIndex) *memCollection {
+ buf := &bytes.Buffer{}
+ qi.WriteBinary(buf)
+ b := buf.Bytes()
+ idxColl.Set(b, []byte{})
+ return ret.SetCollection(fmt.Sprintf("idx:%s:%s", k.Namespace(), b), nil)
+ }
+
+ buf := &bytes.Buffer{}
+ helper.WriteDSKey(buf, helper.WithoutContext, k)
+ keyData := buf.Bytes()
+
+ walkPermutations := func(prefix []byte, irg indexRowGen, ents *memCollection) {
+ prev := []byte{} // intentionally make a non-nil slice, gkvlite hates nil.
+ 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(), prev)
+ prev = data
+ })
+ }
+
+ 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() {
+ buf := &bytes.Buffer{}
+ helper.WriteDSKey(buf, helper.WithoutContext, ancKey)
+ walkPermutations(buf.Bytes(), irg, idxEnts)
+ }
+ } else {
+ walkPermutations(nil, irg, idxEnts)
+ }
+ }
+ }
+
+ return ret
+}
+
+func updateIndicies(store *memStore, key gae.DSKey, oldEnt, newEnt gae.DSPropertyMap) error {
+ var err error
+
+ idxColl := store.GetCollection("idx")
+ if idxColl == nil {
+ idxColl = store.SetCollection("idx", nil)
+ }
+
+ // load all current complex query index definitions.
+ compIdx := []*qIndex{}
+ idxColl.VisitItemsAscend(complexQueryPrefix, false, func(i *gkvlite.Item) bool {
+ if !bytes.HasPrefix(i.Key, complexQueryPrefix) {
+ return false
+ }
+ qi := &qIndex{}
+ if err = qi.ReadBinary(bytes.NewBuffer(i.Key)); err != nil {
+ return false
+ }
+ compIdx = append(compIdx, qi)
+ return true
+ })
+ if err != nil {
+ return err
+ }
+
+ oldIdx := indexEntriesWithBuiltins(key, oldEnt, compIdx)
+
+ newIdx := indexEntriesWithBuiltins(key, newEnt, compIdx)
+
+ prefix := "idx:" + key.Namespace() + ":"
+
+ gkvCollide(oldIdx.GetCollection("idx"), newIdx.GetCollection("idx"), func(k, ov, nv []byte) {
+ ks := prefix + string(k)
+ idxColl.Set(k, []byte{})
+
+ coll := store.GetCollection(ks)
+ if coll == nil {
+ coll = store.SetCollection(ks, nil)
+ }
+ oldColl := oldIdx.GetCollection(ks)
+ newColl := newIdx.GetCollection(ks)
+
+ switch {
+ case ov == nil && nv != nil: // all additions
+ newColl.VisitItemsAscend(nil, false, func(i *gkvlite.Item) bool {
+ coll.Set(i.Key, i.Val)
+ return true
+ })
+ case ov != nil && nv == nil: // all deletions
+ oldColl.VisitItemsAscend(nil, false, func(i *gkvlite.Item) bool {
+ coll.Delete(i.Key)
+ return true
+ })
+ case ov != nil && nv != nil: // merge
+ gkvCollide(oldColl, newColl, func(k, ov, nv []byte) {
+ if nv == nil {
+ coll.Delete(k)
+ } else {
+ coll.Set(k, nv)
+ }
+ })
+ default:
+ panic("impossible")
+ }
+ // TODO(riannucci): remove entries from idxColl and remove index collections
+ // when there are no index entries for that index any more.
+ })
+
+ return nil
+}

Powered by Google App Engine
This is Rietveld 408576698