candycache/candycache.go

220 lines
5.5 KiB
Go
Raw Normal View History

2025-01-06 00:14:48 +03:00
package candycache
2025-01-02 23:30:51 +03:00
import (
"errors"
"reflect"
2025-01-02 23:30:51 +03:00
"sync"
"time"
)
// Структура виде ключ-значение для возвращения списка элементов кэша с их ключами.
type KeyItemPair struct {
Key string
Item Item
}
2025-01-02 23:30:51 +03:00
// Элемент в кэше - это данные и время их жизни.
type Item struct {
destroyTimestamp int64 // Момент в Unix-секундах, когда элемент становится устаревшим
data interface{} // Данные
}
// Кэш - это хранилище элементов и инервал его очистки (ну и мьютекс на всякий случай).
// Интервал очистки хранилища укахывается в НАНОСЕКУНДАХ (используй множители для преобразования во что-то другое).
type Cache struct {
sync.RWMutex // Мьютекс ждя реализации безопасного доступа к общим данным
storage map[string]Item // Хранилище элементов
cleanupInterval time.Duration // Интервал очистки хранилища в наносекундах
}
2025-01-06 00:16:20 +03:00
// Создает новый экземпляр Cache с интервалом очистки cleanupInterval.
2025-01-02 23:30:51 +03:00
// Если cleanupInterval < 0, то кэш не будет очищаться автоматически.
2025-01-06 00:14:48 +03:00
func Cacher(cleanupInterval time.Duration) *Cache {
2025-01-02 23:30:51 +03:00
cache := &Cache{
storage: make(map[string]Item),
cleanupInterval: cleanupInterval,
}
// Запускаем Garbage Collector если интервал очистки больше 0
// Иначе (если он отрицательный) кэш будет жить до ручного вызова Cleanup
if cleanupInterval > 0 {
go cache.gc(cleanupInterval)
}
return cache
}
// gc = Garbage Collector.
func (c *Cache) gc(cleanupInterval time.Duration) {
ticker := time.NewTicker(cleanupInterval)
defer ticker.Stop()
for range ticker.C {
c.Cleanup()
}
}
// Перебирает все элементы в кэше, удаляет устаревшие.
func (c *Cache) Cleanup() {
c.Lock()
defer c.Unlock()
for key, item := range c.storage {
if item.destroyTimestamp <= time.Now().Unix() {
delete(c.storage, key)
}
}
}
// Удаление всех элементов из кэша.
func (c *Cache) Flush() {
c.Lock()
defer c.Unlock()
for key := range c.storage {
delete(c.storage, key)
}
}
// Получение элемента из кэша по ключу.
func (c *Cache) Get(key string) (interface{}, bool) {
c.RLock()
defer c.RUnlock()
item, found := c.storage[key]
// Элемент не найден в кэше
if !found {
return nil, false
}
return item.data, true
}
// Удаление элемента по ключу.
func (c *Cache) Delete(key string) error {
c.Lock()
defer c.Unlock()
if _, found := c.storage[key]; !found {
return errors.New("key not found")
}
delete(c.storage, key)
return nil
}
// Добавление элемента в кэш.
// key - ключ.
// data - данные.
// ttl - время жизни элемента (time to life) в наносекундах.
func (c *Cache) Add(key string, data interface{}, ttl time.Duration) {
c.Lock()
defer c.Unlock()
c.storage[key] = Item{
destroyTimestamp: time.Now().Unix() + int64(ttl.Seconds()),
data: data,
}
}
// Вернет количество элементов в кэше.
func (c *Cache) Count() int {
c.RLock()
defer c.RUnlock()
return len(c.storage)
}
// Печать всех элементов кэша (ключ и время уничтожения).
func (c *Cache) List() []KeyItemPair {
2025-01-02 23:30:51 +03:00
c.RLock()
defer c.RUnlock()
// Создаем срез для хранения пар ключ-значение
items := make([]KeyItemPair, 0, len(c.storage))
// Заполняем срез парами ключ-значение
2025-01-02 23:30:51 +03:00
for key, item := range c.storage {
items = append(items, KeyItemPair{Key: key, Item: item})
2025-01-02 23:30:51 +03:00
}
return items
}
// Вернет размер всего кэша в байтах.
func (c *Cache) Size() int {
c.RLock()
defer c.RUnlock()
size := 0
for key, item := range c.storage {
size += isize(key) + isize(item.data) + isize(item.destroyTimestamp)
}
return size
}
// ПОДДЕРЖИВАЕМЫЕ ТИПЫ:
// Bool +
// Int +
// Int8 +
// Int16 +
// Int32 +
// Int64 +
// Uint +
// Uint8 +
// Uint16 +
// Uint32 +
// Uint64 +
// Uintptr +
// Float32 +
// Float64 +
// Complex64 +
// Complex128 +
// Array +
// Func +
// Map +
// Slice +
// String +
// Struct
func isize(i interface{}) int {
if i == nil {
return 0
}
val := reflect.ValueOf(i)
kind := val.Kind()
size := 0
switch kind {
case reflect.Slice, reflect.Array, reflect.String:
len := val.Len()
for i := 0; i < len; i++ {
size += isize(val.Index(i).Interface())
}
return size
case reflect.Map:
for _, key := range val.MapKeys() {
size += isize(key.Interface()) + isize(val.MapIndex(key).Interface())
}
return size
case reflect.Struct:
for i := 0; i < val.NumField(); i++ {
size += isize(val.Field(i).Interface())
}
return size
default:
return int(reflect.TypeOf(i).Size())
}
}
// Возвращает данные элемента кэша.
func (i *Item) Data() interface{} {
return i.data
}
// Возвращает момент смерти элемента кэша.
func (i *Item) DestroyTimestamp() int64 {
return i.destroyTimestamp
2025-01-02 23:30:51 +03:00
}