123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 |
- /*
- * Public Domain Software
- *
- * I (Matthias Ladkau) am the author of the source code in this file.
- * I have placed the source code in this file in the public domain.
- *
- * For further information see: http://creativecommons.org/publicdomain/zero/1.0/
- */
- package datautil
- import (
- "bytes"
- "fmt"
- "math"
- "sort"
- "sync"
- "time"
- )
- /*
- MapCache is a map based cache object storing string->interface{}. It is possible
- to specify a maximum size, which when reached causes the oldest entries to be
- removed. It is also possible to set an expiry time for values. Values which are
- old are purged on the next access to the object.
- */
- type MapCache struct {
- data map[string]interface{} // Data for the cache
- ts map[string]int64 // Timestamps for values
- size uint64 // Size of the cache
- maxsize uint64 // Max size of the cache
- maxage int64 // Max age of the cache
- mutex *sync.RWMutex // Mutex to protect atomic map operations
- }
- /*
- NewMapCache creates a new MapCache object. The calling function can specify
- the maximum size and the maximum age in seconds for entries. A value of 0
- means no size constraint and no age constraint.
- */
- func NewMapCache(maxsize uint64, maxage int64) *MapCache {
- return &MapCache{make(map[string]interface{}), make(map[string]int64),
- 0, maxsize, maxage, &sync.RWMutex{}}
- }
- /*
- Clear removes all entries.
- */
- func (mc *MapCache) Clear() {
- // Take writer lock
- mc.mutex.Lock()
- defer mc.mutex.Unlock()
- mc.data = make(map[string]interface{})
- mc.ts = make(map[string]int64)
- mc.size = 0
- }
- /*
- Size returns the current size of the MapCache.
- */
- func (mc *MapCache) Size() uint64 {
- return mc.size
- }
- /*
- Put stores an item in the MapCache.
- */
- func (mc *MapCache) Put(k string, v interface{}) {
- // Do cache maintenance
- oldest := mc.maintainCache()
- // Take writer lock
- mc.mutex.Lock()
- defer mc.mutex.Unlock()
- // Check if the entry is a new entry
- if _, exists := mc.data[k]; !exists {
- // If the list is full remove the oldest item otherwise increase the size
- if mc.maxsize != 0 && mc.size == mc.maxsize {
- delete(mc.data, oldest)
- delete(mc.ts, oldest)
- } else {
- mc.size++
- }
- }
- // Do the actual map operation
- mc.data[k] = v
- mc.ts[k] = time.Now().Unix()
- }
- /*
- Remove removes an item in the MapCache.
- */
- func (mc *MapCache) Remove(k string) bool {
- // Do cache maintenance
- mc.maintainCache()
- // Take writer lock
- mc.mutex.Lock()
- defer mc.mutex.Unlock()
- // Check if the entry exists
- _, exists := mc.data[k]
- if exists {
- // Do the actual map operation
- delete(mc.data, k)
- delete(mc.ts, k)
- mc.size--
- }
- return exists
- }
- /*
- Get retrieves an item from the MapCache.
- */
- func (mc *MapCache) Get(k string) (interface{}, bool) {
- // Do cache maintenance
- mc.maintainCache()
- // Take reader lock
- mc.mutex.RLock()
- defer mc.mutex.RUnlock()
- // Do the actual map operation
- v, ok := mc.data[k]
- return v, ok
- }
- /*
- GetAll retrieves all items from the MapCache.
- */
- func (mc *MapCache) GetAll() map[string]interface{} {
- // Do cache maintenance
- mc.maintainCache()
- // Take reader lock
- mc.mutex.RLock()
- defer mc.mutex.RUnlock()
- // Create return map
- cp := make(map[string]interface{})
- for k, v := range mc.data {
- cp[k] = v
- }
- return cp
- }
- /*
- String returns a string representation of this MapCache.
- */
- func (mc *MapCache) String() string {
- mc.mutex.RLock()
- defer mc.mutex.RUnlock()
- // Sort keys before printing the map
- var keys []string
- for k := range mc.data {
- keys = append(keys, k)
- }
- sort.Sort(sort.StringSlice(keys))
- buf := &bytes.Buffer{}
- for _, k := range keys {
- buf.WriteString(fmt.Sprint(k, ":", mc.data[k], "\n"))
- }
- return buf.String()
- }
- /*
- maintainCache removes expired items and returns the oldest entry.
- */
- func (mc *MapCache) maintainCache() string {
- mc.mutex.RLock()
- oldestTS := int64(math.MaxInt64)
- oldestK := ""
- now := time.Now().Unix()
- for k, v := range mc.ts {
- // Check if the entry has expired
- if mc.maxage != 0 && now-v > mc.maxage {
- // Remove entry if it has expired
- mc.mutex.RUnlock()
- mc.mutex.Lock()
- delete(mc.data, k)
- delete(mc.ts, k)
- mc.size--
- mc.mutex.Unlock()
- mc.mutex.RLock()
- }
- // Gather oldest entry
- if v < oldestTS {
- oldestTS = v
- oldestK = k
- }
- }
- mc.mutex.RUnlock()
- return oldestK
- }
|