mapcache.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. /*
  2. * Public Domain Software
  3. *
  4. * I (Matthias Ladkau) am the author of the source code in this file.
  5. * I have placed the source code in this file in the public domain.
  6. *
  7. * For further information see: http://creativecommons.org/publicdomain/zero/1.0/
  8. */
  9. package datautil
  10. import (
  11. "bytes"
  12. "fmt"
  13. "math"
  14. "sort"
  15. "sync"
  16. "time"
  17. )
  18. /*
  19. MapCache is a map based cache object storing string->interface{}. It is possible
  20. to specify a maximum size, which when reached causes the oldest entries to be
  21. removed. It is also possible to set an expiry time for values. Values which are
  22. old are purged on the next access to the object.
  23. */
  24. type MapCache struct {
  25. data map[string]interface{} // Data for the cache
  26. ts map[string]int64 // Timestamps for values
  27. size uint64 // Size of the cache
  28. maxsize uint64 // Max size of the cache
  29. maxage int64 // Max age of the cache
  30. mutex *sync.RWMutex // Mutex to protect atomic map operations
  31. }
  32. /*
  33. NewMapCache creates a new MapCache object. The calling function can specify
  34. the maximum size and the maximum age in seconds for entries. A value of 0
  35. means no size constraint and no age constraint.
  36. */
  37. func NewMapCache(maxsize uint64, maxage int64) *MapCache {
  38. return &MapCache{make(map[string]interface{}), make(map[string]int64),
  39. 0, maxsize, maxage, &sync.RWMutex{}}
  40. }
  41. /*
  42. Clear removes all entries.
  43. */
  44. func (mc *MapCache) Clear() {
  45. // Take writer lock
  46. mc.mutex.Lock()
  47. defer mc.mutex.Unlock()
  48. mc.data = make(map[string]interface{})
  49. mc.ts = make(map[string]int64)
  50. mc.size = 0
  51. }
  52. /*
  53. Size returns the current size of the MapCache.
  54. */
  55. func (mc *MapCache) Size() uint64 {
  56. return mc.size
  57. }
  58. /*
  59. Put stores an item in the MapCache.
  60. */
  61. func (mc *MapCache) Put(k string, v interface{}) {
  62. // Do cache maintenance
  63. oldest := mc.maintainCache()
  64. // Take writer lock
  65. mc.mutex.Lock()
  66. defer mc.mutex.Unlock()
  67. // Check if the entry is a new entry
  68. if _, exists := mc.data[k]; !exists {
  69. // If the list is full remove the oldest item otherwise increase the size
  70. if mc.maxsize != 0 && mc.size == mc.maxsize {
  71. delete(mc.data, oldest)
  72. delete(mc.ts, oldest)
  73. } else {
  74. mc.size++
  75. }
  76. }
  77. // Do the actual map operation
  78. mc.data[k] = v
  79. mc.ts[k] = time.Now().Unix()
  80. }
  81. /*
  82. Remove removes an item in the MapCache.
  83. */
  84. func (mc *MapCache) Remove(k string) bool {
  85. // Do cache maintenance
  86. mc.maintainCache()
  87. // Take writer lock
  88. mc.mutex.Lock()
  89. defer mc.mutex.Unlock()
  90. // Check if the entry exists
  91. _, exists := mc.data[k]
  92. if exists {
  93. // Do the actual map operation
  94. delete(mc.data, k)
  95. delete(mc.ts, k)
  96. mc.size--
  97. }
  98. return exists
  99. }
  100. /*
  101. Get retrieves an item from the MapCache.
  102. */
  103. func (mc *MapCache) Get(k string) (interface{}, bool) {
  104. // Do cache maintenance
  105. mc.maintainCache()
  106. // Take reader lock
  107. mc.mutex.RLock()
  108. defer mc.mutex.RUnlock()
  109. // Do the actual map operation
  110. v, ok := mc.data[k]
  111. return v, ok
  112. }
  113. /*
  114. GetAll retrieves all items from the MapCache.
  115. */
  116. func (mc *MapCache) GetAll() map[string]interface{} {
  117. // Do cache maintenance
  118. mc.maintainCache()
  119. // Take reader lock
  120. mc.mutex.RLock()
  121. defer mc.mutex.RUnlock()
  122. // Create return map
  123. cp := make(map[string]interface{})
  124. for k, v := range mc.data {
  125. cp[k] = v
  126. }
  127. return cp
  128. }
  129. /*
  130. String returns a string representation of this MapCache.
  131. */
  132. func (mc *MapCache) String() string {
  133. mc.mutex.RLock()
  134. defer mc.mutex.RUnlock()
  135. // Sort keys before printing the map
  136. var keys []string
  137. for k := range mc.data {
  138. keys = append(keys, k)
  139. }
  140. sort.Sort(sort.StringSlice(keys))
  141. buf := &bytes.Buffer{}
  142. for _, k := range keys {
  143. buf.WriteString(fmt.Sprint(k, ":", mc.data[k], "\n"))
  144. }
  145. return buf.String()
  146. }
  147. /*
  148. maintainCache removes expired items and returns the oldest entry.
  149. */
  150. func (mc *MapCache) maintainCache() string {
  151. mc.mutex.RLock()
  152. oldestTS := int64(math.MaxInt64)
  153. oldestK := ""
  154. now := time.Now().Unix()
  155. for k, v := range mc.ts {
  156. // Check if the entry has expired
  157. if mc.maxage != 0 && now-v > mc.maxage {
  158. // Remove entry if it has expired
  159. mc.mutex.RUnlock()
  160. mc.mutex.Lock()
  161. delete(mc.data, k)
  162. delete(mc.ts, k)
  163. mc.size--
  164. mc.mutex.Unlock()
  165. mc.mutex.RLock()
  166. }
  167. // Gather oldest entry
  168. if v < oldestTS {
  169. oldestTS = v
  170. oldestK = k
  171. }
  172. }
  173. mc.mutex.RUnlock()
  174. return oldestK
  175. }