| 
									
										
										
										
											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. | 
					
						
							|  |  |  | func Hash(what []byte) uint32 { | 
					
						
							|  |  |  | 	h := fnv.New32() | 
					
						
							|  |  |  | 	h.Write(what) | 
					
						
							|  |  |  | 	return h.Sum32() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Cache is cache. | 
					
						
							|  |  |  | type Cache struct { | 
					
						
							|  |  |  | 	shards [shardSize]*shard | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // shard is a cache with random eviction. | 
					
						
							|  |  |  | type shard struct { | 
					
						
							|  |  |  | 	items map[uint32]interface{} | 
					
						
							|  |  |  | 	size  int | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sync.RWMutex | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // New returns a new cache. | 
					
						
							|  |  |  | func New(size int) *Cache { | 
					
						
							|  |  |  | 	ssize := size / shardSize | 
					
						
							|  |  |  | 	if ssize < 512 { | 
					
						
							|  |  |  | 		ssize = 512 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	c := &Cache{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Initialize all the shards | 
					
						
							|  |  |  | 	for i := 0; i < shardSize; i++ { | 
					
						
							|  |  |  | 		c.shards[i] = newShard(ssize) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return c | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Add adds a new element to the cache. If the element already exists it is overwritten. | 
					
						
							|  |  |  | func (c *Cache) Add(key uint32, el interface{}) { | 
					
						
							|  |  |  | 	shard := key & (shardSize - 1) | 
					
						
							|  |  |  | 	c.shards[shard].Add(key, el) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Get looks up element index under key. | 
					
						
							|  |  |  | func (c *Cache) Get(key uint32) (interface{}, bool) { | 
					
						
							|  |  |  | 	shard := key & (shardSize - 1) | 
					
						
							|  |  |  | 	return c.shards[shard].Get(key) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Remove removes the element indexed with key. | 
					
						
							|  |  |  | func (c *Cache) Remove(key uint32) { | 
					
						
							|  |  |  | 	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 | 
					
						
							|  |  |  | 	for _, s := range c.shards { | 
					
						
							|  |  |  | 		l += s.Len() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return l | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // newShard returns a new shard with size. | 
					
						
							|  |  |  | func newShard(size int) *shard { return &shard{items: make(map[uint32]interface{}), size: size} } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Add adds element indexed by key into the cache. Any existing element is overwritten | 
					
						
							|  |  |  | func (s *shard) Add(key uint32, el interface{}) { | 
					
						
							|  |  |  | 	l := s.Len() | 
					
						
							|  |  |  | 	if l+1 > s.size { | 
					
						
							|  |  |  | 		s.Evict() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	s.Lock() | 
					
						
							|  |  |  | 	s.items[key] = el | 
					
						
							|  |  |  | 	s.Unlock() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Remove removes the element indexed by key from the cache. | 
					
						
							|  |  |  | func (s *shard) Remove(key uint32) { | 
					
						
							|  |  |  | 	s.Lock() | 
					
						
							|  |  |  | 	delete(s.items, key) | 
					
						
							|  |  |  | 	s.Unlock() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Evict removes a random element from the cache. | 
					
						
							|  |  |  | func (s *shard) Evict() { | 
					
						
							|  |  |  | 	key := -1 | 
					
						
							| 
									
										
										
										
											2017-10-23 17:24:48 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	s.RLock() | 
					
						
							| 
									
										
										
										
											2017-06-13 12:39:10 -07:00
										 |  |  | 	for k := range s.items { | 
					
						
							|  |  |  | 		key = int(k) | 
					
						
							|  |  |  | 		break | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-10-23 17:24:48 +01:00
										 |  |  | 	s.RUnlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-13 12:39:10 -07:00
										 |  |  | 	if key == -1 { | 
					
						
							|  |  |  | 		// empty cache | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-10-23 17:24:48 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// If this item is gone between the RUnlock and Lock race we don't care. | 
					
						
							|  |  |  | 	s.Remove(uint32(key)) | 
					
						
							| 
									
										
										
										
											2017-06-13 12:39:10 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Get looks up the element indexed under key. | 
					
						
							|  |  |  | func (s *shard) Get(key uint32) (interface{}, bool) { | 
					
						
							|  |  |  | 	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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const shardSize = 256 |