blob: 8c151a6412341f5405fa2e43418822625977196e [file] [log] [blame]
Jan Schär75ea9f42024-07-29 17:01:41 +02001// Package cache implements a cache. The cache hold 256 shards, each shard
2// holds a cache: a map with a mutex. There is no fancy expunge algorithm, it
3// just randomly evicts elements when it gets full.
4package cache
5
6// Taken and modified from CoreDNS, under Apache 2.0.
7
8import (
9 "sync"
10
11 "golang.org/x/sys/cpu"
12)
13
14const shardSize = 256
15
16// Cache is cache.
17type Cache[T any] struct {
18 shards [shardSize]shard[T]
19}
20
21// shard is a cache with random eviction.
22type shard[T any] struct {
23 items map[uint64]T
24 size int
25
26 sync.RWMutex
27
28 _ cpu.CacheLinePad
29}
30
31// New returns a new cache.
32func New[T any](size int) *Cache[T] {
33 ssize := size / shardSize
34 if ssize < 4 {
35 ssize = 4
36 }
37
38 c := &Cache[T]{}
39
40 // Initialize all the shards
41 for i := 0; i < shardSize; i++ {
42 c.shards[i] = shard[T]{items: make(map[uint64]T), size: ssize}
43 }
44 return c
45}
46
47// Get returns the element under key, and whether the element exists.
48func (c *Cache[T]) Get(key uint64) (el T, exists bool) {
49 shard := key % shardSize
50 return c.shards[shard].Get(key)
51}
52
53// Put adds a new element to the cache. If the element already exists,
54// it is overwritten.
55func (c *Cache[T]) Put(key uint64, el T) {
56 shard := key % shardSize
57 c.shards[shard].Put(key, el)
58}
59
60// GetOrPut returns the element under key if it exists,
61// or else stores newEl there. This operation is atomic.
62func (c *Cache[T]) GetOrPut(key uint64, newEl T) (el T, exists bool) {
63 shard := key % shardSize
64 return c.shards[shard].GetOrPut(key, newEl)
65}
66
67// Remove removes the element indexed with key.
68func (c *Cache[T]) Remove(key uint64) {
69 shard := key % shardSize
70 c.shards[shard].Remove(key)
71}
72
73func (s *shard[T]) Get(key uint64) (el T, exists bool) {
74 s.RLock()
75 el, exists = s.items[key]
76 s.RUnlock()
77 return
78}
79
80func (s *shard[T]) Put(key uint64, el T) {
81 s.Lock()
82 if len(s.items) >= s.size {
83 if _, ok := s.items[key]; !ok {
84 for k := range s.items {
85 delete(s.items, k)
86 break
87 }
88 }
89 }
90 s.items[key] = el
91 s.Unlock()
92}
93
94func (s *shard[T]) GetOrPut(key uint64, newEl T) (el T, exists bool) {
95 s.Lock()
96 el, exists = s.items[key]
97 if !exists {
98 if len(s.items) >= s.size {
99 for k := range s.items {
100 delete(s.items, k)
101 break
102 }
103 }
104 s.items[key] = newEl
105 el = newEl
106 }
107 s.Unlock()
108 return
109}
110
111func (s *shard[T]) Remove(key uint64) {
112 s.Lock()
113 delete(s.items, key)
114 s.Unlock()
115}