2
0

lrubank.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. package stringutil
  2. import (
  3. "container/heap"
  4. "sync"
  5. "time"
  6. )
  7. type lruEntry struct {
  8. value string
  9. used time.Time
  10. }
  11. type maxHeap []*lruEntry
  12. func (h maxHeap) Len() int { return len(h) }
  13. func (h maxHeap) Less(i, j int) bool { return h[i].used.After(h[j].used) } // newer = "larger"
  14. func (h maxHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
  15. func (h *maxHeap) Push(x any) {
  16. *h = append(*h, x.(*lruEntry))
  17. }
  18. func (h *maxHeap) Pop() any {
  19. old := *h
  20. n := len(old)
  21. x := old[n-1]
  22. *h = old[:n-1]
  23. return x
  24. }
  25. func nOldest(arr []*lruEntry, n int) []*lruEntry {
  26. if n <= 0 {
  27. return []*lruEntry{}
  28. }
  29. if n >= len(arr) {
  30. return arr
  31. }
  32. h := maxHeap(arr[:n])
  33. heap.Init(&h)
  34. for _, entry := range arr[n:] {
  35. // swap in oldest, re-heapify
  36. if entry.used.Before(h[0].used) {
  37. h[0] = entry
  38. heap.Fix(&h, 0)
  39. }
  40. }
  41. return []*lruEntry(h)
  42. }
  43. type lruStringBank struct {
  44. lock sync.Mutex
  45. stop chan struct{}
  46. m map[string]*lruEntry
  47. }
  48. func NewLruStringBank(capacity int, evictionInterval time.Duration) StringBank {
  49. stop := make(chan struct{})
  50. bank := &lruStringBank{
  51. m: make(map[string]*lruEntry),
  52. }
  53. go func() {
  54. for {
  55. select {
  56. case <-stop:
  57. return
  58. case <-time.After(evictionInterval):
  59. }
  60. // need to take the lock during eviction
  61. bank.lock.Lock()
  62. if len(bank.m) <= capacity {
  63. bank.lock.Unlock()
  64. continue
  65. }
  66. // we collect a list of all lru entries so we can max heap the first n elements
  67. arr := make([]*lruEntry, 0, len(bank.m))
  68. for _, v := range bank.m {
  69. arr = append(arr, v)
  70. }
  71. oldest := nOldest(arr, len(bank.m)-capacity)
  72. for _, old := range oldest {
  73. delete(bank.m, old.value)
  74. }
  75. bank.lock.Unlock()
  76. }
  77. }()
  78. return bank
  79. }
  80. func (sb *lruStringBank) Stop() {
  81. sb.lock.Lock()
  82. defer sb.lock.Unlock()
  83. if sb.stop != nil {
  84. close(sb.stop)
  85. sb.stop = nil
  86. }
  87. }
  88. func (sb *lruStringBank) LoadOrStore(key, value string) (string, bool) {
  89. sb.lock.Lock()
  90. if v, ok := sb.m[key]; ok {
  91. v.used = time.Now().UTC()
  92. sb.lock.Unlock()
  93. return v.value, ok
  94. }
  95. sb.m[key] = &lruEntry{
  96. value: value,
  97. used: time.Now().UTC(),
  98. }
  99. sb.lock.Unlock()
  100. return value, false
  101. }
  102. func (sb *lruStringBank) LoadOrStoreFunc(key string, f func() string) (string, bool) {
  103. sb.lock.Lock()
  104. if v, ok := sb.m[key]; ok {
  105. v.used = time.Now().UTC()
  106. sb.lock.Unlock()
  107. return v.value, ok
  108. }
  109. // create the key and value using the func (the key could be deallocated later)
  110. value := f()
  111. sb.m[value] = &lruEntry{
  112. value: value,
  113. used: time.Now().UTC(),
  114. }
  115. sb.lock.Unlock()
  116. return value, false
  117. }
  118. func (sb *lruStringBank) Clear() {
  119. sb.lock.Lock()
  120. sb.m = make(map[string]*lruEntry)
  121. sb.lock.Unlock()
  122. }