go-vise

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

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 }