Add persist module
This commit is contained in:
parent
15c64a46e6
commit
064418cb83
2
asm/doc.go
Normal file
2
asm/doc.go
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
// Package asm parses and compiles festive assembly code to bytecode.
|
||||||
|
package asm
|
12
cache/cache.go
vendored
12
cache/cache.go
vendored
@ -10,14 +10,14 @@ type Cache struct {
|
|||||||
CacheSize uint32 // Total allowed cumulative size of values (not code) in cache
|
CacheSize uint32 // Total allowed cumulative size of values (not code) in cache
|
||||||
CacheUseSize uint32 // Currently used bytes by all 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
|
Cache []map[string]string // All loaded cache items
|
||||||
sizes map[string]uint16 // Size limits for all loaded symbols.
|
Sizes map[string]uint16 // Size limits for all loaded symbols.
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCache creates a new ready-to-use cache object
|
// NewCache creates a new ready-to-use cache object
|
||||||
func NewCache() *Cache {
|
func NewCache() *Cache {
|
||||||
ca := &Cache{
|
ca := &Cache{
|
||||||
Cache: []map[string]string{make(map[string]string)},
|
Cache: []map[string]string{make(map[string]string)},
|
||||||
sizes: make(map[string]uint16),
|
Sizes: make(map[string]uint16),
|
||||||
}
|
}
|
||||||
return ca
|
return ca
|
||||||
}
|
}
|
||||||
@ -58,13 +58,13 @@ func(ca *Cache) Add(key string, value string, sizeLimit uint16) error {
|
|||||||
log.Printf("add key %s value size %v limit %v", key, sz, sizeLimit)
|
log.Printf("add key %s value size %v limit %v", key, sz, sizeLimit)
|
||||||
ca.Cache[len(ca.Cache)-1][key] = value
|
ca.Cache[len(ca.Cache)-1][key] = value
|
||||||
ca.CacheUseSize += sz
|
ca.CacheUseSize += sz
|
||||||
ca.sizes[key] = sizeLimit
|
ca.Sizes[key] = sizeLimit
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReservedSize returns the maximum byte size available for the given symbol.
|
// ReservedSize returns the maximum byte size available for the given symbol.
|
||||||
func(ca *Cache) ReservedSize(key string) (uint16, error) {
|
func(ca *Cache) ReservedSize(key string) (uint16, error) {
|
||||||
v, ok := ca.sizes[key]
|
v, ok := ca.Sizes[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
return 0, fmt.Errorf("unknown symbol: %s", key)
|
return 0, fmt.Errorf("unknown symbol: %s", key)
|
||||||
}
|
}
|
||||||
@ -80,8 +80,8 @@ func(ca *Cache) ReservedSize(key string) (uint16, error) {
|
|||||||
// - value is longer than size limit
|
// - value is longer than size limit
|
||||||
// - replacing value exceeds cumulative cache capacity
|
// - replacing value exceeds cumulative cache capacity
|
||||||
func(ca *Cache) Update(key string, value string) error {
|
func(ca *Cache) Update(key string, value string) error {
|
||||||
sizeLimit := ca.sizes[key]
|
sizeLimit := ca.Sizes[key]
|
||||||
if ca.sizes[key] > 0 {
|
if ca.Sizes[key] > 0 {
|
||||||
l := uint16(len(value))
|
l := uint16(len(value))
|
||||||
if l > sizeLimit {
|
if l > sizeLimit {
|
||||||
return fmt.Errorf("update value length %v exceeds value size limit %v", l, sizeLimit)
|
return fmt.Errorf("update value length %v exceeds value size limit %v", l, sizeLimit)
|
||||||
|
3
go.mod
3
go.mod
@ -4,5 +4,8 @@ go 1.20
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alecthomas/participle/v2 v2.0.0
|
github.com/alecthomas/participle/v2 v2.0.0
|
||||||
|
github.com/fxamacker/cbor/v2 v2.4.0
|
||||||
github.com/peteole/testdata-loader v0.3.0
|
github.com/peteole/testdata-loader v0.3.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require github.com/x448/float16 v0.8.4 // indirect
|
||||||
|
61
persist/fs.go
Normal file
61
persist/fs.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package persist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"github.com/fxamacker/cbor/v2"
|
||||||
|
|
||||||
|
"git.defalsify.org/festive/cache"
|
||||||
|
"git.defalsify.org/festive/state"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FsPersister struct {
|
||||||
|
State *state.State
|
||||||
|
Memory *cache.Cache
|
||||||
|
dir string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFsPersister(dir string) *FsPersister {
|
||||||
|
fp, err := filepath.Abs(dir)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return &FsPersister{
|
||||||
|
dir: fp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func(p *FsPersister) WithContent(st *state.State, ca *cache.Cache) *FsPersister {
|
||||||
|
p.State = st
|
||||||
|
p.Memory = ca
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func(p *FsPersister) Serialize() ([]byte, error) {
|
||||||
|
return cbor.Marshal(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func(p *FsPersister) Deserialize(b []byte) error {
|
||||||
|
err := cbor.Unmarshal(b, p)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func(p *FsPersister) Save(key string) error {
|
||||||
|
b, err := p.Serialize()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fp := path.Join(p.dir, key)
|
||||||
|
return ioutil.WriteFile(fp, b, 0600)
|
||||||
|
}
|
||||||
|
|
||||||
|
func(p *FsPersister) Load(key string) error {
|
||||||
|
fp := path.Join(p.dir, key)
|
||||||
|
b, err := ioutil.ReadFile(fp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = p.Deserialize(b)
|
||||||
|
return err
|
||||||
|
}
|
106
persist/fs_test.go
Normal file
106
persist/fs_test.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
package persist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.defalsify.org/festive/cache"
|
||||||
|
"git.defalsify.org/festive/state"
|
||||||
|
"git.defalsify.org/festive/vm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSerializeState(t *testing.T) {
|
||||||
|
st := state.NewState(12)
|
||||||
|
st.Down("foo")
|
||||||
|
st.Down("bar")
|
||||||
|
st.Down("baz")
|
||||||
|
st.Next()
|
||||||
|
st.Next()
|
||||||
|
|
||||||
|
b := vm.NewLine(nil, vm.LOAD, []string{"foo"}, []byte{42}, nil)
|
||||||
|
b = vm.NewLine(b, vm.HALT, nil, nil, nil)
|
||||||
|
st.SetCode(b)
|
||||||
|
|
||||||
|
ca := cache.NewCache().WithCacheSize(1024)
|
||||||
|
ca.Add("inky", "pinky", 13)
|
||||||
|
ca.Add("blinky", "clyde", 42)
|
||||||
|
|
||||||
|
pr := NewFsPersister(".").WithContent(&st, ca)
|
||||||
|
v, err := pr.Serialize()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
log.Printf("v %b", v)
|
||||||
|
|
||||||
|
prnew := NewFsPersister(".")
|
||||||
|
err = prnew.Deserialize(v)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(prnew.State.ExecPath, pr.State.ExecPath) {
|
||||||
|
t.Fatalf("expected %s, got %s", prnew.State.ExecPath, pr.State.ExecPath)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(prnew.State.Code, pr.State.Code) {
|
||||||
|
t.Fatalf("expected %x, got %x", prnew.State.Code, pr.State.Code)
|
||||||
|
}
|
||||||
|
if prnew.State.BitSize != pr.State.BitSize {
|
||||||
|
t.Fatalf("expected %v, got %v", prnew.State.BitSize, pr.State.BitSize)
|
||||||
|
}
|
||||||
|
if prnew.State.SizeIdx != pr.State.SizeIdx {
|
||||||
|
t.Fatalf("expected %v, got %v", prnew.State.SizeIdx, pr.State.SizeIdx)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(prnew.Memory, pr.Memory) {
|
||||||
|
t.Fatalf("expected %v, got %v", prnew.Memory, pr.Memory)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSaveLoad(t *testing.T) {
|
||||||
|
st := state.NewState(12)
|
||||||
|
st.Down("foo")
|
||||||
|
st.Down("bar")
|
||||||
|
st.Down("baz")
|
||||||
|
st.Next()
|
||||||
|
st.Next()
|
||||||
|
|
||||||
|
b := vm.NewLine(nil, vm.LOAD, []string{"foo"}, []byte{42}, nil)
|
||||||
|
b = vm.NewLine(b, vm.HALT, nil, nil, nil)
|
||||||
|
st.SetCode(b)
|
||||||
|
|
||||||
|
ca := cache.NewCache().WithCacheSize(1024)
|
||||||
|
ca.Add("inky", "pinky", 13)
|
||||||
|
ca.Add("blinky", "clyde", 42)
|
||||||
|
|
||||||
|
dir, err := ioutil.TempDir("", "festive_persist")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
pr := NewFsPersister(dir).WithContent(&st, ca)
|
||||||
|
err = pr.Save("xyzzy")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
prnew := NewFsPersister(dir)
|
||||||
|
err = prnew.Load("xyzzy")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(prnew.State.ExecPath, pr.State.ExecPath) {
|
||||||
|
t.Fatalf("expected %s, got %s", prnew.State.ExecPath, pr.State.ExecPath)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(prnew.State.Code, pr.State.Code) {
|
||||||
|
t.Fatalf("expected %x, got %x", prnew.State.Code, pr.State.Code)
|
||||||
|
}
|
||||||
|
if prnew.State.BitSize != pr.State.BitSize {
|
||||||
|
t.Fatalf("expected %v, got %v", prnew.State.BitSize, pr.State.BitSize)
|
||||||
|
}
|
||||||
|
if prnew.State.SizeIdx != pr.State.SizeIdx {
|
||||||
|
t.Fatalf("expected %v, got %v", prnew.State.SizeIdx, pr.State.SizeIdx)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(prnew.Memory, pr.Memory) {
|
||||||
|
t.Fatalf("expected %v, got %v", prnew.Memory, pr.Memory)
|
||||||
|
}
|
||||||
|
}
|
9
persist/persist.go
Normal file
9
persist/persist.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package persist
|
||||||
|
|
||||||
|
type Persister interface {
|
||||||
|
Serialize() ([]byte, error)
|
||||||
|
Deserialize(b []byte) error
|
||||||
|
Save(key string) error
|
||||||
|
Load(key string) error
|
||||||
|
}
|
||||||
|
|
129
state/state.go
129
state/state.go
@ -29,25 +29,24 @@ func(err *IndexError) Error() string {
|
|||||||
//
|
//
|
||||||
// 8 first flags are reserved.
|
// 8 first flags are reserved.
|
||||||
type State struct {
|
type State struct {
|
||||||
Flags []byte // Error state
|
Code []byte // Pending bytecode to execute
|
||||||
|
ExecPath []string // Command symbols stack
|
||||||
|
BitSize uint32 // size of (32-bit capacity) bit flag byte array
|
||||||
|
SizeIdx uint16
|
||||||
|
flags []byte // Error state
|
||||||
input []byte // Last input
|
input []byte // Last input
|
||||||
code []byte // Pending bytecode to execute
|
|
||||||
execPath []string // Command symbols stack
|
|
||||||
arg *string // Optional argument. Nil if not set.
|
|
||||||
bitSize uint32 // size of (32-bit capacity) bit flag byte array
|
|
||||||
sizeIdx uint16
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// number of bytes necessary to represent a bitfield of the given size.
|
// 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
|
||||||
}
|
}
|
||||||
n := bitSize % 8
|
n := BitSize % 8
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
bitSize += (8 - n)
|
BitSize += (8 - n)
|
||||||
}
|
}
|
||||||
return uint8(bitSize / 8)
|
return uint8(BitSize / 8)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the state of a state flag
|
// Retrieve the state of a state flag
|
||||||
@ -58,16 +57,16 @@ func getFlag(bitIndex uint32, bitField []byte) bool {
|
|||||||
return (b & (1 << localBitIndex)) > 0
|
return (b & (1 << localBitIndex)) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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{
|
||||||
bitSize: bitSize + 8,
|
BitSize: BitSize + 8,
|
||||||
}
|
}
|
||||||
byteSize := toByteSize(bitSize + 8)
|
byteSize := toByteSize(BitSize + 8)
|
||||||
if byteSize > 0 {
|
if byteSize > 0 {
|
||||||
st.Flags = make([]byte, byteSize)
|
st.flags = make([]byte, byteSize)
|
||||||
} else {
|
} else {
|
||||||
st.Flags = []byte{}
|
st.flags = []byte{}
|
||||||
}
|
}
|
||||||
return st
|
return st
|
||||||
}
|
}
|
||||||
@ -78,17 +77,17 @@ func NewState(bitSize uint32) State {
|
|||||||
//
|
//
|
||||||
// Fails if bitindex is out of range.
|
// Fails if bitindex is out of range.
|
||||||
func(st *State) SetFlag(bitIndex uint32) (bool, error) {
|
func(st *State) SetFlag(bitIndex uint32) (bool, error) {
|
||||||
if bitIndex + 1 > st.bitSize {
|
if bitIndex + 1 > st.BitSize {
|
||||||
return false, fmt.Errorf("bit index %v is out of range of bitfield size %v", bitIndex, st.bitSize)
|
return false, fmt.Errorf("bit index %v is out of range of bitfield size %v", bitIndex, st.BitSize)
|
||||||
}
|
}
|
||||||
r := getFlag(bitIndex, st.Flags)
|
r := getFlag(bitIndex, st.flags)
|
||||||
if r {
|
if r {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
byteIndex := bitIndex / 8
|
byteIndex := bitIndex / 8
|
||||||
localBitIndex := bitIndex % 8
|
localBitIndex := bitIndex % 8
|
||||||
b := st.Flags[byteIndex]
|
b := st.flags[byteIndex]
|
||||||
st.Flags[byteIndex] = b | (1 << localBitIndex)
|
st.flags[byteIndex] = b | (1 << localBitIndex)
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,17 +98,17 @@ func(st *State) SetFlag(bitIndex uint32) (bool, error) {
|
|||||||
//
|
//
|
||||||
// Fails if bitindex is out of range.
|
// Fails if bitindex is out of range.
|
||||||
func(st *State) ResetFlag(bitIndex uint32) (bool, error) {
|
func(st *State) ResetFlag(bitIndex uint32) (bool, error) {
|
||||||
if bitIndex + 1 > st.bitSize {
|
if bitIndex + 1 > st.BitSize {
|
||||||
return false, fmt.Errorf("bit index %v is out of range of bitfield size %v", bitIndex, st.bitSize)
|
return false, fmt.Errorf("bit index %v is out of range of bitfield size %v", bitIndex, st.BitSize)
|
||||||
}
|
}
|
||||||
r := getFlag(bitIndex, st.Flags)
|
r := getFlag(bitIndex, st.flags)
|
||||||
if !r {
|
if !r {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
byteIndex := bitIndex / 8
|
byteIndex := bitIndex / 8
|
||||||
localBitIndex := bitIndex % 8
|
localBitIndex := bitIndex % 8
|
||||||
b := st.Flags[byteIndex]
|
b := st.flags[byteIndex]
|
||||||
st.Flags[byteIndex] = b & (^(1 << localBitIndex))
|
st.flags[byteIndex] = b & (^(1 << localBitIndex))
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,20 +116,20 @@ func(st *State) ResetFlag(bitIndex uint32) (bool, error) {
|
|||||||
//
|
//
|
||||||
// Fails if bit field index is out of range.
|
// Fails if bit field index is out of range.
|
||||||
func(st *State) GetFlag(bitIndex uint32) (bool, error) {
|
func(st *State) GetFlag(bitIndex uint32) (bool, error) {
|
||||||
if bitIndex + 1 > st.bitSize {
|
if bitIndex + 1 > st.BitSize {
|
||||||
return false, fmt.Errorf("bit index %v is out of range of bitfield size %v", bitIndex, st.bitSize)
|
return false, fmt.Errorf("bit index %v is out of range of bitfield size %v", bitIndex, st.BitSize)
|
||||||
}
|
}
|
||||||
return getFlag(bitIndex, st.Flags), nil
|
return getFlag(bitIndex, st.flags), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FlagBitSize reports the amount of bits available in the bit field index.
|
// FlagBitSize reports the amount of bits available in the bit field index.
|
||||||
func(st *State) FlagBitSize() uint32 {
|
func(st *State) FlagBitSize() uint32 {
|
||||||
return st.bitSize
|
return st.BitSize
|
||||||
}
|
}
|
||||||
|
|
||||||
// FlagBitSize reports the amount of bits available in the bit field index.
|
// FlagBitSize reports the amount of bits available in the bit field index.
|
||||||
func(st *State) FlagByteSize() uint8 {
|
func(st *State) FlagByteSize() uint8 {
|
||||||
return uint8(len(st.Flags))
|
return uint8(len(st.flags))
|
||||||
}
|
}
|
||||||
|
|
||||||
// MatchFlag matches the current state of the given flag.
|
// MatchFlag matches the current state of the given flag.
|
||||||
@ -158,7 +157,7 @@ func(st *State) MatchFlag(sig uint32, invertMatch bool) (bool, error) {
|
|||||||
// If the given byte slice is too small for the bit field bitsize, the check will terminate at end-of-data without error.
|
// If the given byte slice is too small for the bit field bitsize, the check will terminate at end-of-data without error.
|
||||||
func(st *State) GetIndex(flags []byte) bool {
|
func(st *State) GetIndex(flags []byte) bool {
|
||||||
var globalIndex uint32
|
var globalIndex uint32
|
||||||
if st.bitSize == 0 {
|
if st.BitSize == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if len(flags) == 0 {
|
if len(flags) == 0 {
|
||||||
@ -168,9 +167,9 @@ func(st *State) GetIndex(flags []byte) bool {
|
|||||||
var localIndex uint8
|
var localIndex uint8
|
||||||
l := uint8(len(flags))
|
l := uint8(len(flags))
|
||||||
var i uint32
|
var i uint32
|
||||||
for i = 0; i < st.bitSize; i++ {
|
for i = 0; i < st.BitSize; i++ {
|
||||||
testVal := flags[byteIndex] & (1 << localIndex)
|
testVal := flags[byteIndex] & (1 << localIndex)
|
||||||
if (testVal & st.Flags[byteIndex]) > 0 {
|
if (testVal & st.flags[byteIndex]) > 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
globalIndex += 1
|
globalIndex += 1
|
||||||
@ -189,50 +188,50 @@ func(st *State) GetIndex(flags []byte) bool {
|
|||||||
|
|
||||||
// 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 {
|
||||||
return "", 0
|
return "", 0
|
||||||
}
|
}
|
||||||
l := len(st.execPath)
|
l := len(st.ExecPath)
|
||||||
return st.execPath[l-1], st.sizeIdx
|
return st.ExecPath[l-1], st.SizeIdx
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next moves to the next sink page index.
|
// Next moves to the next sink page index.
|
||||||
func(st *State) Next() (uint16, error) {
|
func(st *State) Next() (uint16, error) {
|
||||||
if len(st.execPath) == 0 {
|
if len(st.ExecPath) == 0 {
|
||||||
return 0, fmt.Errorf("state root node not yet defined")
|
return 0, fmt.Errorf("state root node not yet defined")
|
||||||
}
|
}
|
||||||
st.sizeIdx += 1
|
st.SizeIdx += 1
|
||||||
s, idx := st.Where()
|
s, idx := st.Where()
|
||||||
log.Printf("next page for %s: %v", s, idx)
|
log.Printf("next page for %s: %v", s, idx)
|
||||||
return st.sizeIdx, nil
|
return st.SizeIdx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Previous moves to the next sink page index.
|
// Previous moves to the next sink page index.
|
||||||
//
|
//
|
||||||
// Fails if try to move beyond index 0.
|
// Fails if try to move beyond index 0.
|
||||||
func(st *State) Previous() (uint16, error) {
|
func(st *State) Previous() (uint16, error) {
|
||||||
if len(st.execPath) == 0 {
|
if len(st.ExecPath) == 0 {
|
||||||
return 0, fmt.Errorf("state root node not yet defined")
|
return 0, fmt.Errorf("state root node not yet defined")
|
||||||
}
|
}
|
||||||
if st.sizeIdx == 0 {
|
if st.SizeIdx == 0 {
|
||||||
return 0, &IndexError{} // ("already at first index")
|
return 0, &IndexError{} // ("already at first index")
|
||||||
}
|
}
|
||||||
st.sizeIdx -= 1
|
st.SizeIdx -= 1
|
||||||
s, idx := st.Where()
|
s, idx := st.Where()
|
||||||
log.Printf("previous page for %s: %v", s, idx)
|
log.Printf("previous page for %s: %v", s, idx)
|
||||||
return st.sizeIdx, nil
|
return st.SizeIdx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sides informs the caller which index page options will currently succeed.
|
// Sides informs the caller which index page options will currently succeed.
|
||||||
//
|
//
|
||||||
// Two values are returned, for the "next" and "previous" options in that order. A false value means the option is not available in the current state.
|
// Two values are returned, for the "next" and "previous" options in that order. A false value means the option is not available in the current state.
|
||||||
func(st *State) Sides() (bool, bool) {
|
func(st *State) Sides() (bool, bool) {
|
||||||
if len(st.execPath) == 0 {
|
if len(st.ExecPath) == 0 {
|
||||||
return false, false
|
return false, false
|
||||||
}
|
}
|
||||||
next := true
|
next := true
|
||||||
log.Printf("sides %v", st.sizeIdx)
|
log.Printf("sides %v", st.SizeIdx)
|
||||||
if st.sizeIdx == 0 {
|
if st.SizeIdx == 0 {
|
||||||
return next, false
|
return next, false
|
||||||
}
|
}
|
||||||
return next, true
|
return next, true
|
||||||
@ -242,18 +241,18 @@ func(st *State) Sides() (bool, bool) {
|
|||||||
//
|
//
|
||||||
// Fails if first Down() was never called.
|
// Fails if first Down() was never called.
|
||||||
func(st *State) Top() (bool, error) {
|
func(st *State) Top() (bool, error) {
|
||||||
if len(st.execPath) == 0 {
|
if len(st.ExecPath) == 0 {
|
||||||
return false, fmt.Errorf("state root node not yet defined")
|
return false, fmt.Errorf("state root node not yet defined")
|
||||||
}
|
}
|
||||||
return len(st.execPath) == 1, nil
|
return len(st.ExecPath) == 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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) error {
|
func(st *State) Down(input string) error {
|
||||||
st.execPath = append(st.execPath, input)
|
st.ExecPath = append(st.ExecPath, input)
|
||||||
st.sizeIdx = 0
|
st.SizeIdx = 0
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,29 +264,29 @@ func(st *State) Down(input string) error {
|
|||||||
//
|
//
|
||||||
// Fails if called at top frame.
|
// Fails if called at top frame.
|
||||||
func(st *State) Up() (string, error) {
|
func(st *State) Up() (string, error) {
|
||||||
l := len(st.execPath)
|
l := len(st.ExecPath)
|
||||||
if l == 0 {
|
if l == 0 {
|
||||||
return "", fmt.Errorf("exit called beyond top frame")
|
return "", fmt.Errorf("exit called beyond top frame")
|
||||||
}
|
}
|
||||||
log.Printf("execpath before %v", st.execPath)
|
log.Printf("execpath before %v", st.ExecPath)
|
||||||
st.execPath = st.execPath[:l-1]
|
st.ExecPath = st.ExecPath[:l-1]
|
||||||
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.sizeIdx = 0
|
st.SizeIdx = 0
|
||||||
log.Printf("execpath after %v", st.execPath)
|
log.Printf("execpath after %v", st.ExecPath)
|
||||||
return sym, nil
|
return sym, 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.execPath)-1)
|
return uint8(len(st.ExecPath)-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 {
|
||||||
st.code = append(st.code, b...)
|
st.Code = append(st.Code, b...)
|
||||||
log.Printf("code changed to 0x%x", b)
|
log.Printf("code changed to 0x%x", b)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -295,13 +294,13 @@ func(st *State) AppendCode(b []byte) error {
|
|||||||
// SetCode replaces the current bytecode with the given bytecode.
|
// SetCode replaces the current bytecode with the given bytecode.
|
||||||
func(st *State) SetCode(b []byte) {
|
func(st *State) SetCode(b []byte) {
|
||||||
log.Printf("code set to 0x%x", b)
|
log.Printf("code set to 0x%x", b)
|
||||||
st.code = b
|
st.Code = b
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the remaning cached bytecode
|
// Get the remaning cached bytecode
|
||||||
func(st *State) GetCode() ([]byte, error) {
|
func(st *State) GetCode() ([]byte, error) {
|
||||||
b := st.code
|
b := st.Code
|
||||||
st.code = []byte{}
|
st.Code = []byte{}
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,5 +327,5 @@ func(st *State) Reset() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func(st State) String() string {
|
func(st State) String() string {
|
||||||
return fmt.Sprintf("path: %s", strings.Join(st.execPath, "/"))
|
return fmt.Sprintf("idx %v path: %s", st.SizeIdx, strings.Join(st.ExecPath, "/"))
|
||||||
}
|
}
|
||||||
|
@ -8,20 +8,20 @@ import (
|
|||||||
// Check creation
|
// Check creation
|
||||||
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.Fatalf("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.Fatalf("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.Fatalf("invalid state flag length: %v", len(st.Flags))
|
t.Fatalf("invalid state flag length: %v", len(st.flags))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStateFlags(t *testing.T) {
|
func TestStateflags(t *testing.T) {
|
||||||
st := NewState(9)
|
st := NewState(9)
|
||||||
v, err := st.GetFlag(2)
|
v, err := st.GetFlag(2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -98,8 +98,8 @@ func TestStateFlags(t *testing.T) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("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.Fatalf("Expected 0x040401, got %v", st.Flags[:3])
|
t.Fatalf("Expected 0x040401, got %v", st.flags[:3])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user