2019-12-25 21:04:26 +01:00
|
|
|
package http
|
|
|
|
|
|
|
|
import (
|
2020-09-07 22:40:06 +02:00
|
|
|
"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 {
|
|
|
|
capacity int
|
|
|
|
mu sync.RWMutex
|
2020-09-11 20:51:04 +02:00
|
|
|
entries map[uint64]*list.Element
|
|
|
|
values *list.List
|
2019-12-25 21:04:26 +01:00
|
|
|
}
|
|
|
|
|
2020-09-11 20:52:35 +02:00
|
|
|
type CacheStats struct {
|
|
|
|
Capacity int
|
|
|
|
Size int
|
|
|
|
}
|
|
|
|
|
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++
|
|
|
|
}
|
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
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-09-11 20:52:35 +02:00
|
|
|
func (c *Cache) Stats() CacheStats {
|
|
|
|
c.mu.RLock()
|
|
|
|
defer c.mu.RUnlock()
|
|
|
|
return CacheStats{
|
|
|
|
Size: len(c.entries),
|
|
|
|
Capacity: c.capacity,
|
|
|
|
}
|
|
|
|
}
|