github.com/egonelbre/exp@v0.0.0-20240430123955-ed1d3aa93911/cache/half.go (about)

     1  package cache
     2  
     3  type Half struct {
     4  	items       []Entry
     5  	lookup      map[Hash]int
     6  	currentSize int
     7  	maxSize     int
     8  	evicted     EvictFunc
     9  }
    10  
    11  func NewHalf(maxSize int, evicted EvictFunc) *Half {
    12  	return &Half{
    13  		items:       []Entry{},
    14  		lookup:      map[Hash]int{},
    15  		currentSize: 0,
    16  		maxSize:     maxSize,
    17  		evicted:     evicted,
    18  	}
    19  }
    20  
    21  func NewHalfPrealloc(entries, maxSize int, evicted EvictFunc) *Half {
    22  	return &Half{
    23  		items:       make([]Entry, 0, entries),
    24  		lookup:      make(map[Hash]int, entries),
    25  		currentSize: 0,
    26  		maxSize:     maxSize,
    27  		evicted:     evicted,
    28  	}
    29  }
    30  
    31  func (cache *Half) Add(hash Hash, size int) (ok bool) {
    32  	if size > cache.maxSize {
    33  		return false
    34  	}
    35  
    36  	cache.currentSize += size
    37  	for cache.currentSize > cache.maxSize {
    38  		cache.evict(len(cache.items) - 1)
    39  	}
    40  
    41  	index := len(cache.items)
    42  	cache.lookup[hash] = index
    43  	cache.items = append(cache.items, Entry{hash, size})
    44  	cache.bump(index)
    45  
    46  	return true
    47  }
    48  
    49  func (cache *Half) bump(index int) {
    50  	// this could be randomized or use a power of two choices
    51  	cache.swap(index, index/2)
    52  }
    53  
    54  func (cache *Half) swap(a, b int) {
    55  	if a == b {
    56  		return
    57  	}
    58  
    59  	cache.items[a], cache.items[b] = cache.items[b], cache.items[a]
    60  	cache.lookup[cache.items[a].Hash] = a
    61  	cache.lookup[cache.items[b].Hash] = b
    62  }
    63  
    64  func (cache *Half) evict(index int) {
    65  	last := len(cache.items) - 1
    66  	cache.swap(index, last)
    67  
    68  	item := cache.items[last]
    69  	cache.items = cache.items[:last]
    70  	cache.currentSize -= item.Size
    71  	delete(cache.lookup, item.Hash)
    72  
    73  	if cache.evicted != nil {
    74  		cache.evicted(item)
    75  	}
    76  }
    77  
    78  func (cache *Half) Get(hash Hash) (e Entry, ok bool) {
    79  	index, ok := cache.lookup[hash]
    80  	if !ok {
    81  		return Entry{}, false
    82  	}
    83  
    84  	entry := cache.items[index]
    85  	cache.bump(index)
    86  	return entry, true
    87  }
    88  
    89  func (cache *Half) Remove(hash Hash) {
    90  	index, ok := cache.lookup[hash]
    91  	if !ok {
    92  		return
    93  	}
    94  	cache.evict(index)
    95  }
    96  
    97  func (cache *Half) Len() int         { return len(cache.items) }
    98  func (cache *Half) CurrentSize() int { return cache.currentSize }
    99  func (cache *Half) MaxSize() int     { return cache.maxSize }