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
)
2023-03-31 23:35:13 +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 {
2023-03-31 23:35:13 +02:00
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
2023-03-31 23:35:13 +02:00
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
}
2023-03-31 23:35:13 +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
2023-03-31 23:35:13 +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 {
2023-03-31 23:35:13 +02:00
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
2023-03-31 19:04:11 +02:00
}
2023-03-31 23:35:13 +02:00
// 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 ) {
2023-03-31 23:35:13 +02:00
if st . arg == nil {
2023-03-31 19:17:43 +02:00
return "" , fmt . Errorf ( "arg is not set" )
}
2023-03-31 23:35:13 +02:00
return * st . arg , nil
2023-03-31 19:04:11 +02:00
}
2023-03-31 23:35:13 +02:00
// 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 )
2023-03-31 23:35:13 +02:00
st . execPath = append ( st . execPath , input )
2023-03-31 22:18:54 +02:00
st . resetCurrent ( )
}
2023-03-31 23:35:13 +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.
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 ]
2023-03-31 23:35:13 +02:00
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
}
2023-03-31 23:35:13 +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 ) )
2023-03-31 23:35:13 +02:00
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
2023-03-31 23:35:13 +02:00
st . sizes [ key ] = sizeLimit
2023-03-31 16:03:54 +02:00
return nil
}
2023-03-31 23:35:13 +02:00
// 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 {
2023-03-31 23:35:13 +02:00
sizeLimit := st . sizes [ key ]
2023-03-31 21:42:13 +02:00
if st . sizes [ key ] > 0 {
l := uint16 ( len ( value ) )
2023-03-31 23:35:13 +02:00
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 ] = ""
2023-03-31 17:12:14 +02:00
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
}
2023-03-31 23:35:13 +02:00
// 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
}
2023-03-31 23:35:13 +02:00
// 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 ) )
}
2023-03-31 23:35:13 +02:00
// 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
2023-03-31 23:35:13 +02:00
// Val returns value for key
//
// Fails if key is not mapped.
2023-03-31 17:12:14 +02:00
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
}
2023-03-31 23:35:13 +02:00
// 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
}
2023-03-31 23:35:13 +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