112 lines
2.3 KiB
Go
112 lines
2.3 KiB
Go
|
package state
|
||
|
|
||
|
import (
|
||
|
"container/list"
|
||
|
"fmt"
|
||
|
|
||
|
cmn "github.com/tendermint/tmlibs/common"
|
||
|
)
|
||
|
|
||
|
// KVCache is a cache that enforces deterministic sync order.
|
||
|
type KVCache struct {
|
||
|
store KVStore
|
||
|
cache map[string]kvCacheValue
|
||
|
keys *list.List
|
||
|
logging bool
|
||
|
logLines []string
|
||
|
}
|
||
|
|
||
|
type kvCacheValue struct {
|
||
|
v []byte // The value of some key
|
||
|
e *list.Element // The KVCache.keys element
|
||
|
}
|
||
|
|
||
|
// NOTE: If store is nil, creates a new MemKVStore
|
||
|
func NewKVCache(store KVStore) *KVCache {
|
||
|
if store == nil {
|
||
|
store = NewMemKVStore()
|
||
|
}
|
||
|
return (&KVCache{
|
||
|
store: store,
|
||
|
}).Reset()
|
||
|
}
|
||
|
|
||
|
func (kvc *KVCache) SetLogging() {
|
||
|
kvc.logging = true
|
||
|
}
|
||
|
|
||
|
func (kvc *KVCache) GetLogLines() []string {
|
||
|
return kvc.logLines
|
||
|
}
|
||
|
|
||
|
func (kvc *KVCache) ClearLogLines() {
|
||
|
kvc.logLines = nil
|
||
|
}
|
||
|
|
||
|
func (kvc *KVCache) Reset() *KVCache {
|
||
|
kvc.cache = make(map[string]kvCacheValue)
|
||
|
kvc.keys = list.New()
|
||
|
return kvc
|
||
|
}
|
||
|
|
||
|
func (kvc *KVCache) Set(key []byte, value []byte) {
|
||
|
if kvc.logging {
|
||
|
line := fmt.Sprintf("Set %v = %v", LegibleBytes(key), LegibleBytes(value))
|
||
|
kvc.logLines = append(kvc.logLines, line)
|
||
|
}
|
||
|
cacheValue, ok := kvc.cache[string(key)]
|
||
|
if ok {
|
||
|
kvc.keys.MoveToBack(cacheValue.e)
|
||
|
} else {
|
||
|
cacheValue.e = kvc.keys.PushBack(key)
|
||
|
}
|
||
|
cacheValue.v = value
|
||
|
kvc.cache[string(key)] = cacheValue
|
||
|
}
|
||
|
|
||
|
func (kvc *KVCache) Get(key []byte) (value []byte) {
|
||
|
cacheValue, ok := kvc.cache[string(key)]
|
||
|
if ok {
|
||
|
if kvc.logging {
|
||
|
line := fmt.Sprintf("Get (hit) %v = %v", LegibleBytes(key), LegibleBytes(cacheValue.v))
|
||
|
kvc.logLines = append(kvc.logLines, line)
|
||
|
}
|
||
|
return cacheValue.v
|
||
|
} else {
|
||
|
value := kvc.store.Get(key)
|
||
|
kvc.cache[string(key)] = kvCacheValue{
|
||
|
v: value,
|
||
|
e: kvc.keys.PushBack(key),
|
||
|
}
|
||
|
if kvc.logging {
|
||
|
line := fmt.Sprintf("Get (miss) %v = %v", LegibleBytes(key), LegibleBytes(value))
|
||
|
kvc.logLines = append(kvc.logLines, line)
|
||
|
}
|
||
|
return value
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Update the store with the values from the cache
|
||
|
func (kvc *KVCache) Sync() {
|
||
|
for e := kvc.keys.Front(); e != nil; e = e.Next() {
|
||
|
key := e.Value.([]byte)
|
||
|
value := kvc.cache[string(key)]
|
||
|
kvc.store.Set(key, value.v)
|
||
|
}
|
||
|
kvc.Reset()
|
||
|
}
|
||
|
|
||
|
//----------------------------------------
|
||
|
|
||
|
func LegibleBytes(data []byte) string {
|
||
|
s := ""
|
||
|
for _, b := range data {
|
||
|
if 0x21 <= b && b < 0x7F {
|
||
|
s += cmn.Green(string(b))
|
||
|
} else {
|
||
|
s += cmn.Blue(cmn.Fmt("%02X", b))
|
||
|
}
|
||
|
}
|
||
|
return s
|
||
|
}
|