Add element type param T to SlicedBacked[T]. Require T satisfy
constraints.Ordered to make BisectRight() statically type-safe.
This commit is contained in:
parent
9d9c73f97f
commit
8ac89195db
5 changed files with 55 additions and 42 deletions
15
db/db.go
15
db/db.go
|
@ -48,10 +48,10 @@ type ReadOnlyDBColumnFamily struct {
|
||||||
DB *grocksdb.DB
|
DB *grocksdb.DB
|
||||||
Handles map[string]*grocksdb.ColumnFamilyHandle
|
Handles map[string]*grocksdb.ColumnFamilyHandle
|
||||||
Opts *grocksdb.ReadOptions
|
Opts *grocksdb.ReadOptions
|
||||||
TxCounts *stack.SliceBacked
|
TxCounts *stack.SliceBacked[uint32]
|
||||||
Height uint32
|
Height uint32
|
||||||
LastState *prefixes.DBStateValue
|
LastState *prefixes.DBStateValue
|
||||||
Headers *stack.SliceBacked
|
Headers *stack.SliceBacked[[]byte]
|
||||||
BlockingChannelHashes [][]byte
|
BlockingChannelHashes [][]byte
|
||||||
FilteringChannelHashes [][]byte
|
FilteringChannelHashes [][]byte
|
||||||
BlockedStreams map[string][]byte
|
BlockedStreams map[string][]byte
|
||||||
|
@ -556,7 +556,7 @@ func (db *ReadOnlyDBColumnFamily) Advance(height uint32) {
|
||||||
}
|
}
|
||||||
txCount := txCountObj.TxCount
|
txCount := txCountObj.TxCount
|
||||||
|
|
||||||
if db.TxCounts.GetTip().(uint32) >= txCount {
|
if db.TxCounts.GetTip() >= txCount {
|
||||||
log.Error("current tip should be less than new txCount",
|
log.Error("current tip should be less than new txCount",
|
||||||
"tx count tip:", db.TxCounts.GetTip(), "tx count:", txCount)
|
"tx count tip:", db.TxCounts.GetTip(), "tx count:", txCount)
|
||||||
}
|
}
|
||||||
|
@ -636,11 +636,10 @@ func (db *ReadOnlyDBColumnFamily) detectChanges(notifCh chan *internal.HeightHas
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
curHeaderObj := db.Headers.GetTip()
|
curHeader := db.Headers.GetTip()
|
||||||
if curHeaderObj == nil {
|
if curHeader == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
curHeader := curHeaderObj.([]byte)
|
|
||||||
log.Debugln("lastHeightHeader: ", hex.EncodeToString(lastHeightHeader))
|
log.Debugln("lastHeightHeader: ", hex.EncodeToString(lastHeightHeader))
|
||||||
log.Debugln("curHeader: ", hex.EncodeToString(curHeader))
|
log.Debugln("curHeader: ", hex.EncodeToString(curHeader))
|
||||||
if bytes.Equal(curHeader, lastHeightHeader) {
|
if bytes.Equal(curHeader, lastHeightHeader) {
|
||||||
|
@ -716,7 +715,7 @@ func (db *ReadOnlyDBColumnFamily) InitHeaders() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: figure out a reasonable default and make it a constant
|
//TODO: figure out a reasonable default and make it a constant
|
||||||
db.Headers = stack.NewSliceBacked(12000)
|
db.Headers = stack.NewSliceBacked[[]byte](12000)
|
||||||
|
|
||||||
startKey := prefixes.NewHeaderKey(0)
|
startKey := prefixes.NewHeaderKey(0)
|
||||||
// endKey := prefixes.NewHeaderKey(db.LastState.Height)
|
// endKey := prefixes.NewHeaderKey(db.LastState.Height)
|
||||||
|
@ -743,7 +742,7 @@ func (db *ReadOnlyDBColumnFamily) InitTxCounts() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
db.TxCounts = stack.NewSliceBacked(InitialTxCountSize)
|
db.TxCounts = stack.NewSliceBacked[uint32](InitialTxCountSize)
|
||||||
|
|
||||||
options := NewIterateOptions().WithPrefix([]byte{prefixes.TxCount}).WithCfHandle(handle)
|
options := NewIterateOptions().WithPrefix([]byte{prefixes.TxCount}).WithCfHandle(handle)
|
||||||
options = options.WithIncludeKey(false).WithIncludeValue(true).WithIncludeStop(true)
|
options = options.WithIncludeKey(false).WithIncludeValue(true).WithIncludeStop(true)
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/lbryio/herald.go/db/prefixes"
|
"github.com/lbryio/herald.go/db/prefixes"
|
||||||
|
"github.com/lbryio/herald.go/db/stack"
|
||||||
"github.com/lbryio/herald.go/internal"
|
"github.com/lbryio/herald.go/internal"
|
||||||
pb "github.com/lbryio/herald.go/protobuf/go"
|
pb "github.com/lbryio/herald.go/protobuf/go"
|
||||||
lbryurl "github.com/lbryio/lbry.go/v3/url"
|
lbryurl "github.com/lbryio/lbry.go/v3/url"
|
||||||
|
@ -40,7 +41,8 @@ func PrepareResolveResult(
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
height, createdHeight := db.TxCounts.TxCountsBisectRight(txNum, rootTxNum)
|
heights := stack.BisectRight(db.TxCounts, []uint32{txNum, rootTxNum})
|
||||||
|
height, createdHeight := heights[0], heights[1]
|
||||||
lastTakeoverHeight := controllingClaim.Height
|
lastTakeoverHeight := controllingClaim.Height
|
||||||
|
|
||||||
expirationHeight := GetExpirationHeight(height)
|
expirationHeight := GetExpirationHeight(height)
|
||||||
|
@ -86,7 +88,7 @@ func PrepareResolveResult(
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
repostTxPostition = repostTxo.Position
|
repostTxPostition = repostTxo.Position
|
||||||
repostHeight, _ = db.TxCounts.TxCountsBisectRight(repostTxo.TxNum, rootTxNum)
|
repostHeight = stack.BisectRight(db.TxCounts, []uint32{repostTxo.TxNum})[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,7 +124,7 @@ func PrepareResolveResult(
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
channelTxPostition = channelVals.Position
|
channelTxPostition = channelVals.Position
|
||||||
channelHeight, _ = db.TxCounts.TxCountsBisectRight(channelVals.TxNum, rootTxNum)
|
channelHeight = stack.BisectRight(db.TxCounts, []uint32{channelVals.TxNum})[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,23 +7,24 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/lbryio/herald.go/internal"
|
"github.com/lbryio/herald.go/internal"
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SliceBacked struct {
|
type SliceBacked[T any] struct {
|
||||||
slice []interface{}
|
slice []T
|
||||||
len uint32
|
len uint32
|
||||||
mut sync.RWMutex
|
mut sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSliceBacked(size int) *SliceBacked {
|
func NewSliceBacked[T any](size int) *SliceBacked[T] {
|
||||||
return &SliceBacked{
|
return &SliceBacked[T]{
|
||||||
slice: make([]interface{}, size),
|
slice: make([]T, size),
|
||||||
len: 0,
|
len: 0,
|
||||||
mut: sync.RWMutex{},
|
mut: sync.RWMutex{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SliceBacked) Push(v interface{}) {
|
func (s *SliceBacked[T]) Push(v T) {
|
||||||
s.mut.Lock()
|
s.mut.Lock()
|
||||||
defer s.mut.Unlock()
|
defer s.mut.Unlock()
|
||||||
|
|
||||||
|
@ -35,64 +36,67 @@ func (s *SliceBacked) Push(v interface{}) {
|
||||||
s.len++
|
s.len++
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SliceBacked) Pop() interface{} {
|
func (s *SliceBacked[T]) Pop() T {
|
||||||
s.mut.Lock()
|
s.mut.Lock()
|
||||||
defer s.mut.Unlock()
|
defer s.mut.Unlock()
|
||||||
|
|
||||||
if s.len == 0 {
|
if s.len == 0 {
|
||||||
return nil
|
var null T
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
s.len--
|
s.len--
|
||||||
return s.slice[s.len]
|
return s.slice[s.len]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SliceBacked) Get(i uint32) interface{} {
|
func (s *SliceBacked[T]) Get(i uint32) T {
|
||||||
s.mut.RLock()
|
s.mut.RLock()
|
||||||
defer s.mut.RUnlock()
|
defer s.mut.RUnlock()
|
||||||
|
|
||||||
if i >= s.len {
|
if i >= s.len {
|
||||||
return nil
|
var null T
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
return s.slice[i]
|
return s.slice[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SliceBacked) GetTip() interface{} {
|
func (s *SliceBacked[T]) GetTip() T {
|
||||||
s.mut.RLock()
|
s.mut.RLock()
|
||||||
defer s.mut.RUnlock()
|
defer s.mut.RUnlock()
|
||||||
|
|
||||||
if s.len == 0 {
|
if s.len == 0 {
|
||||||
return nil
|
var null T
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
return s.slice[s.len-1]
|
return s.slice[s.len-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SliceBacked) Len() uint32 {
|
func (s *SliceBacked[T]) Len() uint32 {
|
||||||
s.mut.RLock()
|
s.mut.RLock()
|
||||||
defer s.mut.RUnlock()
|
defer s.mut.RUnlock()
|
||||||
|
|
||||||
return s.len
|
return s.len
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SliceBacked) Cap() int {
|
func (s *SliceBacked[T]) Cap() int {
|
||||||
s.mut.RLock()
|
s.mut.RLock()
|
||||||
defer s.mut.RUnlock()
|
defer s.mut.RUnlock()
|
||||||
|
|
||||||
return cap(s.slice)
|
return cap(s.slice)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SliceBacked) GetSlice() []interface{} {
|
func (s *SliceBacked[T]) GetSlice() []T {
|
||||||
// This is not thread safe so I won't bother with locking
|
// This is not thread safe so I won't bother with locking
|
||||||
return s.slice
|
return s.slice
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is dangerous because it assumes underlying types
|
func BisectRight[T constraints.Ordered](s *SliceBacked[T], searchKeys []T) []uint32 {
|
||||||
func (s *SliceBacked) TxCountsBisectRight(txNum, rootTxNum uint32) (uint32, uint32) {
|
|
||||||
s.mut.RLock()
|
s.mut.RLock()
|
||||||
defer s.mut.RUnlock()
|
defer s.mut.RUnlock()
|
||||||
|
|
||||||
txCounts := s.slice[:s.Len()]
|
found := make([]uint32, len(searchKeys))
|
||||||
height := internal.BisectRight(txCounts, txNum)
|
for i, k := range searchKeys {
|
||||||
createdHeight := internal.BisectRight(txCounts, rootTxNum)
|
found[i] = internal.BisectRight(s.slice[:s.Len()], k)
|
||||||
|
}
|
||||||
|
|
||||||
return height, createdHeight
|
return found
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
func TestPush(t *testing.T) {
|
func TestPush(t *testing.T) {
|
||||||
var want uint32 = 3
|
var want uint32 = 3
|
||||||
|
|
||||||
stack := stack.NewSliceBacked(10)
|
stack := stack.NewSliceBacked[int](10)
|
||||||
|
|
||||||
stack.Push(0)
|
stack.Push(0)
|
||||||
stack.Push(1)
|
stack.Push(1)
|
||||||
|
@ -22,7 +22,7 @@ func TestPush(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPushPop(t *testing.T) {
|
func TestPushPop(t *testing.T) {
|
||||||
stack := stack.NewSliceBacked(10)
|
stack := stack.NewSliceBacked[int](10)
|
||||||
|
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
stack.Push(i)
|
stack.Push(i)
|
||||||
|
@ -46,20 +46,20 @@ func TestPushPop(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func doPushes(stack *stack.SliceBacked, numPushes int) {
|
func doPushes(stack *stack.SliceBacked[int], numPushes int) {
|
||||||
for i := 0; i < numPushes; i++ {
|
for i := 0; i < numPushes; i++ {
|
||||||
stack.Push(i)
|
stack.Push(i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func doPops(stack *stack.SliceBacked, numPops int) {
|
func doPops(stack *stack.SliceBacked[int], numPops int) {
|
||||||
for i := 0; i < numPops; i++ {
|
for i := 0; i < numPops; i++ {
|
||||||
stack.Pop()
|
stack.Pop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMultiThreaded(t *testing.T) {
|
func TestMultiThreaded(t *testing.T) {
|
||||||
stack := stack.NewSliceBacked(100000)
|
stack := stack.NewSliceBacked[int](100000)
|
||||||
|
|
||||||
go doPushes(stack, 100000)
|
go doPushes(stack, 100000)
|
||||||
go doPushes(stack, 100000)
|
go doPushes(stack, 100000)
|
||||||
|
@ -83,7 +83,7 @@ func TestMultiThreaded(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGet(t *testing.T) {
|
func TestGet(t *testing.T) {
|
||||||
stack := stack.NewSliceBacked(10)
|
stack := stack.NewSliceBacked[int](10)
|
||||||
|
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
stack.Push(i)
|
stack.Push(i)
|
||||||
|
@ -99,6 +99,10 @@ func TestGet(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if got := stack.Get(5); got != 0 {
|
||||||
|
t.Errorf("got %v, want %v", got, 0)
|
||||||
|
}
|
||||||
|
|
||||||
slice := stack.GetSlice()
|
slice := stack.GetSlice()
|
||||||
|
|
||||||
if len(slice) != 10 {
|
if len(slice) != 10 {
|
||||||
|
@ -107,7 +111,7 @@ func TestGet(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLenCap(t *testing.T) {
|
func TestLenCap(t *testing.T) {
|
||||||
stack := stack.NewSliceBacked(10)
|
stack := stack.NewSliceBacked[int](10)
|
||||||
|
|
||||||
if got := stack.Len(); got != 0 {
|
if got := stack.Len(); got != 0 {
|
||||||
t.Errorf("got %v, want %v", got, 0)
|
t.Errorf("got %v, want %v", got, 0)
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
package internal
|
package internal
|
||||||
|
|
||||||
import "sort"
|
import (
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
|
)
|
||||||
|
|
||||||
// BisectRight returns the index of the first element in the list that is greater than or equal to the value.
|
// BisectRight returns the index of the first element in the list that is greater than or equal to the value.
|
||||||
// https://stackoverflow.com/questions/29959506/is-there-a-go-analog-of-pythons-bisect-module
|
// https://stackoverflow.com/questions/29959506/is-there-a-go-analog-of-pythons-bisect-module
|
||||||
func BisectRight(arr []interface{}, val uint32) uint32 {
|
func BisectRight[T constraints.Ordered](arr []T, val T) uint32 {
|
||||||
i := sort.Search(len(arr), func(i int) bool { return arr[i].(uint32) >= val })
|
i := sort.Search(len(arr), func(i int) bool { return arr[i] >= val })
|
||||||
return uint32(i)
|
return uint32(i)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue