go-vise

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

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 }