cosmos-sdk/store/v2/db/prefixdb.go

368 lines
8.0 KiB
Go

package db
import (
"bytes"
"fmt"
"sync"
corestore "cosmossdk.io/core/store"
"cosmossdk.io/store/v2/errors"
)
// PrefixDB wraps a namespace of another database as a logical database.
type PrefixDB struct {
mtx sync.Mutex
prefix []byte
db corestore.KVStoreWithBatch
}
var _ corestore.KVStoreWithBatch = (*PrefixDB)(nil)
// NewPrefixDB lets you namespace multiple corestore.KVStores within a single corestore.KVStore.
func NewPrefixDB(db corestore.KVStoreWithBatch, prefix []byte) *PrefixDB {
return &PrefixDB{
prefix: prefix,
db: db,
}
}
// Get implements corestore.KVStore.
func (pdb *PrefixDB) Get(key []byte) ([]byte, error) {
if len(key) == 0 {
return nil, errors.ErrKeyEmpty
}
pkey := pdb.prefixed(key)
value, err := pdb.db.Get(pkey)
if err != nil {
return nil, err
}
return value, nil
}
// Has implements corestore.KVStore.
func (pdb *PrefixDB) Has(key []byte) (bool, error) {
if len(key) == 0 {
return false, errors.ErrKeyEmpty
}
ok, err := pdb.db.Has(pdb.prefixed(key))
if err != nil {
return ok, err
}
return ok, nil
}
// Set implements corestore.KVStore.
func (pdb *PrefixDB) Set(key, value []byte) error {
if len(key) == 0 {
return errors.ErrKeyEmpty
}
return pdb.db.Set(pdb.prefixed(key), value)
}
// Delete implements corestore.KVStore.
func (pdb *PrefixDB) Delete(key []byte) error {
if len(key) == 0 {
return errors.ErrKeyEmpty
}
return pdb.db.Delete(pdb.prefixed(key))
}
// Iterator implements corestore.KVStore.
func (pdb *PrefixDB) Iterator(start, end []byte) (corestore.Iterator, error) {
if (start != nil && len(start) == 0) || (end != nil && len(end) == 0) {
return nil, errors.ErrKeyEmpty
}
var pstart, pend []byte
pstart = append(cp(pdb.prefix), start...)
if end == nil {
pend = cpIncr(pdb.prefix)
} else {
pend = append(cp(pdb.prefix), end...)
}
itr, err := pdb.db.Iterator(pstart, pend)
if err != nil {
return nil, err
}
return newPrefixIterator(pdb.prefix, start, end, itr)
}
// ReverseIterator implements corestore.KVStore.
func (pdb *PrefixDB) ReverseIterator(start, end []byte) (corestore.Iterator, error) {
if (start != nil && len(start) == 0) || (end != nil && len(end) == 0) {
return nil, errors.ErrKeyEmpty
}
var pstart, pend []byte
pstart = append(cp(pdb.prefix), start...)
if end == nil {
pend = cpIncr(pdb.prefix)
} else {
pend = append(cp(pdb.prefix), end...)
}
ritr, err := pdb.db.ReverseIterator(pstart, pend)
if err != nil {
return nil, err
}
return newPrefixIterator(pdb.prefix, start, end, ritr)
}
// NewBatch implements corestore.BatchCreator.
func (pdb *PrefixDB) NewBatch() corestore.Batch {
return newPrefixBatch(pdb.prefix, pdb.db.NewBatch())
}
// NewBatchWithSize implements corestore.BatchCreator.
func (pdb *PrefixDB) NewBatchWithSize(size int) corestore.Batch {
return newPrefixBatch(pdb.prefix, pdb.db.NewBatchWithSize(size))
}
// Close implements corestore.KVStore.
func (pdb *PrefixDB) Close() error {
pdb.mtx.Lock()
defer pdb.mtx.Unlock()
return pdb.db.Close()
}
// Print implements corestore.KVStore.
func (pdb *PrefixDB) Print() error {
fmt.Printf("prefix: %X\n", pdb.prefix)
itr, err := pdb.Iterator(nil, nil)
if err != nil {
return err
}
defer itr.Close()
for ; itr.Valid(); itr.Next() {
key := itr.Key()
value := itr.Value()
fmt.Printf("[%X]:\t[%X]\n", key, value)
}
return nil
}
func (pdb *PrefixDB) prefixed(key []byte) []byte {
return append(cp(pdb.prefix), key...)
}
// IteratePrefix is a convenience function for iterating over a key domain
// restricted by prefix.
func IteratePrefix(db corestore.KVStore, prefix []byte) (corestore.Iterator, error) {
var start, end []byte
if len(prefix) == 0 {
start = nil
end = nil
} else {
start = cp(prefix)
end = cpIncr(prefix)
}
itr, err := db.Iterator(start, end)
if err != nil {
return nil, err
}
return itr, nil
}
// Strips prefix while iterating from Iterator.
type prefixDBIterator struct {
prefix []byte
start []byte
end []byte
source corestore.Iterator
valid bool
err error
}
var _ corestore.Iterator = (*prefixDBIterator)(nil)
func newPrefixIterator(prefix, start, end []byte, source corestore.Iterator) (*prefixDBIterator, error) {
pitrInvalid := &prefixDBIterator{
prefix: prefix,
start: start,
end: end,
source: source,
valid: false,
}
// Empty keys are not allowed, so if a key exists in the database that exactly matches the
// prefix we need to skip it.
if source.Valid() && bytes.Equal(source.Key(), prefix) {
source.Next()
}
if !source.Valid() || !bytes.HasPrefix(source.Key(), prefix) {
return pitrInvalid, nil
}
return &prefixDBIterator{
prefix: prefix,
start: start,
end: end,
source: source,
valid: true,
}, nil
}
// Domain implements Iterator.
func (itr *prefixDBIterator) Domain() (start, end []byte) {
return itr.start, itr.end
}
// Valid implements Iterator.
func (itr *prefixDBIterator) Valid() bool {
if !itr.valid || itr.err != nil || !itr.source.Valid() {
return false
}
key := itr.source.Key()
if len(key) < len(itr.prefix) || !bytes.Equal(key[:len(itr.prefix)], itr.prefix) {
itr.err = fmt.Errorf("received invalid key from backend: %x (expected prefix %x)",
key, itr.prefix)
return false
}
return true
}
// Next implements Iterator.
func (itr *prefixDBIterator) Next() {
itr.assertIsValid()
itr.source.Next()
if !itr.source.Valid() || !bytes.HasPrefix(itr.source.Key(), itr.prefix) {
itr.valid = false
} else if bytes.Equal(itr.source.Key(), itr.prefix) {
// Empty keys are not allowed, so if a key exists in the database that exactly matches the
// prefix we need to skip it.
itr.Next()
}
}
// Key implements Iterator.
func (itr *prefixDBIterator) Key() []byte {
itr.assertIsValid()
key := itr.source.Key()
return key[len(itr.prefix):] // we have checked the key in Valid()
}
// Value implements Iterator.
func (itr *prefixDBIterator) Value() []byte {
itr.assertIsValid()
return itr.source.Value()
}
// Error implements Iterator.
func (itr *prefixDBIterator) Error() error {
if err := itr.source.Error(); err != nil {
return err
}
return itr.err
}
// Close implements Iterator.
func (itr *prefixDBIterator) Close() error {
return itr.source.Close()
}
func (itr *prefixDBIterator) assertIsValid() {
if !itr.Valid() {
panic("iterator is invalid")
}
}
type prefixDBBatch struct {
prefix []byte
source corestore.Batch
}
var _ corestore.Batch = (*prefixDBBatch)(nil)
func newPrefixBatch(prefix []byte, source corestore.Batch) prefixDBBatch {
return prefixDBBatch{
prefix: prefix,
source: source,
}
}
// Set implements corestore.Batch.
func (pb prefixDBBatch) Set(key, value []byte) error {
if len(key) == 0 {
return errors.ErrKeyEmpty
}
if value == nil {
return errors.ErrValueNil
}
pkey := append(cp(pb.prefix), key...)
return pb.source.Set(pkey, value)
}
// Delete implements corestore.Batch.
func (pb prefixDBBatch) Delete(key []byte) error {
if len(key) == 0 {
return errors.ErrKeyEmpty
}
pkey := append(cp(pb.prefix), key...)
return pb.source.Delete(pkey)
}
// Write implements corestore.Batch.
func (pb prefixDBBatch) Write() error {
return pb.source.Write()
}
// WriteSync implements corestore.Batch.
func (pb prefixDBBatch) WriteSync() error {
return pb.source.WriteSync()
}
// Close implements corestore.Batch.
func (pb prefixDBBatch) Close() error {
return pb.source.Close()
}
// GetByteSize implements corestore.Batch
func (pb prefixDBBatch) GetByteSize() (int, error) {
if pb.source == nil {
return 0, errors.ErrBatchClosed
}
return pb.source.GetByteSize()
}
func cp(bz []byte) (ret []byte) {
ret = make([]byte, len(bz))
copy(ret, bz)
return ret
}
// Returns a slice of the same length (big endian)
// except incremented by one.
// Returns nil on overflow (e.g. if bz bytes are all 0xFF)
// CONTRACT: len(bz) > 0
func cpIncr(bz []byte) (ret []byte) {
if len(bz) == 0 {
panic("cpIncr expects non-zero bz length")
}
ret = cp(bz)
for i := len(bz) - 1; i >= 0; i-- {
if ret[i] < byte(0xFF) {
ret[i]++
return
}
ret[i] = byte(0x00)
if i == 0 {
// Overflow
return nil
}
}
return nil
}