WIP factor out cache code
This commit is contained in:
parent
238f4546ff
commit
629cae8a55
257
go/cache/cache.go
vendored
Normal file
257
go/cache/cache.go
vendored
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Cache struct {
|
||||||
|
CacheSize uint32 // Total allowed cumulative size of values (not code) in cache
|
||||||
|
CacheUseSize uint32 // Currently used bytes by all values (not code) in cache
|
||||||
|
Cache []map[string]string // All loaded cache items
|
||||||
|
CacheMap map[string]string // Mapped
|
||||||
|
menuSize uint16 // Max size of menu
|
||||||
|
outputSize uint32 // Max size of output
|
||||||
|
sizes map[string]uint16 // Size limits for all loaded symbols.
|
||||||
|
sink *string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCache creates a new ready-to-use cache object
|
||||||
|
func NewCache() *Cache {
|
||||||
|
ca := &Cache{
|
||||||
|
Cache: []map[string]string{make(map[string]string)},
|
||||||
|
sizes: make(map[string]uint16),
|
||||||
|
}
|
||||||
|
ca.resetCurrent()
|
||||||
|
return ca
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithCacheSize applies a cumulative cache size limitation for all cached items.
|
||||||
|
func(ca *Cache) WithCacheSize(cacheSize uint32) *Cache {
|
||||||
|
ca.CacheSize = cacheSize
|
||||||
|
return ca
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithCacheSize applies a cumulative cache size limitation for all cached items.
|
||||||
|
func(ca *Cache) WithOutputSize(outputSize uint32) *Cache {
|
||||||
|
ca.outputSize = outputSize
|
||||||
|
return ca
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds a cache value under a cache symbol key.
|
||||||
|
//
|
||||||
|
// Also stores the size limitation of for key for later updates.
|
||||||
|
//
|
||||||
|
// Fails if:
|
||||||
|
// - key already defined
|
||||||
|
// - value is longer than size limit
|
||||||
|
// - adding value exceeds cumulative cache capacity
|
||||||
|
func(ca *Cache) Add(key string, value string, sizeLimit uint16) error {
|
||||||
|
if sizeLimit > 0 {
|
||||||
|
l := uint16(len(value))
|
||||||
|
if l > sizeLimit {
|
||||||
|
return fmt.Errorf("value length %v exceeds value size limit %v", l, sizeLimit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkFrame := ca.frameOf(key)
|
||||||
|
if checkFrame > -1 {
|
||||||
|
if checkFrame == len(ca.Cache) - 1 {
|
||||||
|
log.Printf("Ignoring load request on frame that has symbol already loaded")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("key %v already defined in frame %v", key, checkFrame)
|
||||||
|
}
|
||||||
|
sz := ca.checkCapacity(value)
|
||||||
|
if sz == 0 {
|
||||||
|
return fmt.Errorf("Cache capacity exceeded %v of %v", ca.CacheUseSize + sz, ca.CacheSize)
|
||||||
|
}
|
||||||
|
log.Printf("add key %s value size %v limit %v", key, sz, sizeLimit)
|
||||||
|
ca.Cache[len(ca.Cache)-1][key] = value
|
||||||
|
ca.CacheUseSize += sz
|
||||||
|
ca.sizes[key] = sizeLimit
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update sets a new value for an existing key.
|
||||||
|
//
|
||||||
|
// Uses the size limitation from when the key was added.
|
||||||
|
//
|
||||||
|
// Fails if:
|
||||||
|
// - key not defined
|
||||||
|
// - value is longer than size limit
|
||||||
|
// - replacing value exceeds cumulative cache capacity
|
||||||
|
func(ca *Cache) Update(key string, value string) error {
|
||||||
|
sizeLimit := ca.sizes[key]
|
||||||
|
if ca.sizes[key] > 0 {
|
||||||
|
l := uint16(len(value))
|
||||||
|
if l > sizeLimit {
|
||||||
|
return fmt.Errorf("update value length %v exceeds value size limit %v", l, sizeLimit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkFrame := ca.frameOf(key)
|
||||||
|
if checkFrame == -1 {
|
||||||
|
return fmt.Errorf("key %v not defined", key)
|
||||||
|
}
|
||||||
|
r := ca.Cache[checkFrame][key]
|
||||||
|
l := uint32(len(r))
|
||||||
|
ca.Cache[checkFrame][key] = ""
|
||||||
|
if ca.CacheMap[key] != "" {
|
||||||
|
ca.CacheMap[key] = value
|
||||||
|
}
|
||||||
|
ca.CacheUseSize -= l
|
||||||
|
sz := ca.checkCapacity(value)
|
||||||
|
if sz == 0 {
|
||||||
|
baseUseSize := ca.CacheUseSize
|
||||||
|
ca.Cache[checkFrame][key] = r
|
||||||
|
ca.CacheUseSize += l
|
||||||
|
return fmt.Errorf("Cache capacity exceeded %v of %v", baseUseSize + sz, ca.CacheSize)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the full key-value mapping for all mapped keys at the current cache level.
|
||||||
|
func(ca *Cache) Get() (map[string]string, error) {
|
||||||
|
if len(ca.Cache) == 0 {
|
||||||
|
return nil, fmt.Errorf("get at top frame")
|
||||||
|
}
|
||||||
|
return ca.Cache[len(ca.Cache)-1], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func(ca *Cache) Sizes() (map[string]uint16, error) {
|
||||||
|
if len(ca.Cache) == 0 {
|
||||||
|
return nil, fmt.Errorf("get at top frame")
|
||||||
|
}
|
||||||
|
sizes := make(map[string]uint16)
|
||||||
|
var haveSink bool
|
||||||
|
for k, _ := range ca.CacheMap {
|
||||||
|
l, ok := ca.sizes[k]
|
||||||
|
if !ok {
|
||||||
|
panic(fmt.Sprintf("missing size for %v", k))
|
||||||
|
}
|
||||||
|
if l == 0 {
|
||||||
|
if haveSink {
|
||||||
|
panic(fmt.Sprintf("duplicate sink for %v", k))
|
||||||
|
}
|
||||||
|
haveSink = true
|
||||||
|
}
|
||||||
|
sizes[k] = l
|
||||||
|
}
|
||||||
|
return sizes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map marks the given key for retrieval.
|
||||||
|
//
|
||||||
|
// After this, Val() will return the value for the key, and Size() will include the value size and limitations in its calculations.
|
||||||
|
//
|
||||||
|
// Only one symbol with no size limitation may be mapped at the current level.
|
||||||
|
func(ca *Cache) Map(key string) error {
|
||||||
|
m, err := ca.Get()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
l := ca.sizes[key]
|
||||||
|
if l == 0 {
|
||||||
|
if ca.sink != nil {
|
||||||
|
return fmt.Errorf("sink already set to symbol '%v'", *ca.sink)
|
||||||
|
}
|
||||||
|
ca.sink = &key
|
||||||
|
}
|
||||||
|
ca.CacheMap[key] = m[key]
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fails if key is not mapped.
|
||||||
|
func(ca *Cache) Val(key string) (string, error) {
|
||||||
|
r := ca.CacheMap[key]
|
||||||
|
if len(r) == 0 {
|
||||||
|
return "", fmt.Errorf("key %v not mapped", key)
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset flushes all state contents below the top level.
|
||||||
|
func(ca *Cache) Reset() {
|
||||||
|
if len(ca.Cache) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ca.Cache = ca.Cache[:1]
|
||||||
|
ca.CacheUseSize = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size returns size used by values and menu, and remaining size available
|
||||||
|
func(ca *Cache) Usage() (uint32, uint32) {
|
||||||
|
var l int
|
||||||
|
var c uint16
|
||||||
|
for k, v := range ca.CacheMap {
|
||||||
|
l += len(v)
|
||||||
|
c += ca.sizes[k]
|
||||||
|
}
|
||||||
|
r := uint32(l)
|
||||||
|
r += uint32(ca.menuSize)
|
||||||
|
return r, uint32(c)-r
|
||||||
|
}
|
||||||
|
|
||||||
|
// return 0-indexed frame number where key is defined. -1 if not defined
|
||||||
|
func(ca *Cache) frameOf(key string) int {
|
||||||
|
for i, m := range ca.Cache {
|
||||||
|
for k, _ := range m {
|
||||||
|
if k == key {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push adds a new level to the cache.
|
||||||
|
func (ca *Cache) Push() error {
|
||||||
|
m := make(map[string]string)
|
||||||
|
ca.Cache = append(ca.Cache, m)
|
||||||
|
ca.resetCurrent()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pop frees the cache of the current level and makes the previous level the current level.
|
||||||
|
//
|
||||||
|
// Fails if already on top level.
|
||||||
|
func (ca *Cache) Pop() error {
|
||||||
|
l := len(ca.Cache)
|
||||||
|
if l == 0 {
|
||||||
|
return fmt.Errorf("already at top level")
|
||||||
|
}
|
||||||
|
l -= 1
|
||||||
|
m := ca.Cache[l]
|
||||||
|
for k, v := range m {
|
||||||
|
sz := len(v)
|
||||||
|
ca.CacheUseSize -= uint32(sz)
|
||||||
|
log.Printf("free frame %v key %v value size %v", l, k, sz)
|
||||||
|
}
|
||||||
|
ca.Cache = ca.Cache[:l]
|
||||||
|
ca.resetCurrent()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check returns true if a key already exists in the cache.
|
||||||
|
func(ca *Cache) Check(key string) bool {
|
||||||
|
return ca.frameOf(key) == -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// flush relveant properties for level change
|
||||||
|
func(ca *Cache) resetCurrent() {
|
||||||
|
ca.sink = nil
|
||||||
|
ca.CacheMap = make(map[string]string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// bytes that will be added to cache use size for string
|
||||||
|
// returns 0 if capacity would be exceeded
|
||||||
|
func(ca *Cache) checkCapacity(v string) uint32 {
|
||||||
|
sz := uint32(len(v))
|
||||||
|
if ca.CacheSize == 0 {
|
||||||
|
return sz
|
||||||
|
}
|
||||||
|
if ca.CacheUseSize + sz > ca.CacheSize {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return sz
|
||||||
|
}
|
186
go/cache/cache_test.go
vendored
Normal file
186
go/cache/cache_test.go
vendored
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func TestNewCache(t *testing.T) {
|
||||||
|
ca := NewCache()
|
||||||
|
if ca.CacheSize != 0 {
|
||||||
|
t.Errorf("cache size not 0")
|
||||||
|
}
|
||||||
|
ca = ca.WithCacheSize(102525)
|
||||||
|
if ca.CacheSize != 102525 {
|
||||||
|
t.Errorf("cache size not 102525")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStateCacheUse(t *testing.T) {
|
||||||
|
ca := NewCache()
|
||||||
|
ca = ca.WithCacheSize(10)
|
||||||
|
ca.Push()
|
||||||
|
err := ca.Add("bar", "baz", 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
err = ca.Add("inky", "pinky", 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
err = ca.Add("blinky", "clyde", 0)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected capacity error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func TestStateDownUp(t *testing.T) {
|
||||||
|
ca := NewCache()
|
||||||
|
err := ca.Push()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
err = ca.Add("foo", "bar", 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
err = ca.Add("baz", "xyzzy", 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if ca.CacheUseSize != 8 {
|
||||||
|
t.Errorf("expected cache use size 8 got %v", ca.CacheUseSize)
|
||||||
|
}
|
||||||
|
err = ca.Pop()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
err = ca.Pop()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
err = ca.Pop()
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected out of top frame error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCacheReset(t *testing.T) {
|
||||||
|
ca := NewCache()
|
||||||
|
err := ca.Add("foo", "bar", 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
err = ca.Add("baz", "xyzzy", 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
ca.Reset()
|
||||||
|
if ca.CacheUseSize != 0 {
|
||||||
|
t.Errorf("expected cache use size 0, got %v", ca.CacheUseSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCacheLoadDup(t *testing.T) {
|
||||||
|
ca := NewCache()
|
||||||
|
err := ca.Push()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
err = ca.Add("foo", "xyzzy", 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
err = ca.Push()
|
||||||
|
err = ca.Add("foo", "baz", 0)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected fail on duplicate load")
|
||||||
|
}
|
||||||
|
ca.Pop()
|
||||||
|
err = ca.Add("foo", "baz", 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCacheCurrentSize(t *testing.T) {
|
||||||
|
ca := NewCache()
|
||||||
|
err := ca.Push()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
err = ca.Add("foo", "inky", 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
err = ca.Push()
|
||||||
|
err = ca.Add("bar", "pinky", 10)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
err = ca.Map("bar")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
err = ca.Add("baz", "tinkywinkydipsylalapoo", 51)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
err = ca.Map("baz")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
l, c := ca.Usage()
|
||||||
|
if l != 27 {
|
||||||
|
t.Errorf("expected actual length 27, got %v", l)
|
||||||
|
}
|
||||||
|
if c != 34 {
|
||||||
|
t.Errorf("expected remaining length 34, got %v", c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStateMapSink(t *testing.T) {
|
||||||
|
ca := NewCache()
|
||||||
|
ca.Push()
|
||||||
|
err := ca.Add("foo", "bar", 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
ca.Push()
|
||||||
|
err = ca.Add("bar", "xyzzy", 6)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
err = ca.Add("baz", "bazbaz", 18)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
err = ca.Add("xyzzy", "plugh", 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
err = ca.Map("foo")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
err = ca.Map("xyzzy")
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected fail on duplicate sink")
|
||||||
|
}
|
||||||
|
err = ca.Map("baz")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
ca.Push()
|
||||||
|
err = ca.Map("foo")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
ca.Pop()
|
||||||
|
err = ca.Map("foo")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
@ -20,26 +20,17 @@ import (
|
|||||||
// Symbol keys do not count towards cache size limitations.
|
// Symbol keys do not count towards cache size limitations.
|
||||||
//
|
//
|
||||||
// 8 first flags are reserved.
|
// 8 first flags are reserved.
|
||||||
//
|
|
||||||
// TODO factor out cache
|
|
||||||
type State struct {
|
type State struct {
|
||||||
Flags []byte // Error state
|
Flags []byte // Error state
|
||||||
CacheSize uint32 // Total allowed cumulative size of values (not code) in cache
|
|
||||||
CacheUseSize uint32 // Currently used bytes by all values (not code) in cache
|
|
||||||
Cache []map[string]string // All loaded cache items
|
|
||||||
CacheMap map[string]string // Mapped
|
|
||||||
menuSize uint16 // Max size of menu
|
|
||||||
outputSize uint32 // Max size of output
|
|
||||||
input []byte // Last input
|
input []byte // Last input
|
||||||
code []byte // Pending bytecode to execute
|
code []byte // Pending bytecode to execute
|
||||||
execPath []string // Command symbols stack
|
execPath []string // Command symbols stack
|
||||||
arg *string // Optional argument. Nil if not set.
|
arg *string // Optional argument. Nil if not set.
|
||||||
sizes map[string]uint16 // Size limits for all loaded symbols.
|
|
||||||
bitSize uint32 // size of (32-bit capacity) bit flag byte array
|
bitSize uint32 // size of (32-bit capacity) bit flag byte array
|
||||||
sink *string
|
|
||||||
sizeIdx uint16
|
sizeIdx uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// number of bytes necessary to represent a bitfield of the given size.
|
||||||
func toByteSize(bitSize uint32) uint8 {
|
func toByteSize(bitSize uint32) uint8 {
|
||||||
if bitSize == 0 {
|
if bitSize == 0 {
|
||||||
return 0
|
return 0
|
||||||
@ -62,8 +53,6 @@ func getFlag(bitIndex uint32, bitField []byte) bool {
|
|||||||
// NewState creates a new State object with bitSize number of error condition states in ADDITION to the 8 builtin flags.
|
// NewState creates a new State object with bitSize number of error condition states in ADDITION to the 8 builtin flags.
|
||||||
func NewState(bitSize uint32) State {
|
func NewState(bitSize uint32) State {
|
||||||
st := State{
|
st := State{
|
||||||
CacheSize: 0,
|
|
||||||
CacheUseSize: 0,
|
|
||||||
bitSize: bitSize + 8,
|
bitSize: bitSize + 8,
|
||||||
}
|
}
|
||||||
byteSize := toByteSize(bitSize + 8)
|
byteSize := toByteSize(bitSize + 8)
|
||||||
@ -72,7 +61,6 @@ func NewState(bitSize uint32) State {
|
|||||||
} else {
|
} else {
|
||||||
st.Flags = []byte{}
|
st.Flags = []byte{}
|
||||||
}
|
}
|
||||||
//st.Down("")
|
|
||||||
return st
|
return st
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,18 +179,6 @@ func(st *State) GetIndex(flags []byte) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithCacheSize applies a cumulative cache size limitation for all cached items.
|
|
||||||
func(st State) WithCacheSize(cacheSize uint32) State {
|
|
||||||
st.CacheSize = cacheSize
|
|
||||||
return st
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithCacheSize applies a cumulative cache size limitation for all cached items.
|
|
||||||
func(st State) WithOutputSize(outputSize uint32) State {
|
|
||||||
st.outputSize = outputSize
|
|
||||||
return st
|
|
||||||
}
|
|
||||||
|
|
||||||
// Where returns the current active rendering symbol.
|
// Where returns the current active rendering symbol.
|
||||||
func(st *State) Where() (string, uint16) {
|
func(st *State) Where() (string, uint16) {
|
||||||
if len(st.execPath) == 0 {
|
if len(st.execPath) == 0 {
|
||||||
@ -267,12 +243,10 @@ func(st *State) Top() (bool, error) {
|
|||||||
// Down adds the given symbol to the command stack.
|
// Down adds the given symbol to the command stack.
|
||||||
//
|
//
|
||||||
// Clears mapping and sink.
|
// Clears mapping and sink.
|
||||||
func(st *State) Down(input string) {
|
func(st *State) Down(input string) error {
|
||||||
m := make(map[string]string)
|
|
||||||
st.Cache = append(st.Cache, m)
|
|
||||||
st.sizes = make(map[string]uint16)
|
|
||||||
st.execPath = append(st.execPath, input)
|
st.execPath = append(st.execPath, input)
|
||||||
st.resetCurrent()
|
st.sizeIdx = 0
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Up removes the latest symbol to the command stack, and make the previous symbol current.
|
// Up removes the latest symbol to the command stack, and make the previous symbol current.
|
||||||
@ -283,206 +257,38 @@ func(st *State) Down(input string) {
|
|||||||
//
|
//
|
||||||
// Fails if called at top frame.
|
// Fails if called at top frame.
|
||||||
func(st *State) Up() (string, error) {
|
func(st *State) Up() (string, error) {
|
||||||
if len(st.execPath) == 0 {
|
l := len(st.execPath)
|
||||||
|
if l == 0 {
|
||||||
return "", fmt.Errorf("exit called beyond top frame")
|
return "", fmt.Errorf("exit called beyond top frame")
|
||||||
}
|
}
|
||||||
l := len(st.Cache)
|
st.execPath = st.execPath[:l-1]
|
||||||
l -= 1
|
|
||||||
m := st.Cache[l]
|
|
||||||
for k, v := range m {
|
|
||||||
sz := len(v)
|
|
||||||
st.CacheUseSize -= uint32(sz)
|
|
||||||
log.Printf("free frame %v key %v value size %v", l, k, sz)
|
|
||||||
}
|
|
||||||
st.Cache = st.Cache[:l]
|
|
||||||
st.execPath = st.execPath[:l]
|
|
||||||
sym := ""
|
sym := ""
|
||||||
if len(st.execPath) > 0 {
|
if len(st.execPath) > 0 {
|
||||||
sym = st.execPath[len(st.execPath)-1]
|
sym = st.execPath[len(st.execPath)-1]
|
||||||
}
|
}
|
||||||
st.resetCurrent()
|
st.sizeIdx = 0
|
||||||
return sym, nil
|
return sym, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add adds a cache value under a cache symbol key.
|
|
||||||
//
|
|
||||||
// Also stores the size limitation of for key for later updates.
|
|
||||||
//
|
|
||||||
// Fails if:
|
|
||||||
// - key already defined
|
|
||||||
// - value is longer than size limit
|
|
||||||
// - adding value exceeds cumulative cache capacity
|
|
||||||
func(st *State) Add(key string, value string, sizeLimit uint16) error {
|
|
||||||
if sizeLimit > 0 {
|
|
||||||
l := uint16(len(value))
|
|
||||||
if l > sizeLimit {
|
|
||||||
return fmt.Errorf("value length %v exceeds value size limit %v", l, sizeLimit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
checkFrame := st.frameOf(key)
|
|
||||||
if checkFrame > -1 {
|
|
||||||
if checkFrame == len(st.execPath) - 1 {
|
|
||||||
log.Printf("Ignoring load request on frame that has symbol already loaded")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return fmt.Errorf("key %v already defined in frame %v", key, checkFrame)
|
|
||||||
}
|
|
||||||
sz := st.checkCapacity(value)
|
|
||||||
if sz == 0 {
|
|
||||||
return fmt.Errorf("Cache capacity exceeded %v of %v", st.CacheUseSize + sz, st.CacheSize)
|
|
||||||
}
|
|
||||||
log.Printf("add key %s value size %v limit %v", key, sz, sizeLimit)
|
|
||||||
st.Cache[len(st.Cache)-1][key] = value
|
|
||||||
st.CacheUseSize += sz
|
|
||||||
st.sizes[key] = sizeLimit
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update sets a new value for an existing key.
|
|
||||||
//
|
|
||||||
// Uses the size limitation from when the key was added.
|
|
||||||
//
|
|
||||||
// Fails if:
|
|
||||||
// - key not defined
|
|
||||||
// - value is longer than size limit
|
|
||||||
// - replacing value exceeds cumulative cache capacity
|
|
||||||
func(st *State) Update(key string, value string) error {
|
|
||||||
sizeLimit := st.sizes[key]
|
|
||||||
if st.sizes[key] > 0 {
|
|
||||||
l := uint16(len(value))
|
|
||||||
if l > sizeLimit {
|
|
||||||
return fmt.Errorf("update value length %v exceeds value size limit %v", l, sizeLimit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
checkFrame := st.frameOf(key)
|
|
||||||
if checkFrame == -1 {
|
|
||||||
return fmt.Errorf("key %v not defined", key)
|
|
||||||
}
|
|
||||||
r := st.Cache[checkFrame][key]
|
|
||||||
l := uint32(len(r))
|
|
||||||
st.Cache[checkFrame][key] = ""
|
|
||||||
if st.CacheMap[key] != "" {
|
|
||||||
st.CacheMap[key] = value
|
|
||||||
}
|
|
||||||
st.CacheUseSize -= l
|
|
||||||
sz := st.checkCapacity(value)
|
|
||||||
if sz == 0 {
|
|
||||||
baseUseSize := st.CacheUseSize
|
|
||||||
st.Cache[checkFrame][key] = r
|
|
||||||
st.CacheUseSize += l
|
|
||||||
return fmt.Errorf("Cache capacity exceeded %v of %v", baseUseSize + sz, st.CacheSize)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map marks the given key for retrieval.
|
|
||||||
//
|
|
||||||
// After this, Val() will return the value for the key, and Size() will include the value size and limitations in its calculations.
|
|
||||||
//
|
|
||||||
// Only one symbol with no size limitation may be mapped at the current level.
|
|
||||||
func(st *State) Map(key string) error {
|
|
||||||
m, err := st.Get()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
l := st.sizes[key]
|
|
||||||
if l == 0 {
|
|
||||||
if st.sink != nil {
|
|
||||||
return fmt.Errorf("sink already set to symbol '%v'", *st.sink)
|
|
||||||
}
|
|
||||||
st.sink = &key
|
|
||||||
}
|
|
||||||
st.CacheMap[key] = m[key]
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Depth returns the current call stack depth.
|
// Depth returns the current call stack depth.
|
||||||
func(st *State) Depth() uint8 {
|
func(st *State) Depth() uint8 {
|
||||||
return uint8(len(st.Cache))
|
return uint8(len(st.execPath)-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns the full key-value mapping for all mapped keys at the current cache level.
|
//func(st *State) SetMenuSize(size uint16) error {
|
||||||
func(st *State) Get() (map[string]string, error) {
|
// st.menuSize = size
|
||||||
if len(st.Cache) == 0 {
|
// log.Printf("menu size changed to %v", st.menuSize)
|
||||||
return nil, fmt.Errorf("get at top frame")
|
// return nil
|
||||||
}
|
//}
|
||||||
return st.Cache[len(st.Cache)-1], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func(st *State) Sizes() (map[string]uint16, error) {
|
|
||||||
if len(st.Cache) == 0 {
|
|
||||||
return nil, fmt.Errorf("get at top frame")
|
|
||||||
}
|
|
||||||
sizes := make(map[string]uint16)
|
|
||||||
var haveSink bool
|
|
||||||
for k, _ := range st.CacheMap {
|
|
||||||
l, ok := st.sizes[k]
|
|
||||||
if !ok {
|
|
||||||
panic(fmt.Sprintf("missing size for %v", k))
|
|
||||||
}
|
|
||||||
if l == 0 {
|
|
||||||
if haveSink {
|
|
||||||
panic(fmt.Sprintf("duplicate sink for %v", k))
|
|
||||||
}
|
|
||||||
haveSink = true
|
|
||||||
}
|
|
||||||
sizes[k] = l
|
|
||||||
}
|
|
||||||
return sizes, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func(st *State) SetMenuSize(size uint16) error {
|
|
||||||
st.menuSize = size
|
|
||||||
log.Printf("menu size changed to %v", st.menuSize)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func(st *State) GetMenuSize() uint16 {
|
|
||||||
return st.menuSize
|
|
||||||
}
|
|
||||||
|
|
||||||
func(st *State) GetOutputSize() uint32 {
|
|
||||||
return st.outputSize
|
|
||||||
}
|
|
||||||
|
|
||||||
// Val returns value for key
|
|
||||||
//
|
//
|
||||||
// Fails if key is not mapped.
|
//func(st *State) GetMenuSize() uint16 {
|
||||||
func(st *State) Val(key string) (string, error) {
|
// return st.menuSize
|
||||||
r := st.CacheMap[key]
|
//}
|
||||||
if len(r) == 0 {
|
//
|
||||||
return "", fmt.Errorf("key %v not mapped", key)
|
//func(st *State) GetOutputSize() uint32 {
|
||||||
}
|
// return st.outputSize
|
||||||
return r, nil
|
//}
|
||||||
}
|
|
||||||
|
|
||||||
// Reset flushes all state contents below the top level, and returns to the top level.
|
|
||||||
func(st *State) Reset() {
|
|
||||||
if len(st.Cache) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
st.Cache = st.Cache[:1]
|
|
||||||
st.CacheUseSize = 0
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check returns true if a key already exists in the cache.
|
|
||||||
func(st *State) Check(key string) bool {
|
|
||||||
return st.frameOf(key) == -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size returns size used by values and menu, and remaining size available
|
|
||||||
func(st *State) Size() (uint32, uint32) {
|
|
||||||
var l int
|
|
||||||
var c uint16
|
|
||||||
for k, v := range st.CacheMap {
|
|
||||||
l += len(v)
|
|
||||||
c += st.sizes[k]
|
|
||||||
}
|
|
||||||
r := uint32(l)
|
|
||||||
r += uint32(st.menuSize)
|
|
||||||
return r, uint32(c)-r
|
|
||||||
}
|
|
||||||
|
|
||||||
// Appendcode adds the given bytecode to the end of the existing code.
|
// Appendcode adds the given bytecode to the end of the existing code.
|
||||||
func(st *State) AppendCode(b []byte) error {
|
func(st *State) AppendCode(b []byte) error {
|
||||||
@ -514,11 +320,6 @@ func(st *State) GetInput() ([]byte, error) {
|
|||||||
|
|
||||||
// SetInput is used to record the latest client input.
|
// SetInput is used to record the latest client input.
|
||||||
func(st *State) SetInput(input []byte) error {
|
func(st *State) SetInput(input []byte) error {
|
||||||
// if input == nil {
|
|
||||||
// log.Printf("clearing input")
|
|
||||||
// st.input = nil
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
l := len(input)
|
l := len(input)
|
||||||
if l > 255 {
|
if l > 255 {
|
||||||
return fmt.Errorf("input size %v too large (limit %v)", l, 255)
|
return fmt.Errorf("input size %v too large (limit %v)", l, 255)
|
||||||
@ -527,33 +328,3 @@ func(st *State) SetInput(input []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// return 0-indexed frame number where key is defined. -1 if not defined
|
|
||||||
func(st *State) frameOf(key string) int {
|
|
||||||
for i, m := range st.Cache {
|
|
||||||
for k, _ := range m {
|
|
||||||
if k == key {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// bytes that will be added to cache use size for string
|
|
||||||
// returns 0 if capacity would be exceeded
|
|
||||||
func(st *State) checkCapacity(v string) uint32 {
|
|
||||||
sz := uint32(len(v))
|
|
||||||
if st.CacheSize == 0 {
|
|
||||||
return sz
|
|
||||||
}
|
|
||||||
if st.CacheUseSize + sz > st.CacheSize {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return sz
|
|
||||||
}
|
|
||||||
|
|
||||||
// flush relveant properties for level change
|
|
||||||
func(st *State) resetCurrent() {
|
|
||||||
st.sink = nil
|
|
||||||
st.CacheMap = make(map[string]string)
|
|
||||||
}
|
|
||||||
|
@ -9,15 +9,15 @@ import (
|
|||||||
func TestNewState(t *testing.T) {
|
func TestNewState(t *testing.T) {
|
||||||
st := NewState(5)
|
st := NewState(5)
|
||||||
if len(st.Flags) != 2 {
|
if len(st.Flags) != 2 {
|
||||||
t.Errorf("invalid state flag length: %v", len(st.Flags))
|
t.Fatalf("invalid state flag length: %v", len(st.Flags))
|
||||||
}
|
}
|
||||||
st = NewState(8)
|
st = NewState(8)
|
||||||
if len(st.Flags) != 2 {
|
if len(st.Flags) != 2 {
|
||||||
t.Errorf("invalid state flag length: %v", len(st.Flags))
|
t.Fatalf("invalid state flag length: %v", len(st.Flags))
|
||||||
}
|
}
|
||||||
st = NewState(17)
|
st = NewState(17)
|
||||||
if len(st.Flags) != 4 {
|
if len(st.Flags) != 4 {
|
||||||
t.Errorf("invalid state flag length: %v", len(st.Flags))
|
t.Fatalf("invalid state flag length: %v", len(st.Flags))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,63 +28,63 @@ func TestStateFlags(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
if v {
|
if v {
|
||||||
t.Errorf("Expected bit 2 not to be set")
|
t.Fatalf("Expected bit 2 not to be set")
|
||||||
}
|
}
|
||||||
v, err = st.SetFlag(2)
|
v, err = st.SetFlag(2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
if !v {
|
if !v {
|
||||||
t.Errorf("Expected change to be set for bit 2")
|
t.Fatalf("Expected change to be set for bit 2")
|
||||||
}
|
}
|
||||||
v, err = st.GetFlag(2)
|
v, err = st.GetFlag(2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
if !v {
|
if !v {
|
||||||
t.Errorf("Expected bit 2 to be set")
|
t.Fatalf("Expected bit 2 to be set")
|
||||||
}
|
}
|
||||||
v, err = st.SetFlag(10)
|
v, err = st.SetFlag(10)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
if !v {
|
if !v {
|
||||||
t.Errorf("Expected change to be set for bit 10")
|
t.Fatalf("Expected change to be set for bit 10")
|
||||||
}
|
}
|
||||||
v, err = st.GetFlag(10)
|
v, err = st.GetFlag(10)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
if !v {
|
if !v {
|
||||||
t.Errorf("Expected bit 10 to be set")
|
t.Fatalf("Expected bit 10 to be set")
|
||||||
}
|
}
|
||||||
v, err = st.ResetFlag(2)
|
v, err = st.ResetFlag(2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
if !v {
|
if !v {
|
||||||
t.Errorf("Expected change to be set for bit 10")
|
t.Fatalf("Expected change to be set for bit 10")
|
||||||
}
|
}
|
||||||
v, err = st.GetFlag(2)
|
v, err = st.GetFlag(2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
if v {
|
if v {
|
||||||
t.Errorf("Expected bit 2 not to be set")
|
t.Fatalf("Expected bit 2 not to be set")
|
||||||
}
|
}
|
||||||
v, err = st.GetFlag(10)
|
v, err = st.GetFlag(10)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
if !v {
|
if !v {
|
||||||
t.Errorf("Expected bit 10 to be set")
|
t.Fatalf("Expected bit 10 to be set")
|
||||||
}
|
}
|
||||||
v, err = st.SetFlag(10)
|
v, err = st.SetFlag(10)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
if v {
|
if v {
|
||||||
t.Errorf("Expected change not to be set for bit 10")
|
t.Fatalf("Expected change not to be set for bit 10")
|
||||||
}
|
}
|
||||||
v, err = st.SetFlag(2)
|
v, err = st.SetFlag(2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -96,10 +96,10 @@ func TestStateFlags(t *testing.T) {
|
|||||||
}
|
}
|
||||||
v, err = st.SetFlag(17)
|
v, err = st.SetFlag(17)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("Expected out of range for bit index 17")
|
t.Fatalf("Expected out of range for bit index 17")
|
||||||
}
|
}
|
||||||
if !bytes.Equal(st.Flags[:3], []byte{0x04, 0x04, 0x01}) {
|
if !bytes.Equal(st.Flags[:3], []byte{0x04, 0x04, 0x01}) {
|
||||||
t.Errorf("Expected 0x040401, got %v", st.Flags[:3])
|
t.Fatalf("Expected 0x040401, got %v", st.Flags[:3])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,212 +108,112 @@ func TestStateFlagFromSlice(t *testing.T) {
|
|||||||
_, _= st.SetFlag(2)
|
_, _= st.SetFlag(2)
|
||||||
v := st.GetIndex([]byte{})
|
v := st.GetIndex([]byte{})
|
||||||
if v {
|
if v {
|
||||||
t.Errorf("Expected no match on empty compare")
|
t.Fatalf("Expected no match on empty compare")
|
||||||
}
|
}
|
||||||
v = st.GetIndex([]byte{0x01})
|
v = st.GetIndex([]byte{0x01})
|
||||||
if v {
|
if v {
|
||||||
t.Errorf("Expected 0x01 not to match")
|
t.Fatalf("Expected 0x01 not to match")
|
||||||
}
|
}
|
||||||
v = st.GetIndex([]byte{0x04})
|
v = st.GetIndex([]byte{0x04})
|
||||||
if !v {
|
if !v {
|
||||||
t.Errorf("Expected 0x04 to match")
|
t.Fatalf("Expected 0x04 to match")
|
||||||
}
|
}
|
||||||
_, _= st.SetFlag(12)
|
_, _= st.SetFlag(12)
|
||||||
v = st.GetIndex([]byte{0x04})
|
v = st.GetIndex([]byte{0x04})
|
||||||
if !v {
|
if !v {
|
||||||
t.Errorf("Expected 0x04 to match")
|
t.Fatalf("Expected 0x04 to match")
|
||||||
}
|
}
|
||||||
v = st.GetIndex([]byte{0x00, 0x10})
|
v = st.GetIndex([]byte{0x00, 0x10})
|
||||||
if !v {
|
if !v {
|
||||||
t.Errorf("Expected 0x1000 to match")
|
t.Fatalf("Expected 0x1000 to match")
|
||||||
}
|
}
|
||||||
v, _ = st.ResetFlag(2)
|
v, _ = st.ResetFlag(2)
|
||||||
v = st.GetIndex([]byte{0x00, 0x10})
|
v = st.GetIndex([]byte{0x00, 0x10})
|
||||||
if !v {
|
if !v {
|
||||||
t.Errorf("Expected 0x1000 to matck")
|
t.Fatalf("Expected 0x1000 to matck")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
func TestStateNavigate(t *testing.T) {
|
||||||
func TestNewStateCache(t *testing.T) {
|
st := NewState(0)
|
||||||
st := NewState(17)
|
err := st.Down("foo")
|
||||||
if st.CacheSize != 0 {
|
if err != nil {
|
||||||
t.Errorf("cache size not 0")
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
st = st.WithCacheSize(102525)
|
err = st.Down("bar")
|
||||||
if st.CacheSize != 102525 {
|
if err != nil {
|
||||||
t.Errorf("cache size not 102525")
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = st.Down("baz")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
s, i := st.Where()
|
||||||
|
if s != "baz" {
|
||||||
|
t.Fatalf("expected baz, got %s", s)
|
||||||
|
}
|
||||||
|
if i != 0 {
|
||||||
|
t.Fatalf("expected idx 0, got %v", i)
|
||||||
|
}
|
||||||
|
r := st.Depth()
|
||||||
|
if r != 2 {
|
||||||
|
t.Fatalf("expected depth 3, got %v", r)
|
||||||
|
}
|
||||||
|
|
||||||
func TestStateCacheUse(t *testing.T) {
|
|
||||||
st := NewState(17)
|
|
||||||
st = st.WithCacheSize(10)
|
|
||||||
st.Down("foo")
|
|
||||||
err := st.Add("bar", "baz", 0)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
err = st.Add("inky", "pinky", 0)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
err = st.Add("blinky", "clyde", 0)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("expected capacity error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStateDownUp(t *testing.T) {
|
|
||||||
st := NewState(17)
|
|
||||||
st.Down("one")
|
|
||||||
st.Down("two")
|
|
||||||
err := st.Add("foo", "bar", 0)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
err = st.Add("baz", "xyzzy", 0)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
if st.CacheUseSize != 8 {
|
|
||||||
t.Errorf("expected cache use size 8 got %v", st.CacheUseSize)
|
|
||||||
}
|
|
||||||
s, err := st.Up()
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
if s != "one" {
|
|
||||||
t.Errorf("expected sym 'one', got '%s'", s)
|
|
||||||
}
|
|
||||||
s, err = st.Up()
|
s, err = st.Up()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if s != "" {
|
if s != "bar" {
|
||||||
t.Errorf("expected sym '', got '%s'", s)
|
t.Fatalf("expected bar, got %s", s)
|
||||||
}
|
}
|
||||||
|
s, i = st.Where()
|
||||||
|
if s != "bar" {
|
||||||
|
t.Fatalf("expected bar, got %s", s)
|
||||||
|
}
|
||||||
|
if i != 0 {
|
||||||
|
t.Fatalf("expected idx 0, got %v", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
i, err = st.Next()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if i != 1 {
|
||||||
|
t.Fatalf("expected idx 1, got %v", i)
|
||||||
|
}
|
||||||
|
i, err = st.Next()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if i != 2 {
|
||||||
|
t.Fatalf("expected idx 2, got %v", i)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s, i = st.Where()
|
||||||
|
if s != "bar" {
|
||||||
|
t.Fatalf("expected baz, got %s", s)
|
||||||
|
}
|
||||||
|
if i != 2 {
|
||||||
|
t.Fatalf("expected idx 2, got %v", i)
|
||||||
|
}
|
||||||
|
|
||||||
s, err = st.Up()
|
s, err = st.Up()
|
||||||
if err == nil {
|
if err != nil {
|
||||||
t.Errorf("expected out of top frame error")
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
if s != "foo" {
|
||||||
|
t.Fatalf("expected foo, got %s", s)
|
||||||
func TestStateReset(t *testing.T) {
|
}
|
||||||
st := NewState(17)
|
s, i = st.Where()
|
||||||
st.Down("one")
|
if s != "foo" {
|
||||||
err := st.Add("foo", "bar", 0)
|
t.Fatalf("expected foo, got %s", s)
|
||||||
if err != nil {
|
}
|
||||||
t.Error(err)
|
if i != 0 {
|
||||||
}
|
t.Fatalf("expected idx 0, got %v", i)
|
||||||
err = st.Add("baz", "xyzzy", 0)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
st.Down("two")
|
|
||||||
st.Down("three")
|
|
||||||
st.Reset()
|
|
||||||
if st.CacheUseSize != 0 {
|
|
||||||
t.Errorf("expected cache use size 0, got %v", st.CacheUseSize)
|
|
||||||
}
|
|
||||||
if st.Depth() != 1 {
|
|
||||||
t.Errorf("expected depth 1, got %v", st.Depth())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStateLoadDup(t *testing.T) {
|
|
||||||
st := NewState(17)
|
|
||||||
st.Down("one")
|
|
||||||
err := st.Add("foo", "bar", 0)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
st.Down("two")
|
|
||||||
err = st.Add("foo", "baz", 0)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("expected fail on duplicate load")
|
|
||||||
}
|
|
||||||
st.Up()
|
|
||||||
err = st.Add("foo", "xyzzy", 0)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStateCurrentSize(t *testing.T) {
|
|
||||||
st := NewState(17)
|
|
||||||
st.Down("one")
|
|
||||||
err := st.Add("foo", "bar", 0)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
st.Down("two")
|
|
||||||
err = st.Add("bar", "xyzzy", 10)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
err = st.Map("bar")
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
err = st.Add("baz", "inkypinkyblinkyclyde", 51)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
err = st.Map("baz")
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
l, c := st.Size()
|
|
||||||
if l != 25 {
|
|
||||||
t.Errorf("expected actual length 25, got %v", l)
|
|
||||||
}
|
|
||||||
if c != 36 {
|
|
||||||
t.Errorf("expected actual length 50, got %v", c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStateMapSink(t *testing.T) {
|
|
||||||
st := NewState(17)
|
|
||||||
st.Down("one")
|
|
||||||
err := st.Add("foo", "bar", 0)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
st.Down("two")
|
|
||||||
err = st.Add("bar", "xyzzy", 6)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
err = st.Add("baz", "bazbaz", 18)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
err = st.Add("xyzzy", "plugh", 0)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
err = st.Map("foo")
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
err = st.Map("xyzzy")
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Expected fail on duplicate sink")
|
|
||||||
}
|
|
||||||
err = st.Map("baz")
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
st.Down("three")
|
|
||||||
err = st.Map("foo")
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
st.Up()
|
|
||||||
err = st.Map("foo")
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user