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