github.com/MetalBlockchain/metalgo@v1.11.9/cache/unique_cache.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package cache
     5  
     6  import (
     7  	"sync"
     8  
     9  	"github.com/MetalBlockchain/metalgo/utils/linked"
    10  )
    11  
    12  var _ Deduplicator[struct{}, Evictable[struct{}]] = (*EvictableLRU[struct{}, Evictable[struct{}]])(nil)
    13  
    14  // EvictableLRU is an LRU cache that notifies the objects when they are evicted.
    15  type EvictableLRU[K comparable, V Evictable[K]] struct {
    16  	lock      sync.Mutex
    17  	entryMap  map[K]*linked.ListElement[V]
    18  	entryList *linked.List[V]
    19  	Size      int
    20  }
    21  
    22  func (c *EvictableLRU[_, V]) Deduplicate(value V) V {
    23  	c.lock.Lock()
    24  	defer c.lock.Unlock()
    25  
    26  	return c.deduplicate(value)
    27  }
    28  
    29  func (c *EvictableLRU[_, _]) Flush() {
    30  	c.lock.Lock()
    31  	defer c.lock.Unlock()
    32  
    33  	c.flush()
    34  }
    35  
    36  func (c *EvictableLRU[K, V]) init() {
    37  	if c.entryMap == nil {
    38  		c.entryMap = make(map[K]*linked.ListElement[V])
    39  	}
    40  	if c.entryList == nil {
    41  		c.entryList = linked.NewList[V]()
    42  	}
    43  	if c.Size <= 0 {
    44  		c.Size = 1
    45  	}
    46  }
    47  
    48  func (c *EvictableLRU[_, V]) resize() {
    49  	for c.entryList.Len() > c.Size {
    50  		e := c.entryList.Front()
    51  		c.entryList.Remove(e)
    52  
    53  		delete(c.entryMap, e.Value.Key())
    54  		e.Value.Evict()
    55  	}
    56  }
    57  
    58  func (c *EvictableLRU[_, V]) deduplicate(value V) V {
    59  	c.init()
    60  	c.resize()
    61  
    62  	key := value.Key()
    63  	if e, ok := c.entryMap[key]; !ok {
    64  		if c.entryList.Len() >= c.Size {
    65  			e = c.entryList.Front()
    66  			c.entryList.MoveToBack(e)
    67  
    68  			delete(c.entryMap, e.Value.Key())
    69  			e.Value.Evict()
    70  
    71  			e.Value = value
    72  		} else {
    73  			e = &linked.ListElement[V]{
    74  				Value: value,
    75  			}
    76  			c.entryList.PushBack(e)
    77  		}
    78  		c.entryMap[key] = e
    79  	} else {
    80  		c.entryList.MoveToBack(e)
    81  
    82  		value = e.Value
    83  	}
    84  	return value
    85  }
    86  
    87  func (c *EvictableLRU[_, _]) flush() {
    88  	c.init()
    89  
    90  	size := c.Size
    91  	c.Size = 0
    92  	c.resize()
    93  	c.Size = size
    94  }