Index: impl/memory/memstore.go |
diff --git a/impl/memory/memstore.go b/impl/memory/memstore.go |
new file mode 100644 |
index 0000000000000000000000000000000000000000..17e6fc03b224249f0fa36e3a763bc62a7007ed9f |
--- /dev/null |
+++ b/impl/memory/memstore.go |
@@ -0,0 +1,213 @@ |
+// 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" |
+ |
+ "github.com/luci/gtreap" |
+ "github.com/luci/luci-go/common/data/treapstore" |
+) |
+ |
+type storeEntry struct { |
+ key []byte |
+ value []byte |
+} |
+ |
+// storeEntryCompare is a gtrea.Compare function for *storeEntry. |
+func storeEntryCompare(a, b interface{}) int { |
+ return bytes.Compare(a.(*storeEntry).key, b.(*storeEntry).key) |
+} |
+ |
+func memStoreCollide(o, n memCollection, f func(k, ov, nv []byte)) { |
+ var oldIter, newIter memIterator |
+ if o != nil { |
+ if !o.IsReadOnly() { |
+ panic("old collection is r/w") |
+ } |
+ oldIter = o.Iterator(nil) |
+ } else { |
+ oldIter = nilIterator{} |
+ } |
+ |
+ if n != nil { |
+ if !n.IsReadOnly() { |
+ panic("new collection is r/w") |
+ } |
+ newIter = n.Iterator(nil) |
+ } else { |
+ newIter = nilIterator{} |
+ } |
+ |
+ l, r := oldIter.Next(), newIter.Next() |
+ for { |
+ switch { |
+ case l == nil && r == nil: |
+ return |
+ |
+ case l == nil: |
+ f(r.key, nil, r.value) |
Vadim Sh.
2016/12/28 01:57:05
nit: this can be micro optimized by avoiding calls
dnj
2016/12/28 02:52:15
I was initially trying to stay as close to the ori
|
+ r = newIter.Next() |
+ |
+ case r == nil: |
+ f(l.key, l.value, nil) |
+ l = oldIter.Next() |
+ |
+ default: |
+ switch bytes.Compare(l.key, r.key) { |
+ case -1: // l < r |
+ f(l.key, l.value, nil) |
+ l = oldIter.Next() |
+ case 0: // l == r |
+ f(l.key, l.value, r.value) |
+ l, r = oldIter.Next(), newIter.Next() |
+ case 1: // l > r |
+ f(r.key, nil, r.value) |
+ r = newIter.Next() |
+ } |
+ } |
+ } |
+} |
+ |
+// memStore is a treapstore.Store which will panic for anything which might |
+// otherwise return an error. |
+// |
+// This is reasonable for in-memory Store objects, since the only errors that |
+// should occur happen with file IO on the underlying file (which of course |
Vadim Sh.
2016/12/28 01:57:05
the comment is probably stale. There should be no
dnj
2016/12/28 02:52:15
Done.
|
+// doesn't exist). |
+type memStore interface { |
+ datastore.TestingSnapshot |
+ |
+ GetCollection(name string) memCollection |
+ GetCollectionNames() []string |
+ GetOrCreateCollection(name string) memCollection |
+ Snapshot() memStore |
+ |
+ IsReadOnly() bool |
+} |
+ |
+// memIterator is an iterator over a memStore's contents. |
+type memIterator interface { |
+ Next() *storeEntry |
+} |
+ |
+// memCollection is a treapstore.Collection which will panic for anything which |
+// might otherwise return an error. |
+// |
+// This is reasonable for in-memory Store objects, since the only errors that |
+// should occur happen with file IO on the underlying file (which of course |
+// doesn't exist. |
+type memCollection interface { |
+ Name() string |
+ Delete(k []byte) |
+ Get(k []byte) []byte |
+ MinItem() *storeEntry |
+ Set(k, v []byte) |
+ Iterator(pivot []byte) memIterator |
+ |
+ IsReadOnly() bool |
+} |
+ |
+type memStoreImpl struct { |
+ s *treapstore.Store |
+} |
+ |
+var _ memStore = (*memStoreImpl)(nil) |
+ |
+func (*memStoreImpl) ImATestingSnapshot() {} |
+ |
+func (ms *memStoreImpl) IsReadOnly() bool { return ms.s.IsReadOnly() } |
+ |
+func newMemStore() memStore { |
+ ret := memStore(&memStoreImpl{treapstore.New(storeEntryCompare)}) |
+ if *logMemCollectionFolder != "" { |
+ ret = wrapTracingMemStore(ret) |
+ } |
+ return ret |
+} |
+ |
+func (ms *memStoreImpl) Snapshot() memStore { |
+ if ms.s.IsReadOnly() { |
+ return ms |
+ } |
+ return &memStoreImpl{ms.s.Snapshot()} |
+} |
+ |
+func (ms *memStoreImpl) GetCollection(name string) memCollection { |
+ coll := ms.s.GetCollection(name) |
+ if coll == nil { |
+ return nil |
+ } |
+ return &memCollectionImpl{coll} |
+} |
+ |
+func (ms *memStoreImpl) GetOrCreateCollection(name string) memCollection { |
+ return &memCollectionImpl{ms.s.GetOrCreateCollection(name)} |
+} |
+ |
+func (ms *memStoreImpl) GetCollectionNames() []string { return ms.s.GetCollectionNames() } |
+ |
+type memIteratorImpl struct { |
+ base *gtreap.Iterator |
+} |
+ |
+func (it *memIteratorImpl) Next() *storeEntry { |
+ v, ok := it.base.Next() |
+ if !ok { |
+ return nil |
+ } |
+ return v.(*storeEntry) |
+} |
+ |
+type memCollectionImpl struct { |
+ c *treapstore.Collection |
+} |
+ |
+var _ memCollection = (*memCollectionImpl)(nil) |
+ |
+func (mc *memCollectionImpl) Name() string { return mc.c.Name() } |
+func (mc *memCollectionImpl) IsReadOnly() bool { return mc.c.IsReadOnly() } |
+ |
+func (mc *memCollectionImpl) Get(k []byte) []byte { |
+ if ent := mc.c.Get(storeKey(k)); ent != nil { |
+ return ent.(*storeEntry).value |
+ } |
+ return nil |
+} |
+ |
+func (mc *memCollectionImpl) MinItem() *storeEntry { |
+ ent, _ := mc.c.Min().(*storeEntry) |
+ return ent |
+} |
+ |
+func (mc *memCollectionImpl) Set(k, v []byte) { mc.c.Put(&storeEntry{k, v}) } |
+func (mc *memCollectionImpl) Delete(k []byte) { mc.c.Delete(storeKey(k)) } |
+ |
+func (mc *memCollectionImpl) Iterator(target []byte) memIterator { |
+ if !mc.c.IsReadOnly() { |
+ // We prevent this to ensure our internal logic, not because it's actually |
+ // an invalid operation. |
+ panic("attempting to get Iterator from r/w memCollection") |
+ } |
+ return &memIteratorImpl{mc.c.Iterator(storeKey(target))} |
+} |
+ |
+func storeKey(k []byte) *storeEntry { return &storeEntry{k, nil} } |
+ |
+// nilIterator is a memIterator that begins in a depleted state. |
+type nilIterator struct{} |
+ |
+func (it nilIterator) Next() *storeEntry { return nil } |
+ |
+func forEachItem(mc memCollection, cb func(k, v []byte) bool) { |
+ it := mc.Iterator(nil) |
+ for ent := it.Next(); ent != nil; ent = it.Next() { |
+ if !cb(ent.key, ent.value) { |
+ break |
+ } |
+ } |
+} |