cache.go (5052B)
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 type Cache struct { 9 CacheSize uint32 // Total allowed cumulative size of values (not code) in cache 10 CacheUseSize uint32 // Currently used bytes by all values (not code) in cache 11 Cache []map[string]string // All loaded cache items 12 Sizes map[string]uint16 // Size limits for all loaded symbols. 13 } 14 15 // NewCache creates a new ready-to-use cache object 16 func NewCache() *Cache { 17 ca := &Cache{ 18 Cache: []map[string]string{make(map[string]string)}, 19 Sizes: make(map[string]uint16), 20 } 21 return ca 22 } 23 24 // WithCacheSize applies a cumulative cache size limitation for all cached items. 25 func(ca *Cache) WithCacheSize(cacheSize uint32) *Cache { 26 ca.CacheSize = cacheSize 27 return ca 28 } 29 30 // Add adds a cache value under a cache symbol key. 31 // 32 // Also stores the size limitation of for key for later updates. 33 // 34 // Fails if: 35 // - key already defined 36 // - value is longer than size limit 37 // - adding value exceeds cumulative cache capacity 38 func(ca *Cache) Add(key string, value string, sizeLimit uint16) error { 39 if sizeLimit > 0 { 40 l := uint16(len(value)) 41 if l > sizeLimit { 42 return fmt.Errorf("value length %v exceeds value size limit %v", l, sizeLimit) 43 } 44 } 45 checkFrame := ca.frameOf(key) 46 if checkFrame > -1 { 47 thisFrame := len(ca.Cache) - 1 48 if checkFrame == thisFrame { 49 Logg.Debugf("Ignoring load request on frame that has symbol already loaded") 50 return nil 51 } 52 return fmt.Errorf("key %v already defined in frame %v, this is frame %v", key, checkFrame, thisFrame) 53 } 54 var sz uint32 55 if len(value) > 0 { 56 sz = ca.checkCapacity(value) 57 if sz == 0 { 58 return fmt.Errorf("Cache capacity exceeded %v of %v", ca.CacheUseSize + sz, ca.CacheSize) 59 } 60 } 61 Logg.Infof("Cache add", "key", key, "size", sz, "limit", sizeLimit) 62 Logg.Tracef("", "Cache add data", value) 63 ca.Cache[len(ca.Cache)-1][key] = value 64 ca.CacheUseSize += sz 65 ca.Sizes[key] = sizeLimit 66 return nil 67 } 68 69 // ReservedSize returns the maximum byte size available for the given symbol. 70 func(ca *Cache) ReservedSize(key string) (uint16, error) { 71 v, ok := ca.Sizes[key] 72 if !ok { 73 return 0, fmt.Errorf("unknown symbol: %s", key) 74 } 75 return v, nil 76 } 77 78 // Update sets a new value for an existing key. 79 // 80 // Uses the size limitation from when the key was added. 81 // 82 // Fails if: 83 // - key not defined 84 // - value is longer than size limit 85 // - replacing value exceeds cumulative cache capacity 86 func(ca *Cache) Update(key string, value string) error { 87 sizeLimit := ca.Sizes[key] 88 if ca.Sizes[key] > 0 { 89 l := uint16(len(value)) 90 if l > sizeLimit { 91 return fmt.Errorf("update value length %v exceeds value size limit %v", l, sizeLimit) 92 } 93 } 94 checkFrame := ca.frameOf(key) 95 if checkFrame == -1 { 96 return fmt.Errorf("key %v not defined", key) 97 } 98 r := ca.Cache[checkFrame][key] 99 l := uint32(len(r)) 100 ca.Cache[checkFrame][key] = "" 101 ca.CacheUseSize -= l 102 sz := ca.checkCapacity(value) 103 if sz == 0 { 104 baseUseSize := ca.CacheUseSize 105 ca.Cache[checkFrame][key] = r 106 ca.CacheUseSize += l 107 return fmt.Errorf("Cache capacity exceeded %v of %v", baseUseSize + sz, ca.CacheSize) 108 } 109 ca.Cache[checkFrame][key] = value 110 ca.CacheUseSize += uint32(len(value)) 111 return nil 112 } 113 114 // Get the content currently loaded for a single key, loaded at any level. 115 // 116 // Fails if key has not been loaded. 117 func(ca *Cache) Get(key string) (string, error) { 118 i := ca.frameOf(key) 119 if i == -1 { 120 return "", fmt.Errorf("key '%s' not found in any frame", key) 121 } 122 r, ok := ca.Cache[i][key] 123 if !ok { 124 return "", fmt.Errorf("unknown key '%s'", key) 125 } 126 return r, nil 127 } 128 129 // Reset flushes all state contents below the top level. 130 func(ca *Cache) Reset() { 131 if len(ca.Cache) == 0 { 132 return 133 } 134 ca.Cache = ca.Cache[:1] 135 ca.CacheUseSize = 0 136 return 137 } 138 139 // Push adds a new level to the cache. 140 func (ca *Cache) Push() error { 141 m := make(map[string]string) 142 ca.Cache = append(ca.Cache, m) 143 return nil 144 } 145 146 // Pop frees the cache of the current level and makes the previous level the current level. 147 // 148 // Fails if already on top level. 149 func (ca *Cache) Pop() error { 150 l := len(ca.Cache) 151 if l == 0 { 152 return fmt.Errorf("already at top level") 153 } 154 l -= 1 155 m := ca.Cache[l] 156 for k, v := range m { 157 sz := len(v) 158 ca.CacheUseSize -= uint32(sz) 159 Logg.Debugf("Cache free", "frame", l, "key", k, "size", sz) 160 } 161 ca.Cache = ca.Cache[:l] 162 //ca.resetCurrent() 163 return nil 164 } 165 166 // Check returns true if a key already exists in the cache. 167 func(ca *Cache) Check(key string) bool { 168 return ca.frameOf(key) == -1 169 } 170 171 // bytes that will be added to cache use size for string 172 // returns 0 if capacity would be exceeded 173 func(ca *Cache) checkCapacity(v string) uint32 { 174 sz := uint32(len(v)) 175 if ca.CacheSize == 0 { 176 return sz 177 } 178 if ca.CacheUseSize + sz > ca.CacheSize { 179 return 0 180 } 181 return sz 182 } 183 184 // return 0-indexed frame number where key is defined. -1 if not defined 185 func(ca *Cache) frameOf(key string) int { 186 for i, m := range ca.Cache { 187 for k, _ := range m { 188 if k == key { 189 return i 190 } 191 } 192 } 193 return -1 194 }