124 lines
2.6 KiB
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)
|
||
|
}
|