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 }