cosmos-sdk/state/span.go

124 lines
2.6 KiB
Go

package state
import wire "github.com/tendermint/go-wire"
var (
keys = []byte("keys")
// uses dataKey from queue.go to prefix data
)
// Span holds a number of different keys in a large range and allows
// use to make some basic range queries, like highest between, lowest between...
// All items are added with an index
//
// This becomes horribly inefficent as len(keys) => 1000+, but by then
// hopefully we have access to the iavl tree to do this well
//
// TODO: doesn't handle deleting....
type Span struct {
store KVStore
// keys is sorted ascending and cannot contain duplicates
keys []uint64
}
// NewSpan loads or initializes a span of keys
func NewSpan(store KVStore) *Span {
s := &Span{store: store}
s.loadKeys()
return s
}
// Set puts a value at a given height
func (s *Span) Set(h uint64, value []byte) {
key := makeKey(h)
s.store.Set(key, value)
s.addKey(h)
s.storeKeys()
}
// Get returns the element at h if it exists
func (s *Span) Get(h uint64) []byte {
key := makeKey(h)
return s.store.Get(key)
}
// Bottom returns the lowest element in the Span, along with its index
func (s *Span) Bottom() ([]byte, uint64) {
if len(s.keys) == 0 {
return nil, 0
}
h := s.keys[0]
return s.Get(h), h
}
// Top returns the highest element in the Span, along with its index
func (s *Span) Top() ([]byte, uint64) {
l := len(s.keys)
if l == 0 {
return nil, 0
}
h := s.keys[l-1]
return s.Get(h), h
}
// GTE returns the lowest element in the Span that is >= h, along with its index
func (s *Span) GTE(h uint64) ([]byte, uint64) {
for _, k := range s.keys {
if k >= h {
return s.Get(k), k
}
}
return nil, 0
}
// LTE returns the highest element in the Span that is <= h,
// along with its index
func (s *Span) LTE(h uint64) ([]byte, uint64) {
var k uint64
// start from the highest and go down for the first match
for i := len(s.keys) - 1; i >= 0; i-- {
k = s.keys[i]
if k <= h {
return s.Get(k), k
}
}
return nil, 0
}
// addKey inserts this key, maintaining sorted order, no duplicates
func (s *Span) addKey(h uint64) {
for i, k := range s.keys {
// don't add duplicates
if h == k {
return
}
// insert before this key
if h < k {
// https://github.com/golang/go/wiki/SliceTricks
s.keys = append(s.keys, 0)
copy(s.keys[i+1:], s.keys[i:])
s.keys[i] = h
return
}
}
// if it is higher than all (or empty keys), append
s.keys = append(s.keys, h)
}
func (s *Span) loadKeys() {
b := s.store.Get(keys)
if b == nil {
return
}
err := wire.ReadBinaryBytes(b, &s.keys)
// hahaha... just like i love to hate :)
if err != nil {
panic(err)
}
}
func (s *Span) storeKeys() {
b := wire.BinaryBytes(s.keys)
s.store.Set(keys, b)
}