cache.go (5478B)
1 package cache 2 3 import ( 4 "fmt" 5 ) 6 7 // Cache stores loaded content, enforcing size limits and keeping track of size usage. 8 // 9 // TODO: hide values from client, while allowing cbor serialization 10 type Cache struct { 11 // Total allowed cumulative size of values (not code) in cache 12 CacheSize uint32 13 // Currently used bytes by all values (not code) in cache 14 CacheUseSize uint32 15 // All loaded cache items 16 Cache []map[string]string 17 // Size limits for all loaded symbols. 18 Sizes map[string]uint16 19 // Last inserted value (regardless of scope) 20 LastValue string 21 invalid bool 22 } 23 24 // NewCache creates a new ready-to-use Cache object 25 func NewCache() *Cache { 26 ca := &Cache{} 27 ca.ResetFull() 28 return ca 29 } 30 31 // Invalidate implements the Memory interface. 32 func (ca *Cache) Invalidate() { 33 ca.invalid = true 34 } 35 36 // Invalid implements the Memory interface. 37 func (ca *Cache) Invalid() bool { 38 return ca.invalid 39 } 40 41 // WithCacheSize is a chainable method that applies a cumulative cache size limitation for all cached items. 42 func (ca *Cache) WithCacheSize(cacheSize uint32) *Cache { 43 ca.CacheSize = cacheSize 44 return ca 45 } 46 47 // Add implements the Memory interface. 48 func (ca *Cache) Add(key string, value string, sizeLimit uint16) error { 49 if sizeLimit > 0 { 50 l := uint16(len(value)) 51 if l > sizeLimit { 52 return fmt.Errorf("value length %v exceeds value size limit %v", l, sizeLimit) 53 } 54 } 55 checkFrame := ca.frameOf(key) 56 if checkFrame > -1 { 57 thisFrame := len(ca.Cache) - 1 58 if checkFrame == thisFrame { 59 return ErrDup 60 } 61 return fmt.Errorf("key %v already defined in frame %v, this is frame %v", key, checkFrame, thisFrame) 62 } 63 var sz uint32 64 if len(value) > 0 { 65 sz = ca.checkCapacity(value) 66 if sz == 0 { 67 return fmt.Errorf("Cache capacity exceeded %v of %v", ca.CacheUseSize+sz, ca.CacheSize) 68 } 69 } 70 logg.Debugf("Cache add", "key", key, "size", sz, "limit", sizeLimit) 71 logg.Tracef("Cache add data", "value", value) 72 ca.Cache[len(ca.Cache)-1][key] = value 73 ca.CacheUseSize += sz 74 ca.Sizes[key] = sizeLimit 75 ca.LastValue = value 76 return nil 77 } 78 79 // ReservedSize implements the Memory interface. 80 func (ca *Cache) ReservedSize(key string) (uint16, error) { 81 v, ok := ca.Sizes[key] 82 if !ok { 83 return 0, fmt.Errorf("unknown symbol: %s", key) 84 } 85 return v, nil 86 } 87 88 // Update implements the Memory interface. 89 func (ca *Cache) Update(key string, value string) error { 90 sizeLimit := ca.Sizes[key] 91 if ca.Sizes[key] > 0 { 92 l := uint16(len(value)) 93 if l > sizeLimit { 94 return fmt.Errorf("update value length %v exceeds value size limit %v", l, sizeLimit) 95 } 96 } 97 checkFrame := ca.frameOf(key) 98 if checkFrame == -1 { 99 return fmt.Errorf("key %v not defined", key) 100 } 101 r := ca.Cache[checkFrame][key] 102 l := uint32(len(r)) 103 ca.Cache[checkFrame][key] = "" 104 ca.CacheUseSize -= l 105 sz := ca.checkCapacity(value) 106 if sz == 0 { 107 baseUseSize := ca.CacheUseSize 108 ca.Cache[checkFrame][key] = r 109 ca.CacheUseSize += l 110 return fmt.Errorf("Cache capacity exceeded %v of %v", baseUseSize+sz, ca.CacheSize) 111 } 112 ca.Cache[checkFrame][key] = value 113 ca.CacheUseSize += uint32(len(value)) 114 return nil 115 } 116 117 // Get implements the Memory interface. 118 func (ca *Cache) Get(key string) (string, error) { 119 i := ca.frameOf(key) 120 if i == -1 { 121 return "", fmt.Errorf("key '%s' not found in any frame", key) 122 } 123 r, ok := ca.Cache[i][key] 124 if !ok { 125 return "", fmt.Errorf("unknown key '%s'", key) 126 } 127 return r, nil 128 } 129 130 // Reset implements the Memory interface. 131 func (ca *Cache) ResetFull() { 132 ca.Cache = []map[string]string{make(map[string]string)} 133 ca.Sizes = make(map[string]uint16) 134 ca.CacheUseSize = 0 135 return 136 } 137 138 // Reset implements the Memory interface. 139 func (ca *Cache) Reset() { 140 var v string 141 if len(ca.Cache) == 0 { 142 return 143 } 144 ca.Cache = ca.Cache[:1] 145 ca.CacheUseSize = 0 146 for _, v = range ca.Cache[0] { 147 ca.CacheUseSize += uint32(len(v)) 148 } 149 return 150 } 151 152 // Push implements the Memory interface. 153 func (ca *Cache) Push() error { 154 m := make(map[string]string) 155 ca.Cache = append(ca.Cache, m) 156 return nil 157 } 158 159 // Pop implements the Memory interface. 160 func (ca *Cache) Pop() error { 161 l := len(ca.Cache) 162 if l == 0 { 163 return fmt.Errorf("already at top level") 164 } 165 l -= 1 166 m := ca.Cache[l] 167 for k, v := range m { 168 sz := len(v) 169 ca.CacheUseSize -= uint32(sz) 170 delete(ca.Sizes, k) 171 logg.Debugf("Cache free", "frame", l, "key", k, "size", sz) 172 } 173 ca.Cache = ca.Cache[:l] 174 if l == 0 { 175 ca.Push() 176 } 177 return nil 178 } 179 180 // Check returns true if a key already exists in the cache. 181 func (ca *Cache) Check(key string) bool { 182 return ca.frameOf(key) == -1 183 } 184 185 // Last implements the Memory interface. 186 // 187 // TODO: needs to be invalidated when out of scope 188 func (ca *Cache) Last() string { 189 s := ca.LastValue 190 ca.LastValue = "" 191 return s 192 } 193 194 // bytes that will be added to cache use size for string 195 // returns 0 if capacity would be exceeded 196 func (ca *Cache) checkCapacity(v string) uint32 { 197 sz := uint32(len(v)) 198 if ca.CacheSize == 0 { 199 return sz 200 } 201 if ca.CacheUseSize+sz > ca.CacheSize { 202 return 0 203 } 204 return sz 205 } 206 207 // return 0-indexed frame number where key is defined. -1 if not defined 208 func (ca *Cache) frameOf(key string) int { 209 for i, m := range ca.Cache { 210 for k, _ := range m { 211 if k == key { 212 return i 213 } 214 } 215 } 216 return -1 217 } 218 219 // Levels implements the Memory interface. 220 func (ca *Cache) Levels() uint32 { 221 return uint32(len(ca.Cache)) 222 } 223 224 // Keys implements the Memory interface. 225 func (ca *Cache) Keys(level uint32) []string { 226 var r []string 227 for k := range ca.Cache[level] { 228 r = append(r, k) 229 } 230 return r 231 }