Optimize deprioritized policy preemption logic

Use the priorityequeue to sort the listed policies to avoid multiple preemption.

Signed-off-by: whitewindmills <jayfantasyhjh@gmail.com>
This commit is contained in:
whitewindmills 2024-01-16 15:22:26 +08:00
parent 34c4d04528
commit e88797b2dc
14 changed files with 1141 additions and 19 deletions

View File

@ -17,6 +17,8 @@ limitations under the License.
package detector package detector
import ( import (
pq "github.com/emirpasic/gods/queues/priorityqueue"
godsutils "github.com/emirpasic/gods/utils"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@ -33,6 +35,13 @@ import (
"github.com/karmada-io/karmada/pkg/util/names" "github.com/karmada-io/karmada/pkg/util/names"
) )
// PriorityKey is the unique propagation policy key with priority.
type PriorityKey struct {
util.QueueKey
// Priority is the priority of the propagation policy.
Priority int32
}
// preemptionEnabled checks if preemption is enabled. // preemptionEnabled checks if preemption is enabled.
func preemptionEnabled(preemption policyv1alpha1.PreemptionBehavior) bool { func preemptionEnabled(preemption policyv1alpha1.PreemptionBehavior) bool {
if preemption != policyv1alpha1.PreemptAlways { if preemption != policyv1alpha1.PreemptAlways {
@ -257,10 +266,10 @@ func (d *ResourceDetector) HandleDeprioritizedPropagationPolicy(oldPolicy policy
return return
} }
// TODO(@RainbowMango): Should sort the listed policies to ensure the // Use the priority queue to sort the listed policies to ensure the
// higher priority PropagationPolicy be process first to avoid possible // higher priority PropagationPolicy be process first to avoid possible
// multiple preemption. // multiple preemption.
var sortedPotentialKeys *pq.Queue
for i := range policies { for i := range policies {
var potentialPolicy policyv1alpha1.PropagationPolicy var potentialPolicy policyv1alpha1.PropagationPolicy
if err = helper.ConvertToTypedObject(policies[i], &potentialPolicy); err != nil { if err = helper.ConvertToTypedObject(policies[i], &potentialPolicy); err != nil {
@ -277,15 +286,24 @@ func (d *ResourceDetector) HandleDeprioritizedPropagationPolicy(oldPolicy policy
potentialPolicy.Spec.Preemption == policyv1alpha1.PreemptAlways && potentialPolicy.Spec.Preemption == policyv1alpha1.PreemptAlways &&
potentialPolicy.ExplicitPriority() > newPolicy.ExplicitPriority() && potentialPolicy.ExplicitPriority() > newPolicy.ExplicitPriority() &&
potentialPolicy.ExplicitPriority() < oldPolicy.ExplicitPriority() { potentialPolicy.ExplicitPriority() < oldPolicy.ExplicitPriority() {
var potentialKey util.QueueKey potentialKey, err := ClusterWideKeyFunc(&potentialPolicy)
potentialKey, err = ClusterWideKeyFunc(&potentialPolicy)
if err != nil { if err != nil {
return klog.Errorf("Failed to convert PropagationPolicy to queued key: %v", err)
continue
} }
klog.Infof("Enqueuing PropagationPolicy(%s/%s) in case of PropagationPolicy(%s/%s) priority changes", potentialPolicy.GetNamespace(), potentialPolicy.GetName(), newPolicy.GetNamespace(), newPolicy.GetName()) klog.Infof("Enqueuing PropagationPolicy(%s/%s) in case of PropagationPolicy(%s/%s) priority changes", potentialPolicy.GetNamespace(), potentialPolicy.GetName(), newPolicy.GetNamespace(), newPolicy.GetName())
d.policyReconcileWorker.Add(potentialKey) if sortedPotentialKeys == nil {
sortedPotentialKeys = pq.NewWith(priorityDescendingComparator)
}
sortedPotentialKeys.Enqueue(&PriorityKey{
QueueKey: potentialKey,
Priority: potentialPolicy.ExplicitPriority(),
})
} }
} }
requeuePotentialKeys(sortedPotentialKeys, d.policyReconcileWorker)
} }
// HandleDeprioritizedClusterPropagationPolicy responses to priority change of a ClusterPropagationPolicy, // HandleDeprioritizedClusterPropagationPolicy responses to priority change of a ClusterPropagationPolicy,
@ -293,19 +311,18 @@ func (d *ResourceDetector) HandleDeprioritizedPropagationPolicy(oldPolicy policy
// check if there is another ClusterPropagationPolicy could preempt the targeted resource, // check if there is another ClusterPropagationPolicy could preempt the targeted resource,
// and put the ClusterPropagationPolicy in the queue to trigger preemption. // and put the ClusterPropagationPolicy in the queue to trigger preemption.
func (d *ResourceDetector) HandleDeprioritizedClusterPropagationPolicy(oldPolicy policyv1alpha1.ClusterPropagationPolicy, newPolicy policyv1alpha1.ClusterPropagationPolicy) { func (d *ResourceDetector) HandleDeprioritizedClusterPropagationPolicy(oldPolicy policyv1alpha1.ClusterPropagationPolicy, newPolicy policyv1alpha1.ClusterPropagationPolicy) {
klog.Infof("ClusterPropagationPolicy(%s/%s) priority changed from %d to %d", klog.Infof("ClusterPropagationPolicy(%s) priority changed from %d to %d",
newPolicy.GetNamespace(), newPolicy.GetName(), *oldPolicy.Spec.Priority, *newPolicy.Spec.Priority) newPolicy.GetName(), *oldPolicy.Spec.Priority, *newPolicy.Spec.Priority)
policies, err := d.clusterPropagationPolicyLister.List(labels.Everything())
policies, err := d.clusterPropagationPolicyLister.ByNamespace(newPolicy.GetNamespace()).List(labels.Everything())
if err != nil { if err != nil {
klog.Errorf("Failed to list ClusterPropagationPolicy from namespace: %s, error: %v", newPolicy.GetNamespace(), err) klog.Errorf("Failed to list ClusterPropagationPolicy, error: %v", err)
return return
} }
// TODO(@RainbowMango): Should sort the listed policies to ensure the // Use the priority queue to sort the listed policies to ensure the
// higher priority ClusterPropagationPolicy be process first to avoid possible // higher priority ClusterPropagationPolicy be process first to avoid possible
// multiple preemption. // multiple preemption.
var sortedPotentialKeys *pq.Queue
for i := range policies { for i := range policies {
var potentialPolicy policyv1alpha1.ClusterPropagationPolicy var potentialPolicy policyv1alpha1.ClusterPropagationPolicy
if err = helper.ConvertToTypedObject(policies[i], &potentialPolicy); err != nil { if err = helper.ConvertToTypedObject(policies[i], &potentialPolicy); err != nil {
@ -322,14 +339,47 @@ func (d *ResourceDetector) HandleDeprioritizedClusterPropagationPolicy(oldPolicy
potentialPolicy.Spec.Preemption == policyv1alpha1.PreemptAlways && potentialPolicy.Spec.Preemption == policyv1alpha1.PreemptAlways &&
potentialPolicy.ExplicitPriority() > newPolicy.ExplicitPriority() && potentialPolicy.ExplicitPriority() > newPolicy.ExplicitPriority() &&
potentialPolicy.ExplicitPriority() < oldPolicy.ExplicitPriority() { potentialPolicy.ExplicitPriority() < oldPolicy.ExplicitPriority() {
var potentialKey util.QueueKey potentialKey, err := ClusterWideKeyFunc(&potentialPolicy)
potentialKey, err = ClusterWideKeyFunc(&potentialPolicy)
if err != nil { if err != nil {
return klog.Errorf("Failed to convert ClusterPropagationPolicy to queued key: %v", err)
continue
} }
klog.Infof("Enqueuing ClusterPropagationPolicy(%s/%s) in case of ClusterPropagationPolicy(%s/%s) priority changes",
potentialPolicy.GetNamespace(), potentialPolicy.GetName(), newPolicy.GetNamespace(), newPolicy.GetName()) klog.Infof("Enqueuing ClusterPropagationPolicy(%s) in case of ClusterPropagationPolicy(%s) priority changes",
d.clusterPolicyReconcileWorker.Add(potentialKey) potentialPolicy.GetName(), newPolicy.GetName())
if sortedPotentialKeys == nil {
sortedPotentialKeys = pq.NewWith(priorityDescendingComparator)
}
sortedPotentialKeys.Enqueue(&PriorityKey{
QueueKey: potentialKey,
Priority: potentialPolicy.ExplicitPriority(),
})
} }
} }
requeuePotentialKeys(sortedPotentialKeys, d.clusterPolicyReconcileWorker)
}
// requeuePotentialKeys re-queues potential policy keys.
func requeuePotentialKeys(sortedPotentialKeys *pq.Queue, worker util.AsyncWorker) {
// No suitable policy key to re-queue.
if sortedPotentialKeys == nil {
return
}
for {
key, ok := sortedPotentialKeys.Dequeue()
if !ok {
break
}
worker.Add(key.(*PriorityKey).QueueKey)
}
}
// priorityDescendingComparator provides a basic descending comparison on policy priority.
func priorityDescendingComparator(a, b interface{}) int {
aPriority := a.(*PriorityKey).Priority
bPriority := b.(*PriorityKey).Priority
return godsutils.Int32Comparator(bPriority, aPriority)
} }

View File

@ -0,0 +1,227 @@
// Copyright (c) 2015, Emir Pasic. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package arraylist implements the array list.
//
// Structure is not thread safe.
//
// Reference: https://en.wikipedia.org/wiki/List_%28abstract_data_type%29
package arraylist
import (
"fmt"
"strings"
"github.com/emirpasic/gods/lists"
"github.com/emirpasic/gods/utils"
)
// Assert List implementation
var _ lists.List = (*List)(nil)
// List holds the elements in a slice
type List struct {
elements []interface{}
size int
}
const (
growthFactor = float32(2.0) // growth by 100%
shrinkFactor = float32(0.25) // shrink when size is 25% of capacity (0 means never shrink)
)
// New instantiates a new list and adds the passed values, if any, to the list
func New(values ...interface{}) *List {
list := &List{}
if len(values) > 0 {
list.Add(values...)
}
return list
}
// Add appends a value at the end of the list
func (list *List) Add(values ...interface{}) {
list.growBy(len(values))
for _, value := range values {
list.elements[list.size] = value
list.size++
}
}
// Get returns the element at index.
// Second return parameter is true if index is within bounds of the array and array is not empty, otherwise false.
func (list *List) Get(index int) (interface{}, bool) {
if !list.withinRange(index) {
return nil, false
}
return list.elements[index], true
}
// Remove removes the element at the given index from the list.
func (list *List) Remove(index int) {
if !list.withinRange(index) {
return
}
list.elements[index] = nil // cleanup reference
copy(list.elements[index:], list.elements[index+1:list.size]) // shift to the left by one (slow operation, need ways to optimize this)
list.size--
list.shrink()
}
// Contains checks if elements (one or more) are present in the set.
// All elements have to be present in the set for the method to return true.
// Performance time complexity of n^2.
// Returns true if no arguments are passed at all, i.e. set is always super-set of empty set.
func (list *List) Contains(values ...interface{}) bool {
for _, searchValue := range values {
found := false
for index := 0; index < list.size; index++ {
if list.elements[index] == searchValue {
found = true
break
}
}
if !found {
return false
}
}
return true
}
// Values returns all elements in the list.
func (list *List) Values() []interface{} {
newElements := make([]interface{}, list.size, list.size)
copy(newElements, list.elements[:list.size])
return newElements
}
//IndexOf returns index of provided element
func (list *List) IndexOf(value interface{}) int {
if list.size == 0 {
return -1
}
for index, element := range list.elements {
if element == value {
return index
}
}
return -1
}
// Empty returns true if list does not contain any elements.
func (list *List) Empty() bool {
return list.size == 0
}
// Size returns number of elements within the list.
func (list *List) Size() int {
return list.size
}
// Clear removes all elements from the list.
func (list *List) Clear() {
list.size = 0
list.elements = []interface{}{}
}
// Sort sorts values (in-place) using.
func (list *List) Sort(comparator utils.Comparator) {
if len(list.elements) < 2 {
return
}
utils.Sort(list.elements[:list.size], comparator)
}
// Swap swaps the two values at the specified positions.
func (list *List) Swap(i, j int) {
if list.withinRange(i) && list.withinRange(j) {
list.elements[i], list.elements[j] = list.elements[j], list.elements[i]
}
}
// Insert inserts values at specified index position shifting the value at that position (if any) and any subsequent elements to the right.
// Does not do anything if position is negative or bigger than list's size
// Note: position equal to list's size is valid, i.e. append.
func (list *List) Insert(index int, values ...interface{}) {
if !list.withinRange(index) {
// Append
if index == list.size {
list.Add(values...)
}
return
}
l := len(values)
list.growBy(l)
list.size += l
copy(list.elements[index+l:], list.elements[index:list.size-l])
copy(list.elements[index:], values)
}
// Set the value at specified index
// Does not do anything if position is negative or bigger than list's size
// Note: position equal to list's size is valid, i.e. append.
func (list *List) Set(index int, value interface{}) {
if !list.withinRange(index) {
// Append
if index == list.size {
list.Add(value)
}
return
}
list.elements[index] = value
}
// String returns a string representation of container
func (list *List) String() string {
str := "ArrayList\n"
values := []string{}
for _, value := range list.elements[:list.size] {
values = append(values, fmt.Sprintf("%v", value))
}
str += strings.Join(values, ", ")
return str
}
// Check that the index is within bounds of the list
func (list *List) withinRange(index int) bool {
return index >= 0 && index < list.size
}
func (list *List) resize(cap int) {
newElements := make([]interface{}, cap, cap)
copy(newElements, list.elements)
list.elements = newElements
}
// Expand the array if necessary, i.e. capacity will be reached if we add n elements
func (list *List) growBy(n int) {
// When capacity is reached, grow by a factor of growthFactor and add number of elements
currentCapacity := cap(list.elements)
if list.size+n >= currentCapacity {
newCapacity := int(growthFactor * float32(currentCapacity+n))
list.resize(newCapacity)
}
}
// Shrink the array if necessary, i.e. when size is shrinkFactor percent of current capacity
func (list *List) shrink() {
if shrinkFactor == 0.0 {
return
}
// Shrink when size is at shrinkFactor * capacity
currentCapacity := cap(list.elements)
if list.size <= int(float32(currentCapacity)*shrinkFactor) {
list.resize(list.size)
}
}

View File

@ -0,0 +1,78 @@
// Copyright (c) 2015, Emir Pasic. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package arraylist
import "github.com/emirpasic/gods/containers"
// Assert Enumerable implementation
var _ containers.EnumerableWithIndex = (*List)(nil)
// Each calls the given function once for each element, passing that element's index and value.
func (list *List) Each(f func(index int, value interface{})) {
iterator := list.Iterator()
for iterator.Next() {
f(iterator.Index(), iterator.Value())
}
}
// Map invokes the given function once for each element and returns a
// container containing the values returned by the given function.
func (list *List) Map(f func(index int, value interface{}) interface{}) *List {
newList := &List{}
iterator := list.Iterator()
for iterator.Next() {
newList.Add(f(iterator.Index(), iterator.Value()))
}
return newList
}
// Select returns a new container containing all elements for which the given function returns a true value.
func (list *List) Select(f func(index int, value interface{}) bool) *List {
newList := &List{}
iterator := list.Iterator()
for iterator.Next() {
if f(iterator.Index(), iterator.Value()) {
newList.Add(iterator.Value())
}
}
return newList
}
// Any passes each element of the collection to the given function and
// returns true if the function ever returns true for any element.
func (list *List) Any(f func(index int, value interface{}) bool) bool {
iterator := list.Iterator()
for iterator.Next() {
if f(iterator.Index(), iterator.Value()) {
return true
}
}
return false
}
// All passes each element of the collection to the given function and
// returns true if the function returns true for all elements.
func (list *List) All(f func(index int, value interface{}) bool) bool {
iterator := list.Iterator()
for iterator.Next() {
if !f(iterator.Index(), iterator.Value()) {
return false
}
}
return true
}
// Find passes each element of the container to the given function and returns
// the first (index,value) for which the function is true or -1,nil otherwise
// if no element matches the criteria.
func (list *List) Find(f func(index int, value interface{}) bool) (int, interface{}) {
iterator := list.Iterator()
for iterator.Next() {
if f(iterator.Index(), iterator.Value()) {
return iterator.Index(), iterator.Value()
}
}
return -1, nil
}

View File

@ -0,0 +1,110 @@
// Copyright (c) 2015, Emir Pasic. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package arraylist
import "github.com/emirpasic/gods/containers"
// Assert Iterator implementation
var _ containers.ReverseIteratorWithIndex = (*Iterator)(nil)
// Iterator holding the iterator's state
type Iterator struct {
list *List
index int
}
// Iterator returns a stateful iterator whose values can be fetched by an index.
func (list *List) Iterator() Iterator {
return Iterator{list: list, index: -1}
}
// Next moves the iterator to the next element and returns true if there was a next element in the container.
// If Next() returns true, then next element's index and value can be retrieved by Index() and Value().
// If Next() was called for the first time, then it will point the iterator to the first element if it exists.
// Modifies the state of the iterator.
func (iterator *Iterator) Next() bool {
if iterator.index < iterator.list.size {
iterator.index++
}
return iterator.list.withinRange(iterator.index)
}
// Prev moves the iterator to the previous element and returns true if there was a previous element in the container.
// If Prev() returns true, then previous element's index and value can be retrieved by Index() and Value().
// Modifies the state of the iterator.
func (iterator *Iterator) Prev() bool {
if iterator.index >= 0 {
iterator.index--
}
return iterator.list.withinRange(iterator.index)
}
// Value returns the current element's value.
// Does not modify the state of the iterator.
func (iterator *Iterator) Value() interface{} {
return iterator.list.elements[iterator.index]
}
// Index returns the current element's index.
// Does not modify the state of the iterator.
func (iterator *Iterator) Index() int {
return iterator.index
}
// Begin resets the iterator to its initial state (one-before-first)
// Call Next() to fetch the first element if any.
func (iterator *Iterator) Begin() {
iterator.index = -1
}
// End moves the iterator past the last element (one-past-the-end).
// Call Prev() to fetch the last element if any.
func (iterator *Iterator) End() {
iterator.index = iterator.list.size
}
// First moves the iterator to the first element and returns true if there was a first element in the container.
// If First() returns true, then first element's index and value can be retrieved by Index() and Value().
// Modifies the state of the iterator.
func (iterator *Iterator) First() bool {
iterator.Begin()
return iterator.Next()
}
// Last moves the iterator to the last element and returns true if there was a last element in the container.
// If Last() returns true, then last element's index and value can be retrieved by Index() and Value().
// Modifies the state of the iterator.
func (iterator *Iterator) Last() bool {
iterator.End()
return iterator.Prev()
}
// NextTo moves the iterator to the next element from current position that satisfies the condition given by the
// passed function, and returns true if there was a next element in the container.
// If NextTo() returns true, then next element's index and value can be retrieved by Index() and Value().
// Modifies the state of the iterator.
func (iterator *Iterator) NextTo(f func(index int, value interface{}) bool) bool {
for iterator.Next() {
index, value := iterator.Index(), iterator.Value()
if f(index, value) {
return true
}
}
return false
}
// PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the
// passed function, and returns true if there was a next element in the container.
// If PrevTo() returns true, then next element's index and value can be retrieved by Index() and Value().
// Modifies the state of the iterator.
func (iterator *Iterator) PrevTo(f func(index int, value interface{}) bool) bool {
for iterator.Prev() {
index, value := iterator.Index(), iterator.Value()
if f(index, value) {
return true
}
}
return false
}

View File

@ -0,0 +1,38 @@
// Copyright (c) 2015, Emir Pasic. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package arraylist
import (
"encoding/json"
"github.com/emirpasic/gods/containers"
)
// Assert Serialization implementation
var _ containers.JSONSerializer = (*List)(nil)
var _ containers.JSONDeserializer = (*List)(nil)
// ToJSON outputs the JSON representation of list's elements.
func (list *List) ToJSON() ([]byte, error) {
return json.Marshal(list.elements[:list.size])
}
// FromJSON populates list's elements from the input JSON representation.
func (list *List) FromJSON(data []byte) error {
err := json.Unmarshal(data, &list.elements)
if err == nil {
list.size = len(list.elements)
}
return err
}
// UnmarshalJSON @implements json.Unmarshaler
func (list *List) UnmarshalJSON(bytes []byte) error {
return list.FromJSON(bytes)
}
// MarshalJSON @implements json.Marshaler
func (list *List) MarshalJSON() ([]byte, error) {
return list.ToJSON()
}

34
vendor/github.com/emirpasic/gods/lists/lists.go generated vendored Normal file
View File

@ -0,0 +1,34 @@
// Copyright (c) 2015, Emir Pasic. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package lists provides an abstract List interface.
//
// In computer science, a list or sequence is an abstract data type that represents an ordered sequence of values, where the same value may occur more than once. An instance of a list is a computer representation of the mathematical concept of a finite sequence; the (potentially) infinite analog of a list is a stream. Lists are a basic example of containers, as they contain other values. If the same value occurs multiple times, each occurrence is considered a distinct item.
//
// Reference: https://en.wikipedia.org/wiki/List_%28abstract_data_type%29
package lists
import (
"github.com/emirpasic/gods/containers"
"github.com/emirpasic/gods/utils"
)
// List interface that all lists implement
type List interface {
Get(index int) (interface{}, bool)
Remove(index int)
Add(values ...interface{})
Contains(values ...interface{}) bool
Sort(comparator utils.Comparator)
Swap(index1, index2 int)
Insert(index int, values ...interface{})
Set(index int, value interface{})
containers.Container
// Empty() bool
// Size() int
// Clear()
// Values() []interface{}
// String() string
}

View File

@ -0,0 +1,92 @@
// Copyright (c) 2015, Emir Pasic. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package priorityqueue
import (
"github.com/emirpasic/gods/containers"
"github.com/emirpasic/gods/trees/binaryheap"
)
// Assert Iterator implementation
var _ containers.ReverseIteratorWithIndex = (*Iterator)(nil)
// Iterator returns a stateful iterator whose values can be fetched by an index.
type Iterator struct {
iterator binaryheap.Iterator
}
// Iterator returns a stateful iterator whose values can be fetched by an index.
func (queue *Queue) Iterator() Iterator {
return Iterator{iterator: queue.heap.Iterator()}
}
// Next moves the iterator to the next element and returns true if there was a next element in the container.
// If Next() returns true, then next element's index and value can be retrieved by Index() and Value().
// If Next() was called for the first time, then it will point the iterator to the first element if it exists.
// Modifies the state of the iterator.
func (iterator *Iterator) Next() bool {
return iterator.iterator.Next()
}
// Prev moves the iterator to the previous element and returns true if there was a previous element in the container.
// If Prev() returns true, then previous element's index and value can be retrieved by Index() and Value().
// Modifies the state of the iterator.
func (iterator *Iterator) Prev() bool {
return iterator.iterator.Prev()
}
// Value returns the current element's value.
// Does not modify the state of the iterator.
func (iterator *Iterator) Value() interface{} {
return iterator.iterator.Value()
}
// Index returns the current element's index.
// Does not modify the state of the iterator.
func (iterator *Iterator) Index() int {
return iterator.iterator.Index()
}
// Begin resets the iterator to its initial state (one-before-first)
// Call Next() to fetch the first element if any.
func (iterator *Iterator) Begin() {
iterator.iterator.Begin()
}
// End moves the iterator past the last element (one-past-the-end).
// Call Prev() to fetch the last element if any.
func (iterator *Iterator) End() {
iterator.iterator.End()
}
// First moves the iterator to the first element and returns true if there was a first element in the container.
// If First() returns true, then first element's index and value can be retrieved by Index() and Value().
// Modifies the state of the iterator.
func (iterator *Iterator) First() bool {
return iterator.iterator.First()
}
// Last moves the iterator to the last element and returns true if there was a last element in the container.
// If Last() returns true, then last element's index and value can be retrieved by Index() and Value().
// Modifies the state of the iterator.
func (iterator *Iterator) Last() bool {
return iterator.iterator.Last()
}
// NextTo moves the iterator to the next element from current position that satisfies the condition given by the
// passed function, and returns true if there was a next element in the container.
// If NextTo() returns true, then next element's index and value can be retrieved by Index() and Value().
// Modifies the state of the iterator.
func (iterator *Iterator) NextTo(f func(index int, value interface{}) bool) bool {
return iterator.iterator.NextTo(f)
}
// PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the
// passed function, and returns true if there was a next element in the container.
// If PrevTo() returns true, then next element's index and value can be retrieved by Index() and Value().
// Modifies the state of the iterator.
func (iterator *Iterator) PrevTo(f func(index int, value interface{}) bool) bool {
return iterator.iterator.PrevTo(f)
}

View File

@ -0,0 +1,86 @@
// Copyright (c) 2015, Emir Pasic. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package priorityqueue implements a priority queue backed by binary queue.
//
// An unbounded priority queue based on a priority queue.
// The elements of the priority queue are ordered by a comparator provided at queue construction time.
//
// The heap of this queue is the least/smallest element with respect to the specified ordering.
// If multiple elements are tied for least value, the heap is one of those elements arbitrarily.
//
// Structure is not thread safe.
//
// References: https://en.wikipedia.org/wiki/Priority_queue
package priorityqueue
import (
"fmt"
"github.com/emirpasic/gods/queues"
"github.com/emirpasic/gods/trees/binaryheap"
"github.com/emirpasic/gods/utils"
"strings"
)
// Assert Queue implementation
var _ queues.Queue = (*Queue)(nil)
// Queue holds elements in an array-list
type Queue struct {
heap *binaryheap.Heap
Comparator utils.Comparator
}
// NewWith instantiates a new empty queue with the custom comparator.
func NewWith(comparator utils.Comparator) *Queue {
return &Queue{heap: binaryheap.NewWith(comparator), Comparator: comparator}
}
// Enqueue adds a value to the end of the queue
func (queue *Queue) Enqueue(value interface{}) {
queue.heap.Push(value)
}
// Dequeue removes first element of the queue and returns it, or nil if queue is empty.
// Second return parameter is true, unless the queue was empty and there was nothing to dequeue.
func (queue *Queue) Dequeue() (value interface{}, ok bool) {
return queue.heap.Pop()
}
// Peek returns top element on the queue without removing it, or nil if queue is empty.
// Second return parameter is true, unless the queue was empty and there was nothing to peek.
func (queue *Queue) Peek() (value interface{}, ok bool) {
return queue.heap.Peek()
}
// Empty returns true if queue does not contain any elements.
func (queue *Queue) Empty() bool {
return queue.heap.Empty()
}
// Size returns number of elements within the queue.
func (queue *Queue) Size() int {
return queue.heap.Size()
}
// Clear removes all elements from the queue.
func (queue *Queue) Clear() {
queue.heap.Clear()
}
// Values returns all elements in the queue.
func (queue *Queue) Values() []interface{} {
return queue.heap.Values()
}
// String returns a string representation of container
func (queue *Queue) String() string {
str := "PriorityQueue\n"
values := make([]string, queue.heap.Size(), queue.heap.Size())
for index, value := range queue.heap.Values() {
values[index] = fmt.Sprintf("%v", value)
}
str += strings.Join(values, ", ")
return str
}

View File

@ -0,0 +1,33 @@
// Copyright (c) 2015, Emir Pasic. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package priorityqueue
import (
"github.com/emirpasic/gods/containers"
)
// Assert Serialization implementation
var _ containers.JSONSerializer = (*Queue)(nil)
var _ containers.JSONDeserializer = (*Queue)(nil)
// ToJSON outputs the JSON representation of the queue.
func (queue *Queue) ToJSON() ([]byte, error) {
return queue.heap.ToJSON()
}
// FromJSON populates the queue from the input JSON representation.
func (queue *Queue) FromJSON(data []byte) error {
return queue.heap.FromJSON(data)
}
// UnmarshalJSON @implements json.Unmarshaler
func (queue *Queue) UnmarshalJSON(bytes []byte) error {
return queue.FromJSON(bytes)
}
// MarshalJSON @implements json.Marshaler
func (queue *Queue) MarshalJSON() ([]byte, error) {
return queue.ToJSON()
}

27
vendor/github.com/emirpasic/gods/queues/queues.go generated vendored Normal file
View File

@ -0,0 +1,27 @@
// Copyright (c) 2021, Aryan Ahadinia. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package queues provides an abstract Queue interface.
//
// In computer science, a queue is a collection of entities that are maintained in a sequence and can be modified by the addition of entities at one end of the sequence and the removal of entities from the other end of the sequence. By convention, the end of the sequence at which elements are added is called the back, tail, or rear of the queue, and the end at which elements are removed is called the head or front of the queue, analogously to the words used when people line up to wait for goods or services.
// The operation of adding an element to the rear of the queue is known as enqueue, and the operation of removing an element from the front is known as dequeue. Other operations may also be allowed, often including a peek or front operation that returns the value of the next element to be dequeued without remove it.
//
// Reference: https://en.wikipedia.org/wiki/Queue_(abstract_data_type)
package queues
import "github.com/emirpasic/gods/containers"
// Queue interface that all queues implement
type Queue interface {
Enqueue(value interface{})
Dequeue() (value interface{}, ok bool)
Peek() (value interface{}, ok bool)
containers.Container
// Empty() bool
// Size() int
// Clear()
// Values() []interface{}
// String() string
}

View File

@ -0,0 +1,166 @@
// Copyright (c) 2015, Emir Pasic. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package binaryheap implements a binary heap backed by array list.
//
// Comparator defines this heap as either min or max heap.
//
// Structure is not thread safe.
//
// References: http://en.wikipedia.org/wiki/Binary_heap
package binaryheap
import (
"fmt"
"github.com/emirpasic/gods/lists/arraylist"
"github.com/emirpasic/gods/trees"
"github.com/emirpasic/gods/utils"
"strings"
)
// Assert Tree implementation
var _ trees.Tree = (*Heap)(nil)
// Heap holds elements in an array-list
type Heap struct {
list *arraylist.List
Comparator utils.Comparator
}
// NewWith instantiates a new empty heap tree with the custom comparator.
func NewWith(comparator utils.Comparator) *Heap {
return &Heap{list: arraylist.New(), Comparator: comparator}
}
// NewWithIntComparator instantiates a new empty heap with the IntComparator, i.e. elements are of type int.
func NewWithIntComparator() *Heap {
return &Heap{list: arraylist.New(), Comparator: utils.IntComparator}
}
// NewWithStringComparator instantiates a new empty heap with the StringComparator, i.e. elements are of type string.
func NewWithStringComparator() *Heap {
return &Heap{list: arraylist.New(), Comparator: utils.StringComparator}
}
// Push adds a value onto the heap and bubbles it up accordingly.
func (heap *Heap) Push(values ...interface{}) {
if len(values) == 1 {
heap.list.Add(values[0])
heap.bubbleUp()
} else {
// Reference: https://en.wikipedia.org/wiki/Binary_heap#Building_a_heap
for _, value := range values {
heap.list.Add(value)
}
size := heap.list.Size()/2 + 1
for i := size; i >= 0; i-- {
heap.bubbleDownIndex(i)
}
}
}
// Pop removes top element on heap and returns it, or nil if heap is empty.
// Second return parameter is true, unless the heap was empty and there was nothing to pop.
func (heap *Heap) Pop() (value interface{}, ok bool) {
value, ok = heap.list.Get(0)
if !ok {
return
}
lastIndex := heap.list.Size() - 1
heap.list.Swap(0, lastIndex)
heap.list.Remove(lastIndex)
heap.bubbleDown()
return
}
// Peek returns top element on the heap without removing it, or nil if heap is empty.
// Second return parameter is true, unless the heap was empty and there was nothing to peek.
func (heap *Heap) Peek() (value interface{}, ok bool) {
return heap.list.Get(0)
}
// Empty returns true if heap does not contain any elements.
func (heap *Heap) Empty() bool {
return heap.list.Empty()
}
// Size returns number of elements within the heap.
func (heap *Heap) Size() int {
return heap.list.Size()
}
// Clear removes all elements from the heap.
func (heap *Heap) Clear() {
heap.list.Clear()
}
// Values returns all elements in the heap.
func (heap *Heap) Values() []interface{} {
values := make([]interface{}, heap.list.Size(), heap.list.Size())
for it := heap.Iterator(); it.Next(); {
values[it.Index()] = it.Value()
}
return values
}
// String returns a string representation of container
func (heap *Heap) String() string {
str := "BinaryHeap\n"
values := []string{}
for it := heap.Iterator(); it.Next(); {
values = append(values, fmt.Sprintf("%v", it.Value()))
}
str += strings.Join(values, ", ")
return str
}
// Performs the "bubble down" operation. This is to place the element that is at the root
// of the heap in its correct place so that the heap maintains the min/max-heap order property.
func (heap *Heap) bubbleDown() {
heap.bubbleDownIndex(0)
}
// Performs the "bubble down" operation. This is to place the element that is at the index
// of the heap in its correct place so that the heap maintains the min/max-heap order property.
func (heap *Heap) bubbleDownIndex(index int) {
size := heap.list.Size()
for leftIndex := index<<1 + 1; leftIndex < size; leftIndex = index<<1 + 1 {
rightIndex := index<<1 + 2
smallerIndex := leftIndex
leftValue, _ := heap.list.Get(leftIndex)
rightValue, _ := heap.list.Get(rightIndex)
if rightIndex < size && heap.Comparator(leftValue, rightValue) > 0 {
smallerIndex = rightIndex
}
indexValue, _ := heap.list.Get(index)
smallerValue, _ := heap.list.Get(smallerIndex)
if heap.Comparator(indexValue, smallerValue) > 0 {
heap.list.Swap(index, smallerIndex)
} else {
break
}
index = smallerIndex
}
}
// Performs the "bubble up" operation. This is to place a newly inserted
// element (i.e. last element in the list) in its correct place so that
// the heap maintains the min/max-heap order property.
func (heap *Heap) bubbleUp() {
index := heap.list.Size() - 1
for parentIndex := (index - 1) >> 1; index > 0; parentIndex = (index - 1) >> 1 {
indexValue, _ := heap.list.Get(index)
parentValue, _ := heap.list.Get(parentIndex)
if heap.Comparator(parentValue, indexValue) <= 0 {
break
}
heap.list.Swap(index, parentIndex)
index = parentIndex
}
}
// Check that the index is within bounds of the list
func (heap *Heap) withinRange(index int) bool {
return index >= 0 && index < heap.list.Size()
}

View File

@ -0,0 +1,143 @@
// Copyright (c) 2015, Emir Pasic. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package binaryheap
import (
"github.com/emirpasic/gods/containers"
)
// Assert Iterator implementation
var _ containers.ReverseIteratorWithIndex = (*Iterator)(nil)
// Iterator returns a stateful iterator whose values can be fetched by an index.
type Iterator struct {
heap *Heap
index int
}
// Iterator returns a stateful iterator whose values can be fetched by an index.
func (heap *Heap) Iterator() Iterator {
return Iterator{heap: heap, index: -1}
}
// Next moves the iterator to the next element and returns true if there was a next element in the container.
// If Next() returns true, then next element's index and value can be retrieved by Index() and Value().
// If Next() was called for the first time, then it will point the iterator to the first element if it exists.
// Modifies the state of the iterator.
func (iterator *Iterator) Next() bool {
if iterator.index < iterator.heap.Size() {
iterator.index++
}
return iterator.heap.withinRange(iterator.index)
}
// Prev moves the iterator to the previous element and returns true if there was a previous element in the container.
// If Prev() returns true, then previous element's index and value can be retrieved by Index() and Value().
// Modifies the state of the iterator.
func (iterator *Iterator) Prev() bool {
if iterator.index >= 0 {
iterator.index--
}
return iterator.heap.withinRange(iterator.index)
}
// Value returns the current element's value.
// Does not modify the state of the iterator.
func (iterator *Iterator) Value() interface{} {
start, end := evaluateRange(iterator.index)
if end > iterator.heap.Size() {
end = iterator.heap.Size()
}
tmpHeap := NewWith(iterator.heap.Comparator)
for n := start; n < end; n++ {
value, _ := iterator.heap.list.Get(n)
tmpHeap.Push(value)
}
for n := 0; n < iterator.index-start; n++ {
tmpHeap.Pop()
}
value, _ := tmpHeap.Pop()
return value
}
// Index returns the current element's index.
// Does not modify the state of the iterator.
func (iterator *Iterator) Index() int {
return iterator.index
}
// Begin resets the iterator to its initial state (one-before-first)
// Call Next() to fetch the first element if any.
func (iterator *Iterator) Begin() {
iterator.index = -1
}
// End moves the iterator past the last element (one-past-the-end).
// Call Prev() to fetch the last element if any.
func (iterator *Iterator) End() {
iterator.index = iterator.heap.Size()
}
// First moves the iterator to the first element and returns true if there was a first element in the container.
// If First() returns true, then first element's index and value can be retrieved by Index() and Value().
// Modifies the state of the iterator.
func (iterator *Iterator) First() bool {
iterator.Begin()
return iterator.Next()
}
// Last moves the iterator to the last element and returns true if there was a last element in the container.
// If Last() returns true, then last element's index and value can be retrieved by Index() and Value().
// Modifies the state of the iterator.
func (iterator *Iterator) Last() bool {
iterator.End()
return iterator.Prev()
}
// NextTo moves the iterator to the next element from current position that satisfies the condition given by the
// passed function, and returns true if there was a next element in the container.
// If NextTo() returns true, then next element's index and value can be retrieved by Index() and Value().
// Modifies the state of the iterator.
func (iterator *Iterator) NextTo(f func(index int, value interface{}) bool) bool {
for iterator.Next() {
index, value := iterator.Index(), iterator.Value()
if f(index, value) {
return true
}
}
return false
}
// PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the
// passed function, and returns true if there was a next element in the container.
// If PrevTo() returns true, then next element's index and value can be retrieved by Index() and Value().
// Modifies the state of the iterator.
func (iterator *Iterator) PrevTo(f func(index int, value interface{}) bool) bool {
for iterator.Prev() {
index, value := iterator.Index(), iterator.Value()
if f(index, value) {
return true
}
}
return false
}
// numOfBits counts the number of bits of an int
func numOfBits(n int) uint {
var count uint
for n != 0 {
count++
n >>= 1
}
return count
}
// evaluateRange evaluates the index range [start,end) of same level nodes in the heap as the index
func evaluateRange(index int) (start int, end int) {
bits := numOfBits(index+1) - 1
start = 1<<bits - 1
end = start + 1<<bits
return
}

View File

@ -0,0 +1,33 @@
// Copyright (c) 2015, Emir Pasic. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package binaryheap
import (
"github.com/emirpasic/gods/containers"
)
// Assert Serialization implementation
var _ containers.JSONSerializer = (*Heap)(nil)
var _ containers.JSONDeserializer = (*Heap)(nil)
// ToJSON outputs the JSON representation of the heap.
func (heap *Heap) ToJSON() ([]byte, error) {
return heap.list.ToJSON()
}
// FromJSON populates the heap from the input JSON representation.
func (heap *Heap) FromJSON(data []byte) error {
return heap.list.FromJSON(data)
}
// UnmarshalJSON @implements json.Unmarshaler
func (heap *Heap) UnmarshalJSON(bytes []byte) error {
return heap.FromJSON(bytes)
}
// MarshalJSON @implements json.Marshaler
func (heap *Heap) MarshalJSON() ([]byte, error) {
return heap.ToJSON()
}

5
vendor/modules.txt vendored
View File

@ -68,7 +68,12 @@ github.com/emicklei/go-restful/v3/log
# github.com/emirpasic/gods v1.18.1 # github.com/emirpasic/gods v1.18.1
## explicit; go 1.2 ## explicit; go 1.2
github.com/emirpasic/gods/containers github.com/emirpasic/gods/containers
github.com/emirpasic/gods/lists
github.com/emirpasic/gods/lists/arraylist
github.com/emirpasic/gods/queues
github.com/emirpasic/gods/queues/priorityqueue
github.com/emirpasic/gods/trees github.com/emirpasic/gods/trees
github.com/emirpasic/gods/trees/binaryheap
github.com/emirpasic/gods/trees/redblacktree github.com/emirpasic/gods/trees/redblacktree
github.com/emirpasic/gods/utils github.com/emirpasic/gods/utils
# github.com/evanphx/json-patch v5.6.0+incompatible # github.com/evanphx/json-patch v5.6.0+incompatible