go-vise

Constrained Size Output Virtual Machine
Info | Log | Files | Refs | README | LICENSE

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 }