| 
									
										
										
										
											2017-06-13 12:39:10 -07:00
										 |  |  | // Package cache implements a cache. The cache hold 256 shards, each shard
 | 
					
						
							|  |  |  | // holds a cache: a map with a mutex. There is no fancy expunge algorithm, it
 | 
					
						
							|  |  |  | // just randomly evicts elements when it gets full.
 | 
					
						
							|  |  |  | package cache
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import (
 | 
					
						
							|  |  |  | 	"hash/fnv"
 | 
					
						
							|  |  |  | 	"sync"
 | 
					
						
							|  |  |  | )
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Hash returns the FNV hash of what.
 | 
					
						
							| 
									
										
										
										
											2018-08-31 17:26:43 -04:00
										 |  |  | func Hash(what []byte) uint64 {
 | 
					
						
							|  |  |  | 	h := fnv.New64()
 | 
					
						
							| 
									
										
										
										
											2017-06-13 12:39:10 -07:00
										 |  |  | 	h.Write(what)
 | 
					
						
							| 
									
										
										
										
											2018-08-31 17:26:43 -04:00
										 |  |  | 	return h.Sum64()
 | 
					
						
							| 
									
										
										
										
											2017-06-13 12:39:10 -07:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Cache is cache.
 | 
					
						
							|  |  |  | type Cache struct {
 | 
					
						
							|  |  |  | 	shards [shardSize]*shard
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // shard is a cache with random eviction.
 | 
					
						
							|  |  |  | type shard struct {
 | 
					
						
							| 
									
										
										
										
											2025-09-10 23:08:27 +03:00
										 |  |  | 	items map[uint64]any
 | 
					
						
							| 
									
										
										
										
											2017-06-13 12:39:10 -07:00
										 |  |  | 	size  int
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sync.RWMutex
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // New returns a new cache.
 | 
					
						
							|  |  |  | func New(size int) *Cache {
 | 
					
						
							| 
									
										
										
										
											2025-09-10 23:08:27 +03:00
										 |  |  | 	ssize := max(size/shardSize, 4)
 | 
					
						
							| 
									
										
										
										
											2017-06-13 12:39:10 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	c := &Cache{}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Initialize all the shards
 | 
					
						
							| 
									
										
										
										
											2025-05-29 03:50:55 +03:00
										 |  |  | 	for i := range shardSize {
 | 
					
						
							| 
									
										
										
										
											2017-06-13 12:39:10 -07:00
										 |  |  | 		c.shards[i] = newShard(ssize)
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	return c
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Add adds a new element to the cache. If the element already exists it is overwritten.
 | 
					
						
							| 
									
										
										
										
											2021-03-21 08:58:16 -07:00
										 |  |  | // Returns true if an existing element was evicted to make room for this element.
 | 
					
						
							| 
									
										
										
										
											2025-09-10 23:08:27 +03:00
										 |  |  | func (c *Cache) Add(key uint64, el any) bool {
 | 
					
						
							| 
									
										
										
										
											2017-06-13 12:39:10 -07:00
										 |  |  | 	shard := key & (shardSize - 1)
 | 
					
						
							| 
									
										
										
										
											2021-03-21 08:58:16 -07:00
										 |  |  | 	return c.shards[shard].Add(key, el)
 | 
					
						
							| 
									
										
										
										
											2017-06-13 12:39:10 -07:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Get looks up element index under key.
 | 
					
						
							| 
									
										
										
										
											2025-09-10 23:08:27 +03:00
										 |  |  | func (c *Cache) Get(key uint64) (any, bool) {
 | 
					
						
							| 
									
										
										
										
											2017-06-13 12:39:10 -07:00
										 |  |  | 	shard := key & (shardSize - 1)
 | 
					
						
							|  |  |  | 	return c.shards[shard].Get(key)
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Remove removes the element indexed with key.
 | 
					
						
							| 
									
										
										
										
											2018-08-31 17:26:43 -04:00
										 |  |  | func (c *Cache) Remove(key uint64) {
 | 
					
						
							| 
									
										
										
										
											2017-06-13 12:39:10 -07:00
										 |  |  | 	shard := key & (shardSize - 1)
 | 
					
						
							|  |  |  | 	c.shards[shard].Remove(key)
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Len returns the number of elements in the cache.
 | 
					
						
							|  |  |  | func (c *Cache) Len() int {
 | 
					
						
							|  |  |  | 	l := 0
 | 
					
						
							| 
									
										
										
										
											2022-03-18 10:13:58 -07:00
										 |  |  | 	for _, s := range &c.shards {
 | 
					
						
							| 
									
										
										
										
											2017-06-13 12:39:10 -07:00
										 |  |  | 		l += s.Len()
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	return l
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:45:28 +02:00
										 |  |  | // Walk walks each shard in the cache.
 | 
					
						
							| 
									
										
										
										
											2025-09-10 23:08:27 +03:00
										 |  |  | func (c *Cache) Walk(f func(map[uint64]any, uint64) bool) {
 | 
					
						
							| 
									
										
										
										
											2022-03-18 10:13:58 -07:00
										 |  |  | 	for _, s := range &c.shards {
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:45:28 +02:00
										 |  |  | 		s.Walk(f)
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-13 12:39:10 -07:00
										 |  |  | // newShard returns a new shard with size.
 | 
					
						
							| 
									
										
										
										
											2025-09-10 23:08:27 +03:00
										 |  |  | func newShard(size int) *shard { return &shard{items: make(map[uint64]any), size: size} }
 | 
					
						
							| 
									
										
										
										
											2017-06-13 12:39:10 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Add adds element indexed by key into the cache. Any existing element is overwritten
 | 
					
						
							| 
									
										
										
										
											2021-03-21 08:58:16 -07:00
										 |  |  | // Returns true if an existing element was evicted to make room for this element.
 | 
					
						
							| 
									
										
										
										
											2025-09-10 23:08:27 +03:00
										 |  |  | func (s *shard) Add(key uint64, el any) bool {
 | 
					
						
							| 
									
										
										
										
											2021-03-21 08:58:16 -07:00
										 |  |  | 	eviction := false
 | 
					
						
							| 
									
										
										
										
											2017-06-13 12:39:10 -07:00
										 |  |  | 	s.Lock()
 | 
					
						
							| 
									
										
										
										
											2019-07-19 05:19:03 -04:00
										 |  |  | 	if len(s.items) >= s.size {
 | 
					
						
							|  |  |  | 		if _, ok := s.items[key]; !ok {
 | 
					
						
							|  |  |  | 			for k := range s.items {
 | 
					
						
							|  |  |  | 				delete(s.items, k)
 | 
					
						
							| 
									
										
										
										
											2021-03-21 08:58:16 -07:00
										 |  |  | 				eviction = true
 | 
					
						
							| 
									
										
										
										
											2019-07-19 05:19:03 -04:00
										 |  |  | 				break
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2017-06-13 12:39:10 -07:00
										 |  |  | 	s.items[key] = el
 | 
					
						
							|  |  |  | 	s.Unlock()
 | 
					
						
							| 
									
										
										
										
											2021-03-21 08:58:16 -07:00
										 |  |  | 	return eviction
 | 
					
						
							| 
									
										
										
										
											2017-06-13 12:39:10 -07:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Remove removes the element indexed by key from the cache.
 | 
					
						
							| 
									
										
										
										
											2018-08-31 17:26:43 -04:00
										 |  |  | func (s *shard) Remove(key uint64) {
 | 
					
						
							| 
									
										
										
										
											2017-06-13 12:39:10 -07:00
										 |  |  | 	s.Lock()
 | 
					
						
							|  |  |  | 	delete(s.items, key)
 | 
					
						
							|  |  |  | 	s.Unlock()
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Evict removes a random element from the cache.
 | 
					
						
							|  |  |  | func (s *shard) Evict() {
 | 
					
						
							| 
									
										
										
										
											2019-07-19 05:19:03 -04:00
										 |  |  | 	s.Lock()
 | 
					
						
							| 
									
										
										
										
											2017-06-13 12:39:10 -07:00
										 |  |  | 	for k := range s.items {
 | 
					
						
							| 
									
										
										
										
											2019-07-19 05:19:03 -04:00
										 |  |  | 		delete(s.items, k)
 | 
					
						
							| 
									
										
										
										
											2017-06-13 12:39:10 -07:00
										 |  |  | 		break
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2019-07-19 05:19:03 -04:00
										 |  |  | 	s.Unlock()
 | 
					
						
							| 
									
										
										
										
											2017-06-13 12:39:10 -07:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Get looks up the element indexed under key.
 | 
					
						
							| 
									
										
										
										
											2025-09-10 23:08:27 +03:00
										 |  |  | func (s *shard) Get(key uint64) (any, bool) {
 | 
					
						
							| 
									
										
										
										
											2017-06-13 12:39:10 -07:00
										 |  |  | 	s.RLock()
 | 
					
						
							|  |  |  | 	el, found := s.items[key]
 | 
					
						
							|  |  |  | 	s.RUnlock()
 | 
					
						
							|  |  |  | 	return el, found
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Len returns the current length of the cache.
 | 
					
						
							|  |  |  | func (s *shard) Len() int {
 | 
					
						
							|  |  |  | 	s.RLock()
 | 
					
						
							|  |  |  | 	l := len(s.items)
 | 
					
						
							|  |  |  | 	s.RUnlock()
 | 
					
						
							|  |  |  | 	return l
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:45:28 +02:00
										 |  |  | // Walk walks the shard for each element the function f is executed while holding a write lock.
 | 
					
						
							| 
									
										
										
										
											2025-09-10 23:08:27 +03:00
										 |  |  | func (s *shard) Walk(f func(map[uint64]any, uint64) bool) {
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:45:28 +02:00
										 |  |  | 	s.RLock()
 | 
					
						
							| 
									
										
										
										
											2021-10-24 11:17:47 +08:00
										 |  |  | 	items := make([]uint64, len(s.items))
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:45:28 +02:00
										 |  |  | 	i := 0
 | 
					
						
							|  |  |  | 	for k := range s.items {
 | 
					
						
							|  |  |  | 		items[i] = k
 | 
					
						
							|  |  |  | 		i++
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	s.RUnlock()
 | 
					
						
							|  |  |  | 	for _, k := range items {
 | 
					
						
							|  |  |  | 		s.Lock()
 | 
					
						
							|  |  |  | 		ok := f(s.items, k)
 | 
					
						
							|  |  |  | 		s.Unlock()
 | 
					
						
							|  |  |  | 		if !ok {
 | 
					
						
							|  |  |  | 			return
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-13 12:39:10 -07:00
										 |  |  | const shardSize = 256
 |