Chromium Code Reviews| 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 |
| + } |
| + } |
| +} |