go-vise

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

commit 629cae8a5590fc5529e424e690439b74e4953f06
parent 238f4546ffa07217a369a258b228d47bfb9b7b23
Author: lash <dev@holbrook.no>
Date:   Sat,  8 Apr 2023 16:09:10 +0100

WIP factor out cache code

Diffstat:
Ago/cache/cache.go | 257+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ago/cache/cache_test.go | 186+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mgo/state/state.go | 271+++++++------------------------------------------------------------------------
Mgo/state/state_test.go | 240+++++++++++++++++++++++--------------------------------------------------------
4 files changed, 534 insertions(+), 420 deletions(-)

diff --git a/go/cache/cache.go b/go/cache/cache.go @@ -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 +} diff --git a/go/cache/cache_test.go b/go/cache/cache_test.go @@ -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) + } +} diff --git a/go/state/state.go b/go/state/state.go @@ -20,26 +20,17 @@ import ( // Symbol keys do not count towards cache size limitations. // // 8 first flags are reserved. -// -// TODO factor out cache type State struct { 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 code []byte // Pending bytecode to execute execPath []string // Command symbols stack 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 - sink *string sizeIdx uint16 } +// number of bytes necessary to represent a bitfield of the given size. func toByteSize(bitSize uint32) uint8 { if bitSize == 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. func NewState(bitSize uint32) State { st := State{ - CacheSize: 0, - CacheUseSize: 0, bitSize: bitSize + 8, } byteSize := toByteSize(bitSize + 8) @@ -72,7 +61,6 @@ func NewState(bitSize uint32) State { } else { st.Flags = []byte{} } - //st.Down("") return st } @@ -191,18 +179,6 @@ func(st *State) GetIndex(flags []byte) bool { 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. func(st *State) Where() (string, uint16) { if len(st.execPath) == 0 { @@ -267,12 +243,10 @@ func(st *State) Top() (bool, error) { // Down adds the given symbol to the command stack. // // Clears mapping and sink. -func(st *State) Down(input string) { - m := make(map[string]string) - st.Cache = append(st.Cache, m) - st.sizes = make(map[string]uint16) +func(st *State) Down(input string) error { 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. @@ -283,206 +257,38 @@ func(st *State) Down(input string) { // // Fails if called at top frame. 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") } - l := len(st.Cache) - 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] + st.execPath = st.execPath[:l-1] sym := "" if len(st.execPath) > 0 { sym = st.execPath[len(st.execPath)-1] } - st.resetCurrent() + st.sizeIdx = 0 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. func(st *State) Depth() uint8 { - return uint8(len(st.Cache)) -} - -// Get returns the full key-value mapping for all mapped keys at the current cache level. -func(st *State) Get() (map[string]string, error) { - if len(st.Cache) == 0 { - return nil, fmt.Errorf("get at top frame") - } - 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 + return uint8(len(st.execPath)-1) } -func(st *State) GetMenuSize() uint16 { - return st.menuSize -} - -func(st *State) GetOutputSize() uint32 { - return st.outputSize -} - -// Val returns value for key +//func(st *State) SetMenuSize(size uint16) error { +// st.menuSize = size +// log.Printf("menu size changed to %v", st.menuSize) +// return nil +//} // -// Fails if key is not mapped. -func(st *State) Val(key string) (string, error) { - r := st.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, 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 -} +//func(st *State) GetMenuSize() uint16 { +// return st.menuSize +//} +// +//func(st *State) GetOutputSize() uint32 { +// return st.outputSize +//} -// 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. 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. func(st *State) SetInput(input []byte) error { -// if input == nil { -// log.Printf("clearing input") -// st.input = nil -// return nil -// } l := len(input) if 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 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) -} diff --git a/go/state/state_test.go b/go/state/state_test.go @@ -9,15 +9,15 @@ import ( func TestNewState(t *testing.T) { st := NewState(5) 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) 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) 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) } 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) if err != nil { t.Error(err) } 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) if err != nil { t.Error(err) } if !v { - t.Errorf("Expected bit 2 to be set") + t.Fatalf("Expected bit 2 to be set") } v, err = st.SetFlag(10) if err != nil { t.Error(err) } 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) if err != nil { t.Error(err) } if !v { - t.Errorf("Expected bit 10 to be set") + t.Fatalf("Expected bit 10 to be set") } v, err = st.ResetFlag(2) if err != nil { t.Error(err) } 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) if err != nil { t.Error(err) } 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) if err != nil { t.Error(err) } if !v { - t.Errorf("Expected bit 10 to be set") + t.Fatalf("Expected bit 10 to be set") } v, err = st.SetFlag(10) if err != nil { t.Error(err) } 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) if err != nil { @@ -96,10 +96,10 @@ func TestStateFlags(t *testing.T) { } v, err = st.SetFlag(17) 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}) { - 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) v := st.GetIndex([]byte{}) if v { - t.Errorf("Expected no match on empty compare") + t.Fatalf("Expected no match on empty compare") } v = st.GetIndex([]byte{0x01}) if v { - t.Errorf("Expected 0x01 not to match") + t.Fatalf("Expected 0x01 not to match") } v = st.GetIndex([]byte{0x04}) if !v { - t.Errorf("Expected 0x04 to match") + t.Fatalf("Expected 0x04 to match") } _, _= st.SetFlag(12) v = st.GetIndex([]byte{0x04}) if !v { - t.Errorf("Expected 0x04 to match") + t.Fatalf("Expected 0x04 to match") } v = st.GetIndex([]byte{0x00, 0x10}) if !v { - t.Errorf("Expected 0x1000 to match") + t.Fatalf("Expected 0x1000 to match") } v, _ = st.ResetFlag(2) v = st.GetIndex([]byte{0x00, 0x10}) if !v { - t.Errorf("Expected 0x1000 to matck") + t.Fatalf("Expected 0x1000 to matck") } } -// -func TestNewStateCache(t *testing.T) { - st := NewState(17) - if st.CacheSize != 0 { - t.Errorf("cache size not 0") - } - st = st.WithCacheSize(102525) - if st.CacheSize != 102525 { - t.Errorf("cache size not 102525") - } - -} - -func TestStateCacheUse(t *testing.T) { - st := NewState(17) - st = st.WithCacheSize(10) - st.Down("foo") - err := st.Add("bar", "baz", 0) +func TestStateNavigate(t *testing.T) { + st := NewState(0) + err := st.Down("foo") if err != nil { - t.Error(err) + t.Fatal(err) } - err = st.Add("inky", "pinky", 0) + err = st.Down("bar") if err != nil { - t.Error(err) + t.Fatal(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) + err = st.Down("baz") 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) + t.Fatal(err) } - if s != "one" { - t.Errorf("expected sym 'one', got '%s'", s) - } - s, err = st.Up() - if err != nil { - t.Error(err) - } - if s != "" { - t.Errorf("expected sym '', got '%s'", s) - } - s, err = st.Up() - if err == nil { - t.Errorf("expected out of top frame error") - } -} -func TestStateReset(t *testing.T) { - st := NewState(17) - st.Down("one") - err := st.Add("foo", "bar", 0) - if err != nil { - t.Error(err) + s, i := st.Where() + if s != "baz" { + t.Fatalf("expected baz, got %s", s) } - err = st.Add("baz", "xyzzy", 0) - if err != nil { - t.Error(err) + if i != 0 { + t.Fatalf("expected idx 0, got %v", i) } - st.Down("two") - st.Down("three") - st.Reset() - if st.CacheUseSize != 0 { - t.Errorf("expected cache use size 0, got %v", st.CacheUseSize) + r := st.Depth() + if r != 2 { + t.Fatalf("expected depth 3, got %v", r) } - 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) + s, err = st.Up() if err != nil { - t.Error(err) + t.Fatal(err) } - st.Down("two") - err = st.Add("foo", "baz", 0) - if err == nil { - t.Errorf("expected fail on duplicate load") + if s != "bar" { + t.Fatalf("expected bar, got %s", s) } - st.Up() - err = st.Add("foo", "xyzzy", 0) - if err != nil { - t.Error(err) + 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) } -} -func TestStateCurrentSize(t *testing.T) { - st := NewState(17) - st.Down("one") - err := st.Add("foo", "bar", 0) + i, err = st.Next() if err != nil { - t.Error(err) + t.Fatal(err) } - st.Down("two") - err = st.Add("bar", "xyzzy", 10) - if err != nil { - t.Error(err) + if i != 1 { + t.Fatalf("expected idx 1, got %v", i) } - err = st.Map("bar") + i, err = st.Next() if err != nil { - t.Error(err) + t.Fatal(err) } - err = st.Add("baz", "inkypinkyblinkyclyde", 51) - if err != nil { - t.Error(err) + if i != 2 { + t.Fatalf("expected idx 2, got %v", i) } - 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) + t.Fatal(err) } -} -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) + s, i = st.Where() + if s != "bar" { + t.Fatalf("expected baz, got %s", s) } - err = st.Add("baz", "bazbaz", 18) - if err != nil { - t.Error(err) + if i != 2 { + t.Fatalf("expected idx 2, got %v", i) } - err = st.Add("xyzzy", "plugh", 0) - if err != nil { - t.Error(err) - } - err = st.Map("foo") + + s, err = st.Up() if err != nil { - t.Error(err) + t.Fatal(err) } - err = st.Map("xyzzy") - if err == nil { - t.Errorf("Expected fail on duplicate sink") + if s != "foo" { + t.Fatalf("expected foo, got %s", s) } - err = st.Map("baz") - if err != nil { - t.Error(err) + s, i = st.Where() + if s != "foo" { + t.Fatalf("expected foo, got %s", s) } - 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) + if i != 0 { + t.Fatalf("expected idx 0, got %v", i) } }