github.com/kaydxh/golang@v0.0.131/go/container/heap/heap.go (about)

     1  /*
     2   *Copyright (c) 2022, kaydxh
     3   *
     4   *Permission is hereby granted, free of charge, to any person obtaining a copy
     5   *of this software and associated documentation files (the "Software"), to deal
     6   *in the Software without restriction, including without limitation the rights
     7   *to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     8   *copies of the Software, and to permit persons to whom the Software is
     9   *furnished to do so, subject to the following conditions:
    10   *
    11   *The above copyright notice and this permission notice shall be included in all
    12   *copies or substantial portions of the Software.
    13   *
    14   *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    15   *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    16   *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    17   *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    18   *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    19   *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    20   *SOFTWARE.
    21   */
    22  package heap
    23  
    24  import (
    25  	"container/heap"
    26  	"fmt"
    27  	"sync"
    28  )
    29  
    30  const (
    31  	closedMsg = "heap is closed"
    32  )
    33  
    34  // LessFunc is used to compare two objects in the heap.
    35  type LessFunc func(interface{}, interface{}) bool
    36  
    37  // KeyFunc knows how to make a key from an object. Implementations
    38  // should be deterministic.
    39  type KeyFunc func(interface{}) string
    40  
    41  type heapItem struct {
    42  	obj   interface{} // The object which is stored in the heap.
    43  	index int         // The index of the object's key in the Heap.queue.
    44  }
    45  
    46  type itemKeyValue struct {
    47  	key string
    48  	obj interface{}
    49  }
    50  
    51  // heapData is an internal struct that implements the standard heap interface
    52  // and keeps the data stored in the heap.
    53  type heapData struct {
    54  	// items is a map from key of the objects to the objects and their index.
    55  	// We depend on the property that items in the map are in the queue and vice versa.
    56  	items map[string]*heapItem
    57  	// queue implements a heap data structure and keeps the order of elements
    58  	// according to the heap invariant. The queue keeps the keys of objects stored
    59  	// in "items".
    60  	queue []string
    61  
    62  	// keyFunc is used to make the key used for queued item insertion and retrieval, and
    63  	// should be deterministic.
    64  	keyFunc KeyFunc
    65  	// lessFunc is used to compare two objects in the heap.
    66  	lessFunc LessFunc
    67  }
    68  
    69  var (
    70  	_ = heap.Interface(&heapData{}) // heapData is a standard heap
    71  )
    72  
    73  // Less compares two objects and returns true if the first one should go
    74  // in front of the second one in the heap.
    75  func (h *heapData) Less(i, j int) bool {
    76  	if i > len(h.queue) || j > len(h.queue) {
    77  		return false
    78  	}
    79  	itemi, ok := h.items[h.queue[i]]
    80  	if !ok {
    81  		return false
    82  	}
    83  	itemj, ok := h.items[h.queue[j]]
    84  	if !ok {
    85  		return false
    86  	}
    87  	return h.lessFunc(itemi.obj, itemj.obj)
    88  }
    89  
    90  // Len returns the number of items in the Heap.
    91  func (h *heapData) Len() int { return len(h.queue) }
    92  
    93  // Swap implements swapping of two elements in the heap. This is a part of standard
    94  // heap interface and should never be called directly.
    95  func (h *heapData) Swap(i, j int) {
    96  	h.queue[i], h.queue[j] = h.queue[j], h.queue[i]
    97  	item := h.items[h.queue[i]]
    98  	item.index = i
    99  	item = h.items[h.queue[j]]
   100  	item.index = j
   101  }
   102  
   103  // Push is supposed to be called by heap.Push only.
   104  func (h *heapData) Push(kv interface{}) {
   105  	keyValue := kv.(*itemKeyValue)
   106  	n := len(h.queue)
   107  	h.items[keyValue.key] = &heapItem{keyValue.obj, n}
   108  	h.queue = append(h.queue, keyValue.key)
   109  }
   110  
   111  // Pop is supposed to be called by heap.Pop only.
   112  func (h *heapData) Pop() interface{} {
   113  	key := h.queue[len(h.queue)-1]
   114  	h.queue = h.queue[0 : len(h.queue)-1]
   115  	item, ok := h.items[key]
   116  	if !ok {
   117  		// This is an error
   118  		return nil
   119  	}
   120  	delete(h.items, key)
   121  	return item.obj
   122  }
   123  
   124  // Heap is a thread-safe producer/consumer queue that implements a heap data structure.
   125  // It can be used to implement priority queues and similar data structures.
   126  type Heap struct {
   127  	lock sync.RWMutex
   128  	cond sync.Cond
   129  
   130  	// data stores objects and has a queue that keeps their ordering according
   131  	// to the heap invariant.
   132  	data *heapData
   133  
   134  	// closed indicates that the queue is closed.
   135  	// It is mainly used to let Pop() exit its control loop while waiting for an item.
   136  	closed bool
   137  }
   138  
   139  // Close the Heap and signals condition variables that may be waiting to pop
   140  // items from the heap.
   141  func (h *Heap) Close() {
   142  	h.lock.Lock()
   143  	defer h.lock.Unlock()
   144  	h.closed = true
   145  	h.cond.Broadcast()
   146  }
   147  
   148  // Add inserts an item, and puts it in the queue. The item is updated if it
   149  // already exists.
   150  func (h *Heap) Add(obj interface{}) error {
   151  	key := h.data.keyFunc(obj)
   152  	h.lock.Lock()
   153  	defer h.lock.Unlock()
   154  	if h.closed {
   155  		return fmt.Errorf(closedMsg)
   156  	}
   157  	if _, exists := h.data.items[key]; exists {
   158  		h.data.items[key].obj = obj
   159  		heap.Fix(h.data, h.data.items[key].index)
   160  	} else {
   161  		h.addIfNotPresentLocked(key, obj)
   162  	}
   163  	h.cond.Broadcast()
   164  	return nil
   165  }
   166  
   167  // BulkAdd adds all the items in the list to the queue and then signals the condition
   168  // variable. It is useful when the caller would like to add all of the items
   169  // to the queue before consumer starts processing them.
   170  func (h *Heap) BulkAdd(list []interface{}) error {
   171  	h.lock.Lock()
   172  	defer h.lock.Unlock()
   173  	if h.closed {
   174  		return fmt.Errorf(closedMsg)
   175  	}
   176  	for _, obj := range list {
   177  		key := h.data.keyFunc(obj)
   178  		if _, exists := h.data.items[key]; exists {
   179  			h.data.items[key].obj = obj
   180  			heap.Fix(h.data, h.data.items[key].index)
   181  		} else {
   182  			h.addIfNotPresentLocked(key, obj)
   183  		}
   184  	}
   185  	h.cond.Broadcast()
   186  	return nil
   187  }
   188  
   189  // AddIfNotPresent inserts an item, and puts it in the queue. If an item with
   190  // the key is present in the map, no changes is made to the item.
   191  //
   192  // This is useful in a single producer/consumer scenario so that the consumer can
   193  // safely retry items without contending with the producer and potentially enqueueing
   194  // stale items.
   195  func (h *Heap) AddIfNotPresent(obj interface{}) error {
   196  	id := h.data.keyFunc(obj)
   197  	h.lock.Lock()
   198  	defer h.lock.Unlock()
   199  	if h.closed {
   200  		return fmt.Errorf(closedMsg)
   201  	}
   202  	h.addIfNotPresentLocked(id, obj)
   203  	h.cond.Broadcast()
   204  	return nil
   205  }
   206  
   207  // AddIfHeapOrder inserts an item, and puts it in the queue. If an item with
   208  // the key is present in the map, and new obj meet the sort of heap,
   209  // then update the item, or  no changes is made to the item.
   210  func (h *Heap) AddIfHeapOrder(obj interface{}) error {
   211  	key := h.data.keyFunc(obj)
   212  	h.lock.Lock()
   213  	defer h.lock.Unlock()
   214  	if h.closed {
   215  		return fmt.Errorf(closedMsg)
   216  	}
   217  	if existedObj, exists := h.data.items[key]; exists {
   218  		if !h.data.lessFunc(existedObj.obj, obj) {
   219  			h.data.items[key].obj = obj
   220  			heap.Fix(h.data, h.data.items[key].index)
   221  		}
   222  	} else {
   223  		h.addIfNotPresentLocked(key, obj)
   224  	}
   225  	h.cond.Broadcast()
   226  	return nil
   227  }
   228  
   229  // addIfNotPresentLocked assumes the lock is already held and adds the provided
   230  // item to the queue if it does not already exist.
   231  func (h *Heap) addIfNotPresentLocked(key string, obj interface{}) {
   232  	if _, exists := h.data.items[key]; exists {
   233  		return
   234  	}
   235  	heap.Push(h.data, &itemKeyValue{key, obj})
   236  }
   237  
   238  // Update is the same as Add in this implementation. When the item does not
   239  // exist, it is added.
   240  func (h *Heap) Update(obj interface{}) error {
   241  	return h.Add(obj)
   242  }
   243  
   244  // Delete removes an item.
   245  func (h *Heap) Delete(obj interface{}) error {
   246  	key := h.data.keyFunc(obj)
   247  	h.lock.Lock()
   248  	defer h.lock.Unlock()
   249  	if item, ok := h.data.items[key]; ok {
   250  		heap.Remove(h.data, item.index)
   251  		return nil
   252  	}
   253  	return fmt.Errorf("object not found")
   254  }
   255  
   256  // Pop waits until an item is ready. If multiple items are
   257  // ready, they are returned in the order given by Heap.data.lessFunc.
   258  func (h *Heap) Pop() (interface{}, error) {
   259  	h.lock.Lock()
   260  	defer h.lock.Unlock()
   261  	for len(h.data.queue) == 0 {
   262  		// When the queue is empty, invocation of Pop() is blocked until new item is enqueued.
   263  		// When Close() is called, the h.closed is set and the condition is broadcast,
   264  		// which causes this loop to continue and return from the Pop().
   265  		if h.closed {
   266  			return nil, fmt.Errorf("heap is closed")
   267  		}
   268  		h.cond.Wait()
   269  	}
   270  	obj := heap.Pop(h.data)
   271  	if obj == nil {
   272  		return nil, fmt.Errorf("object was removed from heap data")
   273  	}
   274  
   275  	return obj, nil
   276  }
   277  
   278  // List returns a list of all the items.
   279  func (h *Heap) List() []interface{} {
   280  	h.lock.RLock()
   281  	defer h.lock.RUnlock()
   282  	list := make([]interface{}, 0, len(h.data.items))
   283  	for _, item := range h.data.items {
   284  		list = append(list, item.obj)
   285  	}
   286  	return list
   287  }
   288  
   289  // ListKeys returns a list of all the keys of the objects currently in the Heap.
   290  // Note: the key order is random, because it's data structure is map
   291  func (h *Heap) ListKeys() []string {
   292  	h.lock.RLock()
   293  	defer h.lock.RUnlock()
   294  	list := make([]string, 0, len(h.data.items))
   295  	for key := range h.data.items {
   296  		list = append(list, key)
   297  	}
   298  	return list
   299  }
   300  
   301  // Get returns the requested item, or sets exists=false.
   302  // return item.obj, exists
   303  func (h *Heap) Get(obj interface{}) (interface{}, bool) {
   304  	key := h.data.keyFunc(obj)
   305  	return h.GetByKey(key)
   306  }
   307  
   308  // GetByKey returns the requested item, or sets exists=false.
   309  func (h *Heap) GetByKey(key string) (interface{}, bool) {
   310  	h.lock.RLock()
   311  	defer h.lock.RUnlock()
   312  	item, exists := h.data.items[key]
   313  	if !exists {
   314  		return nil, false
   315  	}
   316  	return item.obj, true
   317  }
   318  
   319  // IsClosed returns true if the queue is closed.
   320  func (h *Heap) IsClosed() bool {
   321  	h.lock.RLock()
   322  	defer h.lock.RUnlock()
   323  	if h.closed {
   324  		return true
   325  	}
   326  	return false
   327  }
   328  
   329  // NewHeap returns a Heap which can be used to queue up items to process.
   330  func NewHeap(keyFn KeyFunc, lessFn LessFunc) *Heap {
   331  	h := &Heap{
   332  		data: &heapData{
   333  			items:    map[string]*heapItem{},
   334  			queue:    []string{},
   335  			keyFunc:  keyFn,
   336  			lessFunc: lessFn,
   337  		},
   338  	}
   339  	h.cond.L = &h.lock
   340  	return h
   341  }