vise/state/state.go

353 lines
9.1 KiB
Go
Raw Permalink Normal View History

2023-03-31 11:52:04 +02:00
package state
import (
2023-03-31 13:56:11 +02:00
"fmt"
2023-03-31 14:06:59 +02:00
"log"
"strings"
2023-03-31 11:52:04 +02:00
)
2023-04-12 09:42:37 +02:00
type IndexError struct {
}
func(err *IndexError) Error() string {
return fmt.Sprintf("already at first index")
}
// State holds the command stack, error condition of a unique execution session.
//
// It also holds cached values for all results of executed symbols.
//
// Cached values are linked to the command stack level it which they were loaded. When they go out of scope they are freed.
//
// Values must be mapped to a level in order to be available for retrieval and count towards size
//
// It can hold a single argument, which is freed once it is read
//
// Symbols are loaded with individual size limitations. The limitations apply if a load symbol is updated. Symbols may be added with a 0-value for limits, called a "sink." If mapped, the sink will consume all net remaining size allowance unused by other symbols. Only one sink may be mapped per level.
//
// Symbol keys do not count towards cache size limitations.
2023-04-01 09:36:26 +02:00
//
// 8 first flags are reserved.
2023-03-31 11:52:04 +02:00
type State struct {
2023-04-13 00:42:36 +02:00
Code []byte // Pending bytecode to execute
ExecPath []string // Command symbols stack
BitSize uint32 // size of (32-bit capacity) bit flag byte array
SizeIdx uint16
2023-04-13 01:38:33 +02:00
Flags []byte // Error state
Moves uint32 // Number of times navigation has been performed
input []byte // Last input
2023-03-31 11:52:04 +02:00
}
2023-04-08 17:09:10 +02:00
// number of bytes necessary to represent a bitfield of the given size.
2023-04-13 00:42:36 +02:00
func toByteSize(BitSize uint32) uint8 {
if BitSize == 0 {
2023-04-01 09:36:26 +02:00
return 0
}
2023-04-13 00:42:36 +02:00
n := BitSize % 8
2023-03-31 11:52:04 +02:00
if n > 0 {
2023-04-13 00:42:36 +02:00
BitSize += (8 - n)
2023-03-31 11:52:04 +02:00
}
2023-04-13 00:42:36 +02:00
return uint8(BitSize / 8)
2023-04-01 09:36:26 +02:00
}
2023-03-31 11:52:04 +02:00
2023-04-01 09:36:26 +02:00
// Retrieve the state of a state flag
func getFlag(bitIndex uint32, bitField []byte) bool {
byteIndex := bitIndex / 8
localBitIndex := bitIndex % 8
b := bitField[byteIndex]
return (b & (1 << localBitIndex)) > 0
}
2023-04-13 00:42:36 +02:00
// 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 {
2023-03-31 16:24:29 +02:00
st := State{
2023-04-13 00:42:36 +02:00
BitSize: BitSize + 8,
2023-03-31 11:52:04 +02:00
}
2023-04-13 00:42:36 +02:00
byteSize := toByteSize(BitSize + 8)
2023-04-01 09:36:26 +02:00
if byteSize > 0 {
2023-04-13 01:38:33 +02:00
st.Flags = make([]byte, byteSize)
2023-04-01 09:36:26 +02:00
} else {
2023-04-13 01:38:33 +02:00
st.Flags = []byte{}
2023-04-01 09:36:26 +02:00
}
2023-03-31 16:24:29 +02:00
return st
2023-03-31 11:52:04 +02:00
}
2023-04-01 00:25:05 +02:00
// SetFlag sets the flag at the given bit field index
2023-04-01 00:17:44 +02:00
//
// Returns true if bit state was changed.
//
// Fails if bitindex is out of range.
func(st *State) SetFlag(bitIndex uint32) (bool, error) {
2023-04-13 00:42:36 +02:00
if bitIndex + 1 > st.BitSize {
return false, fmt.Errorf("bit index %v is out of range of bitfield size %v", bitIndex, st.BitSize)
2023-04-01 00:17:44 +02:00
}
2023-04-13 01:38:33 +02:00
r := getFlag(bitIndex, st.Flags)
2023-04-01 00:17:44 +02:00
if r {
return false, nil
}
byteIndex := bitIndex / 8
localBitIndex := bitIndex % 8
2023-04-13 01:38:33 +02:00
b := st.Flags[byteIndex]
st.Flags[byteIndex] = b | (1 << localBitIndex)
2023-04-01 00:17:44 +02:00
return true, nil
}
// ResetFlag resets the flag at the given bit field index.
//
// Returns true if bit state was changed.
//
// Fails if bitindex is out of range.
func(st *State) ResetFlag(bitIndex uint32) (bool, error) {
2023-04-13 00:42:36 +02:00
if bitIndex + 1 > st.BitSize {
return false, fmt.Errorf("bit index %v is out of range of bitfield size %v", bitIndex, st.BitSize)
2023-04-01 00:17:44 +02:00
}
2023-04-13 01:38:33 +02:00
r := getFlag(bitIndex, st.Flags)
2023-04-01 00:17:44 +02:00
if !r {
return false, nil
}
byteIndex := bitIndex / 8
localBitIndex := bitIndex % 8
2023-04-13 01:38:33 +02:00
b := st.Flags[byteIndex]
st.Flags[byteIndex] = b & (^(1 << localBitIndex))
2023-04-01 00:17:44 +02:00
return true, nil
}
// GetFlag returns the state of the flag at the given bit field index.
//
// Fails if bit field index is out of range.
func(st *State) GetFlag(bitIndex uint32) (bool, error) {
2023-04-13 00:42:36 +02:00
if bitIndex + 1 > st.BitSize {
return false, fmt.Errorf("bit index %v is out of range of bitfield size %v", bitIndex, st.BitSize)
2023-04-01 00:17:44 +02:00
}
2023-04-13 01:38:33 +02:00
return getFlag(bitIndex, st.Flags), nil
2023-04-01 09:36:26 +02:00
}
// FlagBitSize reports the amount of bits available in the bit field index.
func(st *State) FlagBitSize() uint32 {
2023-04-13 00:42:36 +02:00
return st.BitSize
2023-04-01 09:36:26 +02:00
}
2023-04-01 10:03:03 +02:00
// FlagBitSize reports the amount of bits available in the bit field index.
func(st *State) FlagByteSize() uint8 {
2023-04-13 01:38:33 +02:00
return uint8(len(st.Flags))
2023-04-01 10:03:03 +02:00
}
2023-04-06 11:08:40 +02:00
// MatchFlag matches the current state of the given flag.
//
// The flag is specified given its bit index in the bit field.
//
// If invertMatch is set, a positive result will be returned if the flag is not set.
func(st *State) MatchFlag(sig uint32, invertMatch bool) (bool, error) {
r, err := st.GetFlag(sig)
if err != nil {
return false, err
}
if invertMatch {
if !r {
return true, nil
}
} else if r {
return true, nil
}
return false, nil
}
2023-04-01 09:36:26 +02:00
// GetIndex scans a byte slice in same order as in storage, and returns the index of the first set bit.
//
// 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 {
var globalIndex uint32
2023-04-13 00:42:36 +02:00
if st.BitSize == 0 {
2023-04-01 09:36:26 +02:00
return false
}
if len(flags) == 0 {
return false
}
var byteIndex uint8
var localIndex uint8
l := uint8(len(flags))
var i uint32
2023-04-13 00:42:36 +02:00
for i = 0; i < st.BitSize; i++ {
2023-04-01 09:36:26 +02:00
testVal := flags[byteIndex] & (1 << localIndex)
2023-04-13 01:38:33 +02:00
if (testVal & st.Flags[byteIndex]) > 0 {
2023-04-01 09:36:26 +02:00
return true
}
globalIndex += 1
if globalIndex % 8 == 0 {
byteIndex += 1
localIndex = 0
if byteIndex > (l - 1) {
return false
}
} else {
localIndex += 1
}
}
return false
2023-04-01 00:17:44 +02:00
}
// Where returns the current active rendering symbol.
func(st *State) Where() (string, uint16) {
2023-04-13 00:42:36 +02:00
if len(st.ExecPath) == 0 {
return "", 0
}
2023-04-13 00:42:36 +02:00
l := len(st.ExecPath)
return st.ExecPath[l-1], st.SizeIdx
}
// Next moves to the next sink page index.
func(st *State) Next() (uint16, error) {
2023-04-13 00:42:36 +02:00
if len(st.ExecPath) == 0 {
2023-04-08 10:31:32 +02:00
return 0, fmt.Errorf("state root node not yet defined")
}
2023-04-13 00:42:36 +02:00
st.SizeIdx += 1
s, idx := st.Where()
log.Printf("next page for %s: %v", s, idx)
st.Moves += 1
2023-04-13 00:42:36 +02:00
return st.SizeIdx, nil
}
func(st *State) Same() {
st.Moves += 1
}
// Previous moves to the next sink page index.
//
// Fails if try to move beyond index 0.
func(st *State) Previous() (uint16, error) {
2023-04-13 00:42:36 +02:00
if len(st.ExecPath) == 0 {
2023-04-08 10:31:32 +02:00
return 0, fmt.Errorf("state root node not yet defined")
}
2023-04-13 00:42:36 +02:00
if st.SizeIdx == 0 {
2023-04-12 09:42:37 +02:00
return 0, &IndexError{} // ("already at first index")
}
2023-04-13 00:42:36 +02:00
st.SizeIdx -= 1
s, idx := st.Where()
log.Printf("previous page for %s: %v", s, idx)
st.Moves += 1
2023-04-13 00:42:36 +02:00
return st.SizeIdx, nil
}
// 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.
func(st *State) Sides() (bool, bool) {
2023-04-13 00:42:36 +02:00
if len(st.ExecPath) == 0 {
2023-04-08 10:31:32 +02:00
return false, false
}
next := true
2023-04-13 00:42:36 +02:00
log.Printf("sides %v", st.SizeIdx)
if st.SizeIdx == 0 {
return next, false
}
return next, true
}
2023-04-08 10:31:32 +02:00
// Top returns true if currently at topmode node.
//
// Fails if first Down() was never called.
func(st *State) Top() (bool, error) {
2023-04-13 00:42:36 +02:00
if len(st.ExecPath) == 0 {
2023-04-08 10:31:32 +02:00
return false, fmt.Errorf("state root node not yet defined")
}
2023-04-13 00:42:36 +02:00
return len(st.ExecPath) == 1, nil
2023-04-08 10:31:32 +02:00
}
// Down adds the given symbol to the command stack.
//
// Clears mapping and sink.
2023-04-08 17:09:10 +02:00
func(st *State) Down(input string) error {
2023-04-13 00:42:36 +02:00
st.ExecPath = append(st.ExecPath, input)
st.SizeIdx = 0
st.Moves += 1
2023-04-08 17:09:10 +02:00
return nil
2023-03-31 22:18:54 +02:00
}
// Up removes the latest symbol to the command stack, and make the previous symbol current.
//
// Frees all symbols and associated values loaded at the previous stack level. Cache capacity is increased by the corresponding amount.
//
// Clears mapping and sink.
//
// Fails if called at top frame.
func(st *State) Up() (string, error) {
2023-04-13 00:42:36 +02:00
l := len(st.ExecPath)
2023-04-08 17:09:10 +02:00
if l == 0 {
return "", fmt.Errorf("exit called beyond top frame")
2023-03-31 22:18:54 +02:00
}
2023-04-13 00:42:36 +02:00
log.Printf("execpath before %v", st.ExecPath)
st.ExecPath = st.ExecPath[:l-1]
sym := ""
2023-04-13 00:42:36 +02:00
if len(st.ExecPath) > 0 {
sym = st.ExecPath[len(st.ExecPath)-1]
}
2023-04-13 00:42:36 +02:00
st.SizeIdx = 0
log.Printf("execpath after %v", st.ExecPath)
st.Moves += 1
return sym, nil
2023-03-31 11:59:55 +02:00
}
// Depth returns the current call stack depth.
2023-03-31 15:04:08 +02:00
func(st *State) Depth() uint8 {
2023-04-13 00:42:36 +02:00
return uint8(len(st.ExecPath)-1)
}
2023-04-01 15:47:03 +02:00
// Appendcode adds the given bytecode to the end of the existing code.
func(st *State) AppendCode(b []byte) error {
2023-04-13 00:42:36 +02:00
st.Code = append(st.Code, b...)
2023-04-02 10:07:53 +02:00
log.Printf("code changed to 0x%x", b)
2023-04-01 15:47:03 +02:00
return nil
}
// SetCode replaces the current bytecode with the given bytecode.
func(st *State) SetCode(b []byte) {
2023-04-02 10:07:53 +02:00
log.Printf("code set to 0x%x", b)
2023-04-13 00:42:36 +02:00
st.Code = b
2023-04-01 15:47:03 +02:00
}
// Get the remaning cached bytecode
2023-04-01 15:47:03 +02:00
func(st *State) GetCode() ([]byte, error) {
2023-04-13 00:42:36 +02:00
b := st.Code
st.Code = []byte{}
2023-04-01 15:47:03 +02:00
return b, nil
}
// GetInput gets the most recent client input.
func(st *State) GetInput() ([]byte, error) {
if st.input == nil {
return nil, fmt.Errorf("no input has been set")
}
return st.input, nil
}
// SetInput is used to record the latest client input.
func(st *State) SetInput(input []byte) error {
l := len(input)
if l > 255 {
return fmt.Errorf("input size %v too large (limit %v)", l, 255)
}
st.input = input
return nil
}
// Reset re-initializes the state to run from top node with accumulated client state.
func(st *State) Restart() error {
st.resetBaseFlags()
st.Moves = 0
st.SizeIdx = 0
st.input = []byte{}
return nil
}
// String implements String interface
func(st State) String() string {
return fmt.Sprintf("moves %v idx %v flags: 0x%x path: %s", st.Moves, st.SizeIdx, st.Flags, strings.Join(st.ExecPath, "/"))
}
// initializes all flags not in control of client.
func(st *State) resetBaseFlags() {
st.Flags[0] = 0
}