cosmos-sdk/blockstm/memdb.go
Eric Warehime 825fd62088
feat: Upstream BlockSTM Fork (#25483)
Co-authored-by: yihuang <huang@crypto.com>
Co-authored-by: mmsqe <mavis@crypto.com>
Co-authored-by: mmsqe <tqd0800210105@gmail.com>
Co-authored-by: Tyler <48813565+technicallyty@users.noreply.github.com>
2025-10-24 15:51:57 +00:00

219 lines
5.5 KiB
Go

package blockstm
import (
"io"
"github.com/tidwall/btree"
"cosmossdk.io/store/cachekv"
"cosmossdk.io/store/tracekv"
storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/blockstm/tree"
)
type (
MemDB = GMemDB[[]byte]
ObjMemDB = GMemDB[any]
)
var (
_ storetypes.KVStore = (*MemDB)(nil)
_ storetypes.ObjKVStore = (*ObjMemDB)(nil)
)
// NewMemDB constructs a new in memory store over a []byte value type.
func NewMemDB() *MemDB {
return NewGMemDB(storetypes.BytesIsZero, storetypes.BytesValueLen)
}
// NewObjMemDB constructs a new in memory store over a generic any type.
func NewObjMemDB() *ObjMemDB {
return NewGMemDB(storetypes.AnyIsZero, storetypes.AnyValueLen)
}
// GMemDB is a generic implementation of an in memory Store backed by tidwall/btree.
type GMemDB[V any] struct {
btree.BTreeG[memdbItem[V]]
isZero func(V) bool
valueLen func(V) int
}
// NewGMemDB is the generic constructor for a GMemDB.
func NewGMemDB[V any](
isZero func(V) bool,
valueLen func(V) int,
) *GMemDB[V] {
return &GMemDB[V]{
BTreeG: *btree.NewBTreeG[memdbItem[V]](tree.KeyItemLess),
isZero: isZero,
valueLen: valueLen,
}
}
// NewGMemDBNonConcurrent returns a new BTree which is not concurrency safe.
func NewGMemDBNonConcurrent[V any](
isZero func(V) bool,
valueLen func(V) int,
) *GMemDB[V] {
return &GMemDB[V]{
BTreeG: *btree.NewBTreeGOptions[memdbItem[V]](tree.KeyItemLess, btree.Options{
NoLocks: true,
}),
isZero: isZero,
valueLen: valueLen,
}
}
func (db *GMemDB[V]) Scan(cb func(key Key, value V) bool) {
db.BTreeG.Scan(func(item memdbItem[V]) bool {
return cb(item.key, item.value)
})
}
func (db *GMemDB[V]) Get(key []byte) V {
item, ok := db.BTreeG.Get(memdbItem[V]{key: key})
if !ok {
var empty V
return empty
}
return item.value
}
func (db *GMemDB[V]) Has(key []byte) bool {
return !db.isZero(db.Get(key))
}
func (db *GMemDB[V]) Set(key []byte, value V) {
if db.isZero(value) {
panic("nil value not allowed")
}
db.BTreeG.Set(memdbItem[V]{key: key, value: value})
}
func (db *GMemDB[V]) Delete(key []byte) {
db.BTreeG.Delete(memdbItem[V]{key: key})
}
// OverlayGet returns a value from the btree and true if we found a value.
// When used as an overlay (e.g. WriteSet), it stores the `nil` value to represent deleted keys,
// so we return separate bool value for found status.
func (db *GMemDB[V]) OverlayGet(key Key) (V, bool) {
item, ok := db.BTreeG.Get(memdbItem[V]{key: key})
if !ok {
var zero V
return zero, false
}
return item.value, true
}
// OverlaySet sets a value in the btree
// When used as an overlay (e.g. WriteSet), it stores the `nil` value to represent deleted keys,
func (db *GMemDB[V]) OverlaySet(key Key, value V) {
db.BTreeG.Set(memdbItem[V]{key: key, value: value})
}
func (db *GMemDB[V]) Iterator(start, end []byte) storetypes.GIterator[V] {
return db.iterator(start, end, true)
}
func (db *GMemDB[V]) ReverseIterator(start, end []byte) storetypes.GIterator[V] {
return db.iterator(start, end, false)
}
func (db *GMemDB[V]) iterator(start, end Key, ascending bool) storetypes.GIterator[V] {
return NewMemDBIterator(start, end, db.Iter(), ascending)
}
func (db *GMemDB[V]) GetStoreType() storetypes.StoreType {
return storetypes.StoreTypeIAVL
}
// CacheWrap implements types.KVStore.
func (db *GMemDB[V]) CacheWrap() storetypes.CacheWrap {
return cachekv.NewGStore(db, db.isZero, db.valueLen)
}
// CacheWrapWithTrace implements types.KVStore.
func (db *GMemDB[V]) CacheWrapWithTrace(w io.Writer, tc storetypes.TraceContext) storetypes.CacheWrap {
if store, ok := any(db).(*GMemDB[[]byte]); ok {
return cachekv.NewGStore(tracekv.NewStore(store, w, tc), store.isZero, store.valueLen)
}
return db.CacheWrap()
}
// MemDBIterator wraps a generic BTreeIteratorG over a memdbItem.
// It is used as an iterator over a GMemDB implementation.
type MemDBIterator[V any] struct {
tree.BTreeIteratorG[memdbItem[V]]
}
var _ storetypes.Iterator = (*MemDBIterator[[]byte])(nil)
func NewMemDBIterator[V any](start, end Key, iter btree.IterG[memdbItem[V]], ascending bool) *MemDBIterator[V] {
return &MemDBIterator[V]{*tree.NewBTreeIteratorG(
memdbItem[V]{key: start},
memdbItem[V]{key: end},
iter,
ascending,
)}
}
// NewNoopIterator constructs a storetypes.GIterator with an invalidated wrapped iterator.
func NewNoopIterator[V any](start, end Key, ascending bool) storetypes.GIterator[V] {
return &MemDBIterator[V]{tree.NewNoopBTreeIteratorG[memdbItem[V]](
start,
end,
ascending,
false,
)}
}
func (it *MemDBIterator[V]) Value() V {
return it.Item().value
}
type memdbItem[V any] struct {
key Key
value V
}
var _ tree.KeyItem = memdbItem[[]byte]{}
func (item memdbItem[V]) GetKey() []byte {
return item.key
}
type MultiMemDB struct {
dbs map[storetypes.StoreKey]storetypes.Store
}
var _ MultiStore = (*MultiMemDB)(nil)
func NewMultiMemDB(stores map[storetypes.StoreKey]int) *MultiMemDB {
dbs := make(map[storetypes.StoreKey]storetypes.Store, len(stores))
for name := range stores {
switch name.(type) {
case *storetypes.ObjectStoreKey:
dbs[name] = NewObjMemDB()
default:
dbs[name] = NewMemDB()
}
}
return &MultiMemDB{
dbs: dbs,
}
}
func (mmdb *MultiMemDB) GetStore(store storetypes.StoreKey) storetypes.Store {
return mmdb.dbs[store]
}
func (mmdb *MultiMemDB) GetKVStore(store storetypes.StoreKey) storetypes.KVStore {
return mmdb.GetStore(store).(storetypes.KVStore)
}
func (mmdb *MultiMemDB) GetObjKVStore(store storetypes.StoreKey) storetypes.ObjKVStore {
return mmdb.GetStore(store).(storetypes.ObjKVStore)
}