mirror of
https://github.com/nadoo/glider.git
synced 2025-02-23 01:15:41 +08:00
128 lines
2.5 KiB
Go
128 lines
2.5 KiB
Go
package dns
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// LruCache is the struct of LruCache.
|
|
type LruCache struct {
|
|
mu sync.Mutex
|
|
size int
|
|
head *item
|
|
tail *item
|
|
cache map[string]*item
|
|
store map[string][]byte
|
|
}
|
|
|
|
// item is the struct of cache item.
|
|
type item struct {
|
|
key string
|
|
val []byte
|
|
exp int64
|
|
prev *item
|
|
next *item
|
|
}
|
|
|
|
// NewLruCache returns a new LruCache.
|
|
func NewLruCache(size int) *LruCache {
|
|
// init 2 items here, it doesn't matter cuz they will be deleted when the cache is full
|
|
head, tail := &item{key: "head"}, &item{key: "tail"}
|
|
head.next, tail.prev = tail, head
|
|
c := &LruCache{
|
|
size: size,
|
|
head: head,
|
|
tail: tail,
|
|
cache: make(map[string]*item, size),
|
|
store: make(map[string][]byte),
|
|
}
|
|
c.cache[head.key], c.cache[tail.key] = head, tail
|
|
return c
|
|
}
|
|
|
|
// Get gets an item from cache.
|
|
func (c *LruCache) Get(k string) (v []byte, expired bool) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
|
|
if v, ok := c.store[k]; ok {
|
|
return v, false
|
|
}
|
|
|
|
if it, ok := c.cache[k]; ok {
|
|
v = it.val
|
|
if it.exp < time.Now().Unix() {
|
|
expired = true
|
|
}
|
|
c.moveToHead(it)
|
|
}
|
|
return
|
|
}
|
|
|
|
// Set sets an item with key, value, and ttl(seconds).
|
|
// if the ttl is zero, this item will be set and never be deleted.
|
|
// if the key exists, update it with value and exp and move it to head.
|
|
// if the key does not exist, put a new item to the cache's head.
|
|
// finally, remove the tail if the cache is full.
|
|
func (c *LruCache) Set(k string, v []byte, ttl int) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
|
|
if ttl == 0 {
|
|
c.store[k] = v
|
|
return
|
|
}
|
|
|
|
exp := time.Now().Add(time.Second * time.Duration(ttl)).Unix()
|
|
if it, ok := c.cache[k]; ok {
|
|
it.val = v
|
|
it.exp = exp
|
|
c.moveToHead(it)
|
|
return
|
|
}
|
|
|
|
c.putToHead(k, v, exp)
|
|
|
|
// NOTE: the cache size will always >= 2,
|
|
// but it doesn't matter in our environment.
|
|
if len(c.cache) > c.size {
|
|
c.removeTail()
|
|
}
|
|
}
|
|
|
|
// putToHead puts a new item to cache's head.
|
|
func (c *LruCache) putToHead(k string, v []byte, exp int64) {
|
|
it := &item{key: k, val: v, exp: exp, prev: nil, next: c.head}
|
|
it.prev = nil
|
|
it.next = c.head
|
|
c.head.prev = it
|
|
c.head = it
|
|
|
|
c.cache[k] = it
|
|
}
|
|
|
|
// moveToHead moves an existing item to cache's head.
|
|
func (c *LruCache) moveToHead(it *item) {
|
|
if it != c.head {
|
|
if c.tail == it {
|
|
c.tail = it.prev
|
|
c.tail.next = nil
|
|
} else {
|
|
it.prev.next = it.next
|
|
it.next.prev = it.prev
|
|
}
|
|
it.prev = nil
|
|
it.next = c.head
|
|
c.head.prev = it
|
|
c.head = it
|
|
}
|
|
}
|
|
|
|
// removeTail removes the tail from cache.
|
|
func (c *LruCache) removeTail() {
|
|
delete(c.cache, c.tail.key)
|
|
|
|
c.tail.prev.next = nil
|
|
c.tail = c.tail.prev
|
|
}
|