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 }