mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
common/prque: implement LazyQueue
This commit is contained in:
parent
9ee4bc94c3
commit
8f3f41eb52
1 changed files with 161 additions and 0 deletions
161
common/prque/lazyqueue.go
Normal file
161
common/prque/lazyqueue.go
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
// CookieJar - A contestant's algorithm toolbox
|
||||
// Copyright (c) 2013 Peter Szilagyi. All rights reserved.
|
||||
//
|
||||
// CookieJar is dual licensed: use of this source code is governed by a BSD
|
||||
// license that can be found in the LICENSE file. Alternatively, the CookieJar
|
||||
// toolbox may be used in accordance with the terms and conditions contained
|
||||
// in a signed written agreement between you and the author(s).
|
||||
|
||||
// This is a duplicated and slightly modified version of "gopkg.in/karalabe/cookiejar.v2/collections/LazyQueue".
|
||||
|
||||
// Package LazyQueue implements a priority queue data structure supporting arbitrary
|
||||
// value types and int64 priorities.
|
||||
//
|
||||
// If you would like to use a min-priority queue, simply negate the priorities.
|
||||
//
|
||||
// Internally the queue is based on the standard heap package working on a
|
||||
// sortable version of the block based stack.
|
||||
package prque
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/mclock"
|
||||
)
|
||||
|
||||
type (
|
||||
PriorityCallback func(data interface{}, now mclock.AbsTime) int64
|
||||
MaxPriorityCallback func(data interface{}, until mclock.AbsTime) int64
|
||||
)
|
||||
|
||||
// Priority queue data structure.
|
||||
type LazyQueue struct {
|
||||
clock mclock.Clock
|
||||
queue [2]*sstack
|
||||
popQueue *sstack
|
||||
period time.Duration
|
||||
maxUntil mclock.AbsTime
|
||||
indexOffset int
|
||||
setIndex SetIndexCallback
|
||||
priority PriorityCallback
|
||||
maxPriority MaxPriorityCallback
|
||||
}
|
||||
|
||||
// New creates a new priority queue.
|
||||
func NewLazyQueue(setIndex SetIndexCallback, priority PriorityCallback, maxPriority MaxPriorityCallback, clock mclock.Clock, period time.Duration) *LazyQueue {
|
||||
q := &LazyQueue{
|
||||
popQueue: newSstack(nil),
|
||||
setIndex: setIndex,
|
||||
priority: priority,
|
||||
maxPriority: maxPriority,
|
||||
clock: clock,
|
||||
period: period}
|
||||
q.Reset()
|
||||
q.Refresh()
|
||||
return q
|
||||
}
|
||||
|
||||
func (q *LazyQueue) Refresh() {
|
||||
q.maxUntil = q.clock.Now() + mclock.AbsTime(q.period)
|
||||
for q.queue[0].Len() != 0 {
|
||||
q.Push(heap.Pop(q.queue[0]).(*item).value)
|
||||
}
|
||||
q.queue[0], q.queue[1] = q.queue[1], q.queue[0]
|
||||
q.indexOffset = 1 - q.indexOffset
|
||||
q.maxUntil += mclock.AbsTime(q.period)
|
||||
}
|
||||
|
||||
func (q *LazyQueue) setIndex0(data interface{}, index int) {
|
||||
q.setIndex(data, index+index)
|
||||
}
|
||||
|
||||
func (q *LazyQueue) setIndex1(data interface{}, index int) {
|
||||
q.setIndex(data, index+index+1)
|
||||
}
|
||||
|
||||
// Pushes a value with a given priority into the queue, expanding if necessary.
|
||||
func (q *LazyQueue) Push(data interface{}) {
|
||||
heap.Push(q.queue[1], &item{data, q.maxPriority(data, q.maxUntil)})
|
||||
}
|
||||
|
||||
func (q *LazyQueue) Update(i int) {
|
||||
q.Push(q.Remove(i))
|
||||
}
|
||||
|
||||
// Pops the value with the greates priority off the stack and returns it.
|
||||
// Currently no shrinking is done.
|
||||
func (q *LazyQueue) Pop() (interface{}, int64) {
|
||||
var (
|
||||
resData interface{}
|
||||
resPri int64
|
||||
)
|
||||
q.MultiPop(func(data interface{}, priority int64) bool {
|
||||
resData = data
|
||||
resPri = priority
|
||||
return false
|
||||
})
|
||||
return resData, resPri
|
||||
}
|
||||
|
||||
func (q *LazyQueue) peekIndex() int {
|
||||
if q.queue[0].Len() != 0 {
|
||||
if q.queue[1].Len() != 0 && q.queue[1].blocks[0][0].priority > q.queue[0].blocks[0][0].priority {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
if q.queue[1].Len() != 0 {
|
||||
return 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func (q *LazyQueue) MultiPop(callback func(data interface{}, priority int64) bool) {
|
||||
now := q.clock.Now()
|
||||
nextIndex := q.peekIndex()
|
||||
for nextIndex != -1 {
|
||||
data := heap.Pop(q.queue[nextIndex]).(*item).value
|
||||
q.popQueue.Push(&item{data, q.priority(data, now)})
|
||||
nextIndex = q.peekIndex()
|
||||
for q.popQueue.Len() != 0 && (nextIndex == -1 || q.queue[nextIndex].blocks[0][0].priority < q.popQueue.blocks[0][0].priority) {
|
||||
i := heap.Pop(q.popQueue).(*item)
|
||||
if !callback(i.value, i.priority) {
|
||||
for q.popQueue.Len() != 0 {
|
||||
q.Push(heap.Pop(q.popQueue).(*item).value)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pops only the item from the queue, dropping the associated priority value.
|
||||
func (q *LazyQueue) PopItem() interface{} {
|
||||
i, _ := q.Pop()
|
||||
return i.(*item).value
|
||||
}
|
||||
|
||||
// Remove removes the element with the given index.
|
||||
func (q *LazyQueue) Remove(i int) interface{} {
|
||||
if i < 0 {
|
||||
return nil
|
||||
}
|
||||
return heap.Remove(q.queue[i&1^q.indexOffset], i>>1)
|
||||
}
|
||||
|
||||
// Checks whether the priority queue is empty.
|
||||
func (q *LazyQueue) Empty() bool {
|
||||
return q.queue[0].Len() == 0 && q.queue[1].Len() == 0
|
||||
}
|
||||
|
||||
// Returns the number of element in the priority queue.
|
||||
func (q *LazyQueue) Size() int {
|
||||
return q.queue[0].Len() + q.queue[1].Len()
|
||||
}
|
||||
|
||||
// Clears the contents of the priority queue.
|
||||
func (q *LazyQueue) Reset() {
|
||||
q.queue[0] = newSstack(q.setIndex0)
|
||||
q.queue[1] = newSstack(q.setIndex1)
|
||||
}
|
||||
Loading…
Reference in a new issue