vise/go/state/state.go

412 lines
11 KiB
Go
Raw 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"
2023-03-31 11:52:04 +02:00
)
// 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
//
// TODO factor out cache
2023-03-31 11:52:04 +02:00
type State struct {
Flags []byte // Error state
CacheSize uint32 // Total allowed cumulative size of values in cache
CacheUseSize uint32 // Currently used bytes by all values in cache
Cache []map[string]string // All loaded cache items
CacheMap map[string]string // Mapped
2023-04-01 11:58:02 +02:00
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.
2023-04-01 00:17:44 +02:00
sink *string // Sink symbol set for level
bitSize uint32 // size of (32-bit capacity) bit flag byte array
2023-03-31 22:08:06 +02:00
//sizeIdx uint16
2023-03-31 11:52:04 +02:00
}
2023-04-01 09:36:26 +02:00
func toByteSize(bitSize uint32) uint8 {
2023-03-31 11:52:04 +02:00
if bitSize == 0 {
2023-04-01 09:36:26 +02:00
return 0
}
2023-03-31 11:52:04 +02:00
n := bitSize % 8
if n > 0 {
bitSize += (8 - n)
}
2023-04-01 09:36:26 +02:00
return uint8(bitSize / 8)
}
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
}
// NewState creates a new State object with bitSize number of error condition states.
func NewState(bitSize uint32) State {
2023-03-31 16:24:29 +02:00
st := State{
2023-03-31 11:52:04 +02:00
CacheSize: 0,
CacheUseSize: 0,
2023-04-01 00:17:44 +02:00
bitSize: bitSize,
2023-03-31 11:52:04 +02:00
}
2023-04-01 09:36:26 +02:00
byteSize := toByteSize(bitSize)
if byteSize > 0 {
st.Flags = make([]byte, byteSize)
} else {
st.Flags = []byte{}
}
2023-03-31 16:24:29 +02:00
st.Down("")
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) {
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 09:36:26 +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
b := st.Flags[byteIndex]
st.Flags[byteIndex] = b | (1 << localBitIndex)
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) {
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 09:36:26 +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
b := st.Flags[byteIndex]
st.Flags[byteIndex] = b & (^(1 << localBitIndex))
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) {
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 09:36:26 +02:00
return getFlag(bitIndex, st.Flags), nil
}
// FlagBitSize reports the amount of bits available in the bit field index.
func(st *State) FlagBitSize() uint32 {
return st.bitSize
}
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 {
return uint8(len(st.Flags))
}
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
if st.bitSize == 0 {
return false
}
if len(flags) == 0 {
return false
}
var byteIndex uint8
var localIndex uint8
l := uint8(len(flags))
var i uint32
for i = 0; i < st.bitSize; i++ {
testVal := flags[byteIndex] & (1 << localIndex)
if (testVal & st.Flags[byteIndex]) > 0 {
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
}
// WithCacheSize applies a cumulative cache size limitation for all cached items.
2023-03-31 11:52:04 +02:00
func(st State) WithCacheSize(cacheSize uint32) State {
st.CacheSize = cacheSize
return st
}
2023-03-31 11:59:55 +02:00
// Where returns the current active rendering symbol.
func(st State) Where() string {
if len(st.execPath) == 0 {
return ""
}
l := len(st.execPath)
return st.execPath[l-1]
}
// PutArg adds the optional argument.
//
// Fails if arg already set.
2023-03-31 19:17:43 +02:00
func(st *State) PutArg(input string) error {
st.arg = &input
if st.arg != nil {
return fmt.Errorf("arg already set to %s", *st.arg)
}
2023-03-31 19:17:43 +02:00
return nil
}
// PopArg retrieves the optional argument. Will be freed upon retrieval.
//
// Fails if arg not set (or already freed).
2023-03-31 19:17:43 +02:00
func(st *State) PopArg() (string, error) {
if st.arg == nil {
2023-03-31 19:17:43 +02:00
return "", fmt.Errorf("arg is not set")
}
return *st.arg, nil
}
// Down adds the given symbol to the command stack.
//
// Clears mapping and sink.
2023-03-31 16:03:54 +02:00
func(st *State) Down(input string) {
2023-03-31 11:59:55 +02:00
m := make(map[string]string)
st.Cache = append(st.Cache, m)
2023-03-31 21:42:13 +02:00
st.sizes = make(map[string]uint16)
st.execPath = append(st.execPath, input)
2023-03-31 22:18:54 +02:00
st.resetCurrent()
}
// 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.
2023-03-31 22:18:54 +02:00
func(st *State) Up() error {
l := len(st.Cache)
if l == 0 {
return fmt.Errorf("exit called beyond top frame")
}
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]
2023-03-31 22:18:54 +02:00
st.resetCurrent()
return nil
2023-03-31 11:59:55 +02:00
}
// 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 {
2023-03-31 21:23:45 +02:00
l := uint16(len(value))
if l > sizeLimit {
return fmt.Errorf("value length %v exceeds value size limit %v", l, sizeLimit)
2023-03-31 21:23:45 +02:00
}
}
2023-03-31 15:04:08 +02:00
checkFrame := st.frameOf(key)
if checkFrame > -1 {
return fmt.Errorf("key %v already defined in frame %v", key, checkFrame)
}
sz := st.checkCapacity(value)
2023-03-31 13:56:11 +02:00
if sz == 0 {
return fmt.Errorf("Cache capacity exceeded %v of %v", st.CacheUseSize + sz, st.CacheSize)
}
2023-03-31 15:04:08 +02:00
log.Printf("add key %s value size %v", key, sz)
st.Cache[len(st.Cache)-1][key] = value
2023-03-31 13:56:11 +02:00
st.CacheUseSize += sz
st.sizes[key] = sizeLimit
2023-03-31 16:03:54 +02:00
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
2023-03-31 16:03:54 +02:00
func(st *State) Update(key string, value string) error {
sizeLimit := st.sizes[key]
2023-03-31 21:42:13 +02:00
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)
2023-03-31 21:42:13 +02:00
}
}
2023-03-31 16:03:54 +02:00
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
}
2023-03-31 16:03:54 +02:00
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)
}
2023-03-31 11:59:55 +02:00
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.
2023-03-31 22:08:06 +02:00
func(st *State) Map(key string) error {
2023-03-31 14:24:14 +02:00
m, err := st.Get()
if err != nil {
return err
}
2023-03-31 22:08:06 +02:00
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]
2023-03-31 14:24:14 +02:00
return nil
}
// Depth returns the current call stack depth.
2023-03-31 15:04:08 +02:00
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.
2023-03-31 11:59:55 +02:00
func(st *State) Get() (map[string]string, error) {
2023-03-31 15:04:08 +02:00
if len(st.Cache) == 0 {
return nil, fmt.Errorf("get at top frame")
}
2023-03-31 11:59:55 +02:00
return st.Cache[len(st.Cache)-1], nil
}
2023-03-31 13:56:11 +02:00
// Val returns value for key
//
// 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.
2023-03-31 16:03:54 +02:00
func(st *State) Reset() {
if len(st.Cache) == 0 {
return
}
2023-03-31 15:04:08 +02:00
st.Cache = st.Cache[:1]
st.CacheUseSize = 0
2023-03-31 16:03:54 +02:00
return
2023-03-31 15:04:08 +02:00
}
// Check returns true if a key already exists in the cache.
2023-03-31 15:04:08 +02:00
func(st *State) Check(key string) bool {
return st.frameOf(key) == -1
}
2023-03-31 22:08:06 +02:00
// Returns size used by values, and remaining size available
2023-03-31 21:42:13 +02:00
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]
}
2023-03-31 22:08:06 +02:00
r := uint32(l)
return r, uint32(c)-r
2023-03-31 21:42:13 +02:00
}
2023-03-31 16:03:54 +02:00
// return 0-indexed frame number where key is defined. -1 if not defined
2023-03-31 15:04:08 +02:00
func(st *State) frameOf(key string) int {
for i, m := range st.Cache {
for k, _ := range m {
if k == key {
return i
}
}
}
return -1
}
2023-03-31 16:03:54 +02:00
// bytes that will be added to cache use size for string
// returns 0 if capacity would be exceeded
2023-03-31 13:56:11 +02:00
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
}
2023-03-31 22:18:54 +02:00
2023-04-01 00:17:44 +02:00
// flush relveant properties for level change
2023-03-31 22:18:54 +02:00
func(st *State) resetCurrent() {
st.sink = nil
st.CacheMap = make(map[string]string)
}
2023-04-01 00:17:44 +02:00