commit 629cae8a5590fc5529e424e690439b74e4953f06
parent 238f4546ffa07217a369a258b228d47bfb9b7b23
Author: lash <dev@holbrook.no>
Date: Sat, 8 Apr 2023 16:09:10 +0100
WIP factor out cache code
Diffstat:
A | go/cache/cache.go | | | 257 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | go/cache/cache_test.go | | | 186 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | go/state/state.go | | | 271 | +++++++------------------------------------------------------------------------ |
M | go/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)
}
}