| Index: impl/memory/memstore.go
|
| diff --git a/impl/memory/memstore.go b/impl/memory/memstore.go
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..b1f7c7c5820788ba8b31336bf0ae0a460043d22c
|
| --- /dev/null
|
| +++ b/impl/memory/memstore.go
|
| @@ -0,0 +1,217 @@
|
| +// 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 gtreap.Compare function for *storeEntry.
|
| +func storeEntryCompare(a, b interface{}) int {
|
| + // TODO(dnj): Investigate optimizing this by removing the type assertions,
|
| + // either by explicitly tailoring gtreap / treapstore to use []byte or by
|
| + // optimizing via special-case interface.
|
| + 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:
|
| + // No more "old" items, use up the rest of "new" and finish.
|
| + for r != nil {
|
| + f(r.key, nil, r.value)
|
| + r = newIter.Next()
|
| + }
|
| + return
|
| +
|
| + case r == nil:
|
| + // No more "new" items, use up the rest of "old" and finish.
|
| + for l != nil {
|
| + f(l.key, l.value, nil)
|
| + l = oldIter.Next()
|
| + }
|
| + return
|
| +
|
| + 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 generic interface modeled after treapstore.Store.
|
| +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
|
| +}
|
| +
|
| +// memVisitor is a callback for ForEachItem.
|
| +type memVisitor func(k, v []byte) bool
|
| +
|
| +// memCollection is a generic interface modeled after treapstore.Collection.
|
| +type memCollection interface {
|
| + Name() string
|
| + Delete(k []byte)
|
| + Get(k []byte) []byte
|
| + MinItem() *storeEntry
|
| + Set(k, v []byte)
|
| + Iterator(pivot []byte) memIterator
|
| + ForEachItem(memVisitor)
|
| +
|
| + 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()})
|
| + 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 {
|
| + coll := ms.s.GetCollection(name)
|
| + if coll == nil {
|
| + coll = ms.s.CreateCollection(name, storeEntryCompare)
|
| + }
|
| + return &memCollectionImpl{coll}
|
| +}
|
| +
|
| +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 (mc *memCollectionImpl) ForEachItem(fn memVisitor) {
|
| + mc.c.VisitAscend(storeKey(nil), func(it gtreap.Item) bool {
|
| + ent := it.(*storeEntry)
|
| + return fn(ent.key, ent.value)
|
| + })
|
| +}
|
| +
|
| +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 }
|
|
|