package db
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
func init() {
|
|
registerDBCreator(MemDBBackendStr, func(name string, dir string) (DB, error) {
|
|
return NewMemDB(), nil
|
|
}, false)
|
|
}
|
|
|
|
type MemDB struct {
|
|
mtx sync.Mutex
|
|
db map[string][]byte
|
|
}
|
|
|
|
func NewMemDB() *MemDB {
|
|
database := &MemDB{db: make(map[string][]byte)}
|
|
return database
|
|
}
|
|
|
|
func (db *MemDB) Get(key []byte) []byte {
|
|
db.mtx.Lock()
|
|
defer db.mtx.Unlock()
|
|
return db.db[string(key)]
|
|
}
|
|
|
|
func (db *MemDB) Set(key []byte, value []byte) {
|
|
db.mtx.Lock()
|
|
defer db.mtx.Unlock()
|
|
db.db[string(key)] = value
|
|
}
|
|
|
|
func (db *MemDB) SetSync(key []byte, value []byte) {
|
|
db.mtx.Lock()
|
|
defer db.mtx.Unlock()
|
|
db.db[string(key)] = value
|
|
}
|
|
|
|
func (db *MemDB) Delete(key []byte) {
|
|
db.mtx.Lock()
|
|
defer db.mtx.Unlock()
|
|
delete(db.db, string(key))
|
|
}
|
|
|
|
func (db *MemDB) DeleteSync(key []byte) {
|
|
db.mtx.Lock()
|
|
defer db.mtx.Unlock()
|
|
delete(db.db, string(key))
|
|
}
|
|
|
|
func (db *MemDB) Close() {
|
|
// Close is a noop since for an in-memory
|
|
// database, we don't have a destination
|
|
// to flush contents to nor do we want
|
|
// any data loss on invoking Close()
|
|
// See the discussion in https://github.com/tendermint/tmlibs/pull/56
|
|
}
|
|
|
|
func (db *MemDB) Print() {
|
|
db.mtx.Lock()
|
|
defer db.mtx.Unlock()
|
|
for key, value := range db.db {
|
|
fmt.Printf("[%X]:\t[%X]\n", []byte(key), value)
|
|
}
|
|
}
|
|
|
|
func (db *MemDB) Stats() map[string]string {
|
|
stats := make(map[string]string)
|
|
stats["database.type"] = "memDB"
|
|
return stats
|
|
}
|
|
|
|
type memDBIterator struct {
|
|
last int
|
|
keys []string
|
|
db *MemDB
|
|
}
|
|
|
|
func newMemDBIterator() *memDBIterator {
|
|
return &memDBIterator{}
|
|
}
|
|
|
|
func (it *memDBIterator) Next() bool {
|
|
if it.last >= len(it.keys)-1 {
|
|
return false
|
|
}
|
|
it.last++
|
|
return true
|
|
}
|
|
|
|
func (it *memDBIterator) Key() []byte {
|
|
return []byte(it.keys[it.last])
|
|
}
|
|
|
|
func (it *memDBIterator) Value() []byte {
|
|
return it.db.Get(it.Key())
|
|
}
|
|
|
|
func (it *memDBIterator) Release() {
|
|
it.db = nil
|
|
it.keys = nil
|
|
}
|
|
|
|
func (it *memDBIterator) Error() error {
|
|
return nil
|
|
}
|
|
|
|
func (db *MemDB) Iterator() Iterator {
|
|
return db.IteratorPrefix([]byte{})
|
|
}
|
|
|
|
func (db *MemDB) IteratorPrefix(prefix []byte) Iterator {
|
|
it := newMemDBIterator()
|
|
it.db = db
|
|
it.last = -1
|
|
|
|
db.mtx.Lock()
|
|
defer db.mtx.Unlock()
|
|
|
|
// unfortunately we need a copy of all of the keys
|
|
for key, _ := range db.db {
|
|
if strings.HasPrefix(key, string(prefix)) {
|
|
it.keys = append(it.keys, key)
|
|
}
|
|
}
|
|
// and we need to sort them
|
|
sort.Strings(it.keys)
|
|
return it
|
|
}
|
|
|
|
func (db *MemDB) NewBatch() Batch {
|
|
return &memDBBatch{db, nil}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------
|
|
|
|
type memDBBatch struct {
|
|
db *MemDB
|
|
ops []operation
|
|
}
|
|
|
|
type opType int
|
|
|
|
const (
|
|
opTypeSet = 1
|
|
opTypeDelete = 2
|
|
)
|
|
|
|
type operation struct {
|
|
opType
|
|
key []byte
|
|
value []byte
|
|
}
|
|
|
|
func (mBatch *memDBBatch) Set(key, value []byte) {
|
|
mBatch.ops = append(mBatch.ops, operation{opTypeSet, key, value})
|
|
}
|
|
|
|
func (mBatch *memDBBatch) Delete(key []byte) {
|
|
mBatch.ops = append(mBatch.ops, operation{opTypeDelete, key, nil})
|
|
}
|
|
|
|
func (mBatch *memDBBatch) Write() {
|
|
mBatch.db.mtx.Lock()
|
|
defer mBatch.db.mtx.Unlock()
|
|
|
|
for _, op := range mBatch.ops {
|
|
if op.opType == opTypeSet {
|
|
mBatch.db.db[string(op.key)] = op.value
|
|
} else if op.opType == opTypeDelete {
|
|
delete(mBatch.db.db, string(op.key))
|
|
}
|
|
}
|
|
|
|
}
|