mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2025-06-05 21:59:39 +02:00
Database updates (#144)
* start moving some database stuff around * continue moving db stuff around * more fiddling * more updates * and some more * and yet more * i broke SOMETHING but what, it's a mystery * tidy up * vendor ttlcache * use ttlcache * fix up some tests * rename some stuff * little reminder * some more updates
This commit is contained in:
18
vendor/github.com/ReneKroon/ttlcache/.travis.yml
generated
vendored
Normal file
18
vendor/github.com/ReneKroon/ttlcache/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- "1.14"
|
||||
- "1.13"
|
||||
git:
|
||||
depth: 1
|
||||
|
||||
install:
|
||||
- go install -race std
|
||||
- go install golang.org/x/tools/cmd/cover
|
||||
- go install golang.org/x/lint/golint
|
||||
- export PATH=$HOME/gopath/bin:$PATH
|
||||
|
||||
script:
|
||||
- golint .
|
||||
- go test -cover -race -count=1 -timeout=30s -run .
|
||||
- cd bench; go test -run=Bench.* -bench=. -benchmem
|
21
vendor/github.com/ReneKroon/ttlcache/LICENSE
generated
vendored
Normal file
21
vendor/github.com/ReneKroon/ttlcache/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Rene Kroon
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
71
vendor/github.com/ReneKroon/ttlcache/Readme.md
generated
vendored
Normal file
71
vendor/github.com/ReneKroon/ttlcache/Readme.md
generated
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
## TTLCache - an in-memory cache with expiration
|
||||
|
||||
TTLCache is a simple key/value cache in golang with the following functions:
|
||||
|
||||
1. Thread-safe
|
||||
2. Individual expiring time or global expiring time, you can choose
|
||||
3. Auto-Extending expiration on `Get` -or- DNS style TTL, see `SkipTtlExtensionOnHit(bool)`
|
||||
4. Fast and memory efficient
|
||||
5. Can trigger callback on key expiration
|
||||
6. Cleanup resources by calling `Close()` at end of lifecycle.
|
||||
|
||||
Note (issue #25): by default, due to historic reasons, the TTL will be reset on each cache hit and you need to explicitly configure the cache to use a TTL that will not get extended.
|
||||
|
||||
[](https://travis-ci.org/ReneKroon/ttlcache)
|
||||
|
||||
#### Usage
|
||||
```go
|
||||
import (
|
||||
"time"
|
||||
"fmt"
|
||||
|
||||
"github.com/ReneKroon/ttlcache"
|
||||
)
|
||||
|
||||
func main () {
|
||||
newItemCallback := func(key string, value interface{}) {
|
||||
fmt.Printf("New key(%s) added\n", key)
|
||||
}
|
||||
checkExpirationCallback := func(key string, value interface{}) bool {
|
||||
if key == "key1" {
|
||||
// if the key equals "key1", the value
|
||||
// will not be allowed to expire
|
||||
return false
|
||||
}
|
||||
// all other values are allowed to expire
|
||||
return true
|
||||
}
|
||||
expirationCallback := func(key string, value interface{}) {
|
||||
fmt.Printf("This key(%s) has expired\n", key)
|
||||
}
|
||||
|
||||
cache := ttlcache.NewCache()
|
||||
defer cache.Close()
|
||||
cache.SetTTL(time.Duration(10 * time.Second))
|
||||
cache.SetExpirationCallback(expirationCallback)
|
||||
|
||||
cache.Set("key", "value")
|
||||
cache.SetWithTTL("keyWithTTL", "value", 10 * time.Second)
|
||||
|
||||
value, exists := cache.Get("key")
|
||||
count := cache.Count()
|
||||
result := cache.Remove("key")
|
||||
}
|
||||
```
|
||||
|
||||
#### TTLCache - Some design considerations
|
||||
|
||||
1. The complexity of the current cache is already quite high. Therefore i will not add 'convenience' features like an interface to supply a function to get missing keys.
|
||||
2. The locking should be done only in the functions of the Cache struct. Else data races can occur or recursive locks are needed, which are both unwanted.
|
||||
3. I prefer correct functionality over fast tests. It's ok for new tests to take seconds to proof something.
|
||||
|
||||
#### Original Project
|
||||
|
||||
TTLCache was forked from [wunderlist/ttlcache](https://github.com/wunderlist/ttlcache) to add extra functions not avaiable in the original scope.
|
||||
The main differences are:
|
||||
|
||||
1. A item can store any kind of object, previously, only strings could be saved
|
||||
2. Optionally, you can add callbacks too: check if a value should expire, be notified if a value expires, and be notified when new values are added to the cache
|
||||
3. The expiration can be either global or per item
|
||||
4. Can exist items without expiration time
|
||||
5. Expirations and callbacks are realtime. Don't have a pooling time to check anymore, now it's done with a heap.
|
307
vendor/github.com/ReneKroon/ttlcache/cache.go
generated
vendored
Normal file
307
vendor/github.com/ReneKroon/ttlcache/cache.go
generated
vendored
Normal file
@ -0,0 +1,307 @@
|
||||
package ttlcache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CheckExpireCallback is used as a callback for an external check on item expiration
|
||||
type checkExpireCallback func(key string, value interface{}) bool
|
||||
|
||||
// ExpireCallback is used as a callback on item expiration or when notifying of an item new to the cache
|
||||
type expireCallback func(key string, value interface{})
|
||||
|
||||
// Cache is a synchronized map of items that can auto-expire once stale
|
||||
type Cache struct {
|
||||
mutex sync.Mutex
|
||||
ttl time.Duration
|
||||
items map[string]*item
|
||||
expireCallback expireCallback
|
||||
checkExpireCallback checkExpireCallback
|
||||
newItemCallback expireCallback
|
||||
priorityQueue *priorityQueue
|
||||
expirationNotification chan bool
|
||||
expirationTime time.Time
|
||||
skipTTLExtension bool
|
||||
shutdownSignal chan (chan struct{})
|
||||
isShutDown bool
|
||||
}
|
||||
|
||||
func (cache *Cache) getItem(key string) (*item, bool, bool) {
|
||||
item, exists := cache.items[key]
|
||||
if !exists || item.expired() {
|
||||
return nil, false, false
|
||||
}
|
||||
|
||||
if item.ttl >= 0 && (item.ttl > 0 || cache.ttl > 0) {
|
||||
if cache.ttl > 0 && item.ttl == 0 {
|
||||
item.ttl = cache.ttl
|
||||
}
|
||||
|
||||
if !cache.skipTTLExtension {
|
||||
item.touch()
|
||||
}
|
||||
cache.priorityQueue.update(item)
|
||||
}
|
||||
|
||||
expirationNotification := false
|
||||
if cache.expirationTime.After(time.Now().Add(item.ttl)) {
|
||||
expirationNotification = true
|
||||
}
|
||||
return item, exists, expirationNotification
|
||||
}
|
||||
|
||||
func (cache *Cache) startExpirationProcessing() {
|
||||
timer := time.NewTimer(time.Hour)
|
||||
for {
|
||||
var sleepTime time.Duration
|
||||
cache.mutex.Lock()
|
||||
if cache.priorityQueue.Len() > 0 {
|
||||
sleepTime = time.Until(cache.priorityQueue.items[0].expireAt)
|
||||
if sleepTime < 0 && cache.priorityQueue.items[0].expireAt.IsZero() {
|
||||
sleepTime = time.Hour
|
||||
} else if sleepTime < 0 {
|
||||
sleepTime = time.Microsecond
|
||||
}
|
||||
if cache.ttl > 0 {
|
||||
sleepTime = min(sleepTime, cache.ttl)
|
||||
}
|
||||
|
||||
} else if cache.ttl > 0 {
|
||||
sleepTime = cache.ttl
|
||||
} else {
|
||||
sleepTime = time.Hour
|
||||
}
|
||||
|
||||
cache.expirationTime = time.Now().Add(sleepTime)
|
||||
cache.mutex.Unlock()
|
||||
|
||||
timer.Reset(sleepTime)
|
||||
select {
|
||||
case shutdownFeedback := <-cache.shutdownSignal:
|
||||
timer.Stop()
|
||||
cache.mutex.Lock()
|
||||
if cache.priorityQueue.Len() > 0 {
|
||||
cache.evictjob()
|
||||
}
|
||||
cache.mutex.Unlock()
|
||||
shutdownFeedback <- struct{}{}
|
||||
return
|
||||
case <-timer.C:
|
||||
timer.Stop()
|
||||
cache.mutex.Lock()
|
||||
if cache.priorityQueue.Len() == 0 {
|
||||
cache.mutex.Unlock()
|
||||
continue
|
||||
}
|
||||
|
||||
cache.cleanjob()
|
||||
cache.mutex.Unlock()
|
||||
|
||||
case <-cache.expirationNotification:
|
||||
timer.Stop()
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (cache *Cache) evictjob() {
|
||||
// index will only be advanced if the current entry will not be evicted
|
||||
i := 0
|
||||
for item := cache.priorityQueue.items[i]; ; item = cache.priorityQueue.items[i] {
|
||||
|
||||
cache.priorityQueue.remove(item)
|
||||
delete(cache.items, item.key)
|
||||
if cache.expireCallback != nil {
|
||||
go cache.expireCallback(item.key, item.data)
|
||||
}
|
||||
if cache.priorityQueue.Len() == 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (cache *Cache) cleanjob() {
|
||||
// index will only be advanced if the current entry will not be evicted
|
||||
i := 0
|
||||
for item := cache.priorityQueue.items[i]; item.expired(); item = cache.priorityQueue.items[i] {
|
||||
|
||||
if cache.checkExpireCallback != nil {
|
||||
if !cache.checkExpireCallback(item.key, item.data) {
|
||||
item.touch()
|
||||
cache.priorityQueue.update(item)
|
||||
i++
|
||||
if i == cache.priorityQueue.Len() {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
cache.priorityQueue.remove(item)
|
||||
delete(cache.items, item.key)
|
||||
if cache.expireCallback != nil {
|
||||
go cache.expireCallback(item.key, item.data)
|
||||
}
|
||||
if cache.priorityQueue.Len() == 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close calls Purge, and then stops the goroutine that does ttl checking, for a clean shutdown.
|
||||
// The cache is no longer cleaning up after the first call to Close, repeated calls are safe though.
|
||||
func (cache *Cache) Close() {
|
||||
|
||||
cache.mutex.Lock()
|
||||
if !cache.isShutDown {
|
||||
cache.isShutDown = true
|
||||
cache.mutex.Unlock()
|
||||
feedback := make(chan struct{})
|
||||
cache.shutdownSignal <- feedback
|
||||
<-feedback
|
||||
close(cache.shutdownSignal)
|
||||
} else {
|
||||
cache.mutex.Unlock()
|
||||
}
|
||||
cache.Purge()
|
||||
}
|
||||
|
||||
// Set is a thread-safe way to add new items to the map
|
||||
func (cache *Cache) Set(key string, data interface{}) {
|
||||
cache.SetWithTTL(key, data, ItemExpireWithGlobalTTL)
|
||||
}
|
||||
|
||||
// SetWithTTL is a thread-safe way to add new items to the map with individual ttl
|
||||
func (cache *Cache) SetWithTTL(key string, data interface{}, ttl time.Duration) {
|
||||
cache.mutex.Lock()
|
||||
item, exists, _ := cache.getItem(key)
|
||||
|
||||
if exists {
|
||||
item.data = data
|
||||
item.ttl = ttl
|
||||
} else {
|
||||
item = newItem(key, data, ttl)
|
||||
cache.items[key] = item
|
||||
}
|
||||
|
||||
if item.ttl >= 0 && (item.ttl > 0 || cache.ttl > 0) {
|
||||
if cache.ttl > 0 && item.ttl == 0 {
|
||||
item.ttl = cache.ttl
|
||||
}
|
||||
item.touch()
|
||||
}
|
||||
|
||||
if exists {
|
||||
cache.priorityQueue.update(item)
|
||||
} else {
|
||||
cache.priorityQueue.push(item)
|
||||
}
|
||||
|
||||
cache.mutex.Unlock()
|
||||
if !exists && cache.newItemCallback != nil {
|
||||
cache.newItemCallback(key, data)
|
||||
}
|
||||
cache.expirationNotification <- true
|
||||
}
|
||||
|
||||
// Get is a thread-safe way to lookup items
|
||||
// Every lookup, also touches the item, hence extending it's life
|
||||
func (cache *Cache) Get(key string) (interface{}, bool) {
|
||||
cache.mutex.Lock()
|
||||
item, exists, triggerExpirationNotification := cache.getItem(key)
|
||||
|
||||
var dataToReturn interface{}
|
||||
if exists {
|
||||
dataToReturn = item.data
|
||||
}
|
||||
cache.mutex.Unlock()
|
||||
if triggerExpirationNotification {
|
||||
cache.expirationNotification <- true
|
||||
}
|
||||
return dataToReturn, exists
|
||||
}
|
||||
|
||||
func (cache *Cache) Remove(key string) bool {
|
||||
cache.mutex.Lock()
|
||||
object, exists := cache.items[key]
|
||||
if !exists {
|
||||
cache.mutex.Unlock()
|
||||
return false
|
||||
}
|
||||
delete(cache.items, object.key)
|
||||
cache.priorityQueue.remove(object)
|
||||
cache.mutex.Unlock()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Count returns the number of items in the cache
|
||||
func (cache *Cache) Count() int {
|
||||
cache.mutex.Lock()
|
||||
length := len(cache.items)
|
||||
cache.mutex.Unlock()
|
||||
return length
|
||||
}
|
||||
|
||||
func (cache *Cache) SetTTL(ttl time.Duration) {
|
||||
cache.mutex.Lock()
|
||||
cache.ttl = ttl
|
||||
cache.mutex.Unlock()
|
||||
cache.expirationNotification <- true
|
||||
}
|
||||
|
||||
// SetExpirationCallback sets a callback that will be called when an item expires
|
||||
func (cache *Cache) SetExpirationCallback(callback expireCallback) {
|
||||
cache.expireCallback = callback
|
||||
}
|
||||
|
||||
// SetCheckExpirationCallback sets a callback that will be called when an item is about to expire
|
||||
// in order to allow external code to decide whether the item expires or remains for another TTL cycle
|
||||
func (cache *Cache) SetCheckExpirationCallback(callback checkExpireCallback) {
|
||||
cache.checkExpireCallback = callback
|
||||
}
|
||||
|
||||
// SetNewItemCallback sets a callback that will be called when a new item is added to the cache
|
||||
func (cache *Cache) SetNewItemCallback(callback expireCallback) {
|
||||
cache.newItemCallback = callback
|
||||
}
|
||||
|
||||
// SkipTtlExtensionOnHit allows the user to change the cache behaviour. When this flag is set to true it will
|
||||
// no longer extend TTL of items when they are retrieved using Get, or when their expiration condition is evaluated
|
||||
// using SetCheckExpirationCallback.
|
||||
func (cache *Cache) SkipTtlExtensionOnHit(value bool) {
|
||||
cache.skipTTLExtension = value
|
||||
}
|
||||
|
||||
// Purge will remove all entries
|
||||
func (cache *Cache) Purge() {
|
||||
cache.mutex.Lock()
|
||||
cache.items = make(map[string]*item)
|
||||
cache.priorityQueue = newPriorityQueue()
|
||||
cache.mutex.Unlock()
|
||||
}
|
||||
|
||||
// NewCache is a helper to create instance of the Cache struct
|
||||
func NewCache() *Cache {
|
||||
|
||||
shutdownChan := make(chan chan struct{})
|
||||
|
||||
cache := &Cache{
|
||||
items: make(map[string]*item),
|
||||
priorityQueue: newPriorityQueue(),
|
||||
expirationNotification: make(chan bool),
|
||||
expirationTime: time.Now(),
|
||||
shutdownSignal: shutdownChan,
|
||||
isShutDown: false,
|
||||
}
|
||||
go cache.startExpirationProcessing()
|
||||
return cache
|
||||
}
|
||||
|
||||
func min(duration time.Duration, second time.Duration) time.Duration {
|
||||
if duration < second {
|
||||
return duration
|
||||
}
|
||||
return second
|
||||
}
|
9
vendor/github.com/ReneKroon/ttlcache/go.mod
generated
vendored
Normal file
9
vendor/github.com/ReneKroon/ttlcache/go.mod
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
module github.com/ReneKroon/ttlcache
|
||||
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/stretchr/testify v1.3.0
|
||||
go.uber.org/goleak v0.10.0
|
||||
)
|
11
vendor/github.com/ReneKroon/ttlcache/go.sum
generated
vendored
Normal file
11
vendor/github.com/ReneKroon/ttlcache/go.sum
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
go.uber.org/goleak v0.10.0 h1:G3eWbSNIskeRqtsN/1uI5B+eP73y3JUuBsv9AZjehb4=
|
||||
go.uber.org/goleak v0.10.0/go.mod h1:VCZuO8V8mFPlL0F5J5GK1rtHV3DrFcQ1R8ryq7FK0aI=
|
46
vendor/github.com/ReneKroon/ttlcache/item.go
generated
vendored
Normal file
46
vendor/github.com/ReneKroon/ttlcache/item.go
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
package ttlcache
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// ItemNotExpire Will avoid the item being expired by TTL, but can still be exired by callback etc.
|
||||
ItemNotExpire time.Duration = -1
|
||||
// ItemExpireWithGlobalTTL will use the global TTL when set.
|
||||
ItemExpireWithGlobalTTL time.Duration = 0
|
||||
)
|
||||
|
||||
func newItem(key string, data interface{}, ttl time.Duration) *item {
|
||||
item := &item{
|
||||
data: data,
|
||||
ttl: ttl,
|
||||
key: key,
|
||||
}
|
||||
// since nobody is aware yet of this item, it's safe to touch without lock here
|
||||
item.touch()
|
||||
return item
|
||||
}
|
||||
|
||||
type item struct {
|
||||
key string
|
||||
data interface{}
|
||||
ttl time.Duration
|
||||
expireAt time.Time
|
||||
queueIndex int
|
||||
}
|
||||
|
||||
// Reset the item expiration time
|
||||
func (item *item) touch() {
|
||||
if item.ttl > 0 {
|
||||
item.expireAt = time.Now().Add(item.ttl)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify if the item is expired
|
||||
func (item *item) expired() bool {
|
||||
if item.ttl <= 0 {
|
||||
return false
|
||||
}
|
||||
return item.expireAt.Before(time.Now())
|
||||
}
|
71
vendor/github.com/ReneKroon/ttlcache/priority_queue.go
generated
vendored
Normal file
71
vendor/github.com/ReneKroon/ttlcache/priority_queue.go
generated
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
package ttlcache
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
)
|
||||
|
||||
func newPriorityQueue() *priorityQueue {
|
||||
queue := &priorityQueue{}
|
||||
heap.Init(queue)
|
||||
return queue
|
||||
}
|
||||
|
||||
type priorityQueue struct {
|
||||
items []*item
|
||||
}
|
||||
|
||||
func (pq *priorityQueue) update(item *item) {
|
||||
heap.Fix(pq, item.queueIndex)
|
||||
}
|
||||
|
||||
func (pq *priorityQueue) push(item *item) {
|
||||
heap.Push(pq, item)
|
||||
}
|
||||
|
||||
func (pq *priorityQueue) pop() *item {
|
||||
if pq.Len() == 0 {
|
||||
return nil
|
||||
}
|
||||
return heap.Pop(pq).(*item)
|
||||
}
|
||||
|
||||
func (pq *priorityQueue) remove(item *item) {
|
||||
heap.Remove(pq, item.queueIndex)
|
||||
}
|
||||
|
||||
func (pq priorityQueue) Len() int {
|
||||
length := len(pq.items)
|
||||
return length
|
||||
}
|
||||
|
||||
// Less will consider items with time.Time default value (epoch start) as more than set items.
|
||||
func (pq priorityQueue) Less(i, j int) bool {
|
||||
if pq.items[i].expireAt.IsZero() {
|
||||
return false
|
||||
}
|
||||
if pq.items[j].expireAt.IsZero() {
|
||||
return true
|
||||
}
|
||||
return pq.items[i].expireAt.Before(pq.items[j].expireAt)
|
||||
}
|
||||
|
||||
func (pq priorityQueue) Swap(i, j int) {
|
||||
pq.items[i], pq.items[j] = pq.items[j], pq.items[i]
|
||||
pq.items[i].queueIndex = i
|
||||
pq.items[j].queueIndex = j
|
||||
}
|
||||
|
||||
func (pq *priorityQueue) Push(x interface{}) {
|
||||
item := x.(*item)
|
||||
item.queueIndex = len(pq.items)
|
||||
pq.items = append(pq.items, item)
|
||||
}
|
||||
|
||||
func (pq *priorityQueue) Pop() interface{} {
|
||||
old := pq.items
|
||||
n := len(old)
|
||||
item := old[n-1]
|
||||
item.queueIndex = -1
|
||||
pq.items = old[0 : n-1]
|
||||
return item
|
||||
}
|
Reference in New Issue
Block a user