github.com/egonelbre/exp@v0.0.0-20240430123955-ed1d3aa93911/cache/random.go (about) 1 package cache 2 3 import ( 4 "github.com/zeebo/mwc" 5 ) 6 7 type Random struct { 8 items []Entry 9 lookup map[Hash]int 10 currentSize int 11 maxSize int 12 evicted EvictFunc 13 14 rng mwc.T 15 } 16 17 const ( 18 lowFactor = 20 19 highFactor = 80 20 divisor = 100 21 ) 22 23 func NewRandom(maxSize int, evicted EvictFunc) *Random { 24 return &Random{ 25 items: []Entry{}, 26 lookup: map[Hash]int{}, 27 currentSize: 0, 28 maxSize: maxSize, 29 evicted: evicted, 30 31 rng: *mwc.Rand(), 32 } 33 } 34 35 func NewRandomPrealloc(entries, maxSize int, evicted EvictFunc) *Random { 36 return &Random{ 37 items: make([]Entry, 0, entries), 38 lookup: make(map[Hash]int, entries), 39 currentSize: 0, 40 maxSize: maxSize, 41 evicted: evicted, 42 43 rng: *mwc.Rand(), 44 } 45 } 46 47 func (cache *Random) Add(hash Hash, size int) (ok bool) { 48 if size > cache.maxSize { 49 return false 50 } 51 52 cache.currentSize += size 53 for cache.currentSize > cache.maxSize { 54 cache.evict(len(cache.items) - 1) 55 } 56 57 index := len(cache.items) 58 cache.lookup[hash] = index 59 cache.items = append(cache.items, Entry{hash, size}) 60 cache.bump(index) 61 62 return true 63 } 64 65 func (cache *Random) bump(index int) { 66 low := index * lowFactor / divisor 67 high := index * highFactor / divisor 68 delta := high - low 69 70 if delta == 0 { 71 cache.swap(index, low) 72 return 73 } 74 75 // we don't need an unbiased random value 76 x := cache.rng.Uint64() % uint64(high-low) 77 cache.swap(index, low+int(x)) 78 } 79 80 func (cache *Random) swap(a, b int) { 81 if a == b { 82 return 83 } 84 85 cache.items[a], cache.items[b] = cache.items[b], cache.items[a] 86 cache.lookup[cache.items[a].Hash] = a 87 cache.lookup[cache.items[b].Hash] = b 88 } 89 90 func (cache *Random) evict(index int) { 91 last := len(cache.items) - 1 92 cache.swap(index, last) 93 94 item := cache.items[last] 95 cache.items = cache.items[:last] 96 cache.currentSize -= item.Size 97 delete(cache.lookup, item.Hash) 98 99 if cache.evicted != nil { 100 cache.evicted(item) 101 } 102 } 103 104 func (cache *Random) Get(hash Hash) (e Entry, ok bool) { 105 index, ok := cache.lookup[hash] 106 if !ok { 107 return Entry{}, false 108 } 109 110 entry := cache.items[index] 111 cache.bump(index) 112 return entry, true 113 } 114 115 func (cache *Random) Remove(hash Hash) { 116 index, ok := cache.lookup[hash] 117 if !ok { 118 return 119 } 120 cache.evict(index) 121 } 122 123 func (cache *Random) Len() int { return len(cache.items) } 124 func (cache *Random) CurrentSize() int { return cache.currentSize } 125 func (cache *Random) MaxSize() int { return cache.maxSize }