echoip/http/cache.go

100 lines
1.7 KiB
Go
Raw Normal View History

2019-12-25 21:04:26 +01:00
package http
import (
"container/list"
2020-09-11 20:52:35 +02:00
"fmt"
2019-12-25 21:04:26 +01:00
"hash/fnv"
"net"
"sync"
)
type Cache struct {
2020-09-11 21:55:09 +02:00
capacity int
mu sync.RWMutex
entries map[uint64]*list.Element
values *list.List
evictions uint64
2019-12-25 21:04:26 +01:00
}
2020-09-11 20:52:35 +02:00
type CacheStats struct {
2020-09-11 21:55:09 +02:00
Capacity int
Size int
Evictions uint64
2020-09-11 20:52:35 +02:00
}
2019-12-25 21:04:26 +01:00
func NewCache(capacity int) *Cache {
if capacity < 0 {
capacity = 0
}
return &Cache{
capacity: capacity,
2020-09-11 20:51:04 +02:00
entries: make(map[uint64]*list.Element),
values: list.New(),
2019-12-25 21:04:26 +01:00
}
}
func key(ip net.IP) uint64 {
h := fnv.New64a()
h.Write(ip)
return h.Sum64()
}
2020-09-05 22:07:35 +02:00
func (c *Cache) Set(ip net.IP, resp Response) {
2019-12-25 21:04:26 +01:00
if c.capacity == 0 {
return
}
k := key(ip)
c.mu.Lock()
defer c.mu.Unlock()
2020-09-11 21:16:43 +02:00
minEvictions := len(c.entries) - c.capacity + 1
if minEvictions > 0 { // At or above capacity. Shrink the cache
evicted := 0
for el := c.values.Front(); el != nil && evicted < minEvictions; {
value := el.Value.(Response)
delete(c.entries, key(value.IP))
next := el.Next()
c.values.Remove(el)
el = next
evicted++
}
2020-09-11 21:55:09 +02:00
c.evictions += uint64(evicted)
2019-12-25 21:04:26 +01:00
}
2020-09-11 20:51:04 +02:00
current, ok := c.entries[k]
if ok {
c.values.Remove(current)
}
c.entries[k] = c.values.PushBack(resp)
2019-12-25 21:04:26 +01:00
}
2020-09-05 22:07:35 +02:00
func (c *Cache) Get(ip net.IP) (Response, bool) {
2019-12-25 21:04:26 +01:00
k := key(ip)
c.mu.RLock()
defer c.mu.RUnlock()
r, ok := c.entries[k]
2020-09-11 20:51:04 +02:00
if !ok {
return Response{}, false
}
return r.Value.(Response), true
2019-12-25 21:04:26 +01:00
}
2020-09-11 20:52:35 +02:00
2020-09-11 21:16:43 +02:00
func (c *Cache) Resize(capacity int) error {
if capacity < 0 {
return fmt.Errorf("invalid capacity: %d\n", capacity)
}
c.mu.Lock()
defer c.mu.Unlock()
c.capacity = capacity
2020-09-11 21:55:09 +02:00
c.evictions = 0
2020-09-11 21:16:43 +02:00
return nil
}
2020-09-11 20:52:35 +02:00
func (c *Cache) Stats() CacheStats {
c.mu.RLock()
defer c.mu.RUnlock()
return CacheStats{
2020-09-11 21:55:09 +02:00
Size: len(c.entries),
Capacity: c.capacity,
Evictions: c.evictions,
2020-09-11 20:52:35 +02:00
}
}