go-vise

Constrained Size Output Virtual Machine
Info | Log | Files | Refs | README | LICENSE

state.go (11330B)


      1 package state
      2 
      3 import (
      4 	"fmt"
      5 	"strings"
      6 
      7 	"git.defalsify.org/vise.git/lang"
      8 )
      9 
     10 const (
     11 	INPUT_LIMIT = 255
     12 )
     13 
     14 var (
     15 	IndexError = fmt.Errorf("already at first index")
     16 	MaxLevel   = 128
     17 )
     18 
     19 // State holds the command stack, error condition of a unique execution session.
     20 //
     21 // It also holds cached values for all results of executed symbols.
     22 //
     23 // Cached values are linked to the command stack level it which they were loaded. When they go out of scope they are freed.
     24 //
     25 // It can hold a single argument, which is freed once it is read.
     26 //
     27 // Values must be mapped to a level in order to be available for retrieval and count towards size.
     28 //
     29 // 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.
     30 //
     31 // Symbol keys do not count towards cache size limitations.
     32 //
     33 // 8 first flags are reserved.
     34 type State struct {
     35 	Code     []byte         // Pending bytecode to execute
     36 	ExecPath []string       // Command symbols stack
     37 	BitSize  uint32         // Size of (32-bit capacity) bit flag byte array
     38 	SizeIdx  uint16         // Lateral page browse index in current frame
     39 	Flags    []byte         // Error state
     40 	Moves    uint32         // Number of times navigation has been performed
     41 	Language *lang.Language // Language selector for rendering
     42 	input    []byte         // Last input
     43 	debug    bool           // Make string representation more human friendly
     44 	invalid  bool
     45 	lastMove uint8
     46 }
     47 
     48 // number of bytes necessary to represent a bitfield of the given size.
     49 func toByteSize(BitSize uint32) uint8 {
     50 	if BitSize == 0 {
     51 		return 0
     52 	}
     53 	n := BitSize % 8
     54 	if n > 0 {
     55 		BitSize += (8 - n)
     56 	}
     57 	return uint8(BitSize / 8)
     58 }
     59 
     60 // Invalidate marks a state as invalid.
     61 //
     62 // An invalid state should not be persisted or propagated
     63 func (st *State) Invalidate() {
     64 	st.invalid = true
     65 }
     66 
     67 // Invalid returns true if state is invalid.
     68 //
     69 // An invalid state should not be persisted or propagated
     70 func (st *State) Invalid() bool {
     71 	return st.invalid
     72 }
     73 
     74 //// Retrieve the state of a state flag
     75 //func getFlag(bitIndex uint32, bitField []byte) bool {
     76 //	byteIndex := bitIndex / 8
     77 //	localBitIndex := bitIndex % 8
     78 //	b := bitField[byteIndex]
     79 //	return (b & (1 << localBitIndex)) > 0
     80 //}
     81 
     82 // NewState creates a new State object with BitSize number of error condition states in ADDITION to the 8 builtin flags.
     83 func NewState(BitSize uint32) *State {
     84 	st := &State{
     85 		BitSize: BitSize + 8,
     86 	}
     87 	byteSize := toByteSize(BitSize + 8)
     88 	if byteSize > 0 {
     89 		st.Flags = make([]byte, byteSize)
     90 	} else {
     91 		st.Flags = []byte{}
     92 	}
     93 	return st
     94 }
     95 
     96 // UseDebug enables rendering of registered string values of state flags in the string representation.
     97 func (st *State) UseDebug() {
     98 	st.debug = true
     99 }
    100 
    101 // SetFlag sets the flag at the given bit field index
    102 //
    103 // Returns true if bit state was changed.
    104 //
    105 // Fails if bitindex is out of range.
    106 func (st *State) SetFlag(bitIndex uint32) bool {
    107 	if bitIndex+1 > st.BitSize {
    108 		panic(fmt.Sprintf("bit index %v is out of range of bitfield size %v", bitIndex, st.BitSize))
    109 	}
    110 	r := getFlag(bitIndex, st.Flags)
    111 	if r {
    112 		return false
    113 	}
    114 	byteIndex := bitIndex / 8
    115 	localBitIndex := bitIndex % 8
    116 	b := st.Flags[byteIndex]
    117 	st.Flags[byteIndex] = b | (1 << localBitIndex)
    118 	return true
    119 }
    120 
    121 // ResetFlag resets the flag at the given bit field index.
    122 //
    123 // Returns true if bit state was changed.
    124 //
    125 // Fails if bitindex is out of range.
    126 func (st *State) ResetFlag(bitIndex uint32) bool {
    127 	if bitIndex+1 > st.BitSize {
    128 		panic(fmt.Sprintf("bit index %v is out of range of bitfield size %v", bitIndex, st.BitSize))
    129 	}
    130 	r := getFlag(bitIndex, st.Flags)
    131 	if !r {
    132 		return false
    133 	}
    134 	byteIndex := bitIndex / 8
    135 	localBitIndex := bitIndex % 8
    136 	b := st.Flags[byteIndex]
    137 	st.Flags[byteIndex] = b & (^(1 << localBitIndex))
    138 	return true
    139 }
    140 
    141 // GetFlag returns the state of the flag at the given bit field index.
    142 //
    143 // Fails if bit field index is out of range.
    144 func (st *State) GetFlag(bitIndex uint32) bool {
    145 	if bitIndex+1 > st.BitSize {
    146 		panic(fmt.Sprintf("bit index %v is out of range of bitfield size %v", bitIndex, st.BitSize))
    147 	}
    148 	return getFlag(bitIndex, st.Flags)
    149 }
    150 
    151 // FlagBitSize reports the amount of bits available in the bit field index.
    152 func (st *State) FlagBitSize() uint32 {
    153 	return st.BitSize
    154 }
    155 
    156 // FlagBitSize reports the amount of bits available in the bit field index.
    157 func (st *State) FlagByteSize() uint8 {
    158 	return uint8(len(st.Flags))
    159 }
    160 
    161 // MatchFlag matches the current state of the given flag.
    162 //
    163 // The flag is specified given its bit index in the bit field.
    164 //
    165 // If matchSet is set, a positive result will be returned if the flag is set.
    166 func (st *State) MatchFlag(sig uint32, matchSet bool) bool {
    167 	r := st.GetFlag(sig)
    168 	return matchSet == r
    169 }
    170 
    171 // GetIndex scans a byte slice in same order as in storage, and returns the index of the first set bit.
    172 //
    173 // If the given byte slice is too small for the bit field bitsize, the check will terminate at end-of-data without error.
    174 func (st *State) GetIndex(flags []byte) bool {
    175 	var globalIndex uint32
    176 	if st.BitSize == 0 {
    177 		return false
    178 	}
    179 	if len(flags) == 0 {
    180 		return false
    181 	}
    182 	var byteIndex uint8
    183 	var localIndex uint8
    184 	l := uint8(len(flags))
    185 	var i uint32
    186 	for i = 0; i < st.BitSize; i++ {
    187 		testVal := flags[byteIndex] & (1 << localIndex)
    188 		if (testVal & st.Flags[byteIndex]) > 0 {
    189 			return true
    190 		}
    191 		globalIndex += 1
    192 		if globalIndex%8 == 0 {
    193 			byteIndex += 1
    194 			localIndex = 0
    195 			if byteIndex > (l - 1) {
    196 				return false
    197 			}
    198 		} else {
    199 			localIndex += 1
    200 		}
    201 	}
    202 	return false
    203 }
    204 
    205 // Where returns the current active rendering symbol.
    206 func (st *State) Where() (string, uint16) {
    207 	if len(st.ExecPath) == 0 {
    208 		return "", 0
    209 	}
    210 	l := len(st.ExecPath)
    211 	return st.ExecPath[l-1], st.SizeIdx
    212 }
    213 
    214 // Lateral returns true if the last state move was Next() or Previous()
    215 func (st *State) Lateral() bool {
    216 	return st.lastMove&6 > 0
    217 }
    218 
    219 // Back returns true if the last state move was Up()
    220 func (st *State) Back() bool {
    221 	return st.lastMove&1 > 0
    222 }
    223 
    224 // Next moves to the next sink page index.
    225 func (st *State) Next() (uint16, error) {
    226 	if len(st.ExecPath) == 0 {
    227 		return 0, fmt.Errorf("state root node not yet defined")
    228 	}
    229 	st.SizeIdx += 1
    230 	s, idx := st.Where()
    231 	logg.Debugf("next page", "location", s, "index", idx)
    232 	st.Moves += 1
    233 	st.lastMove = 2
    234 	return st.SizeIdx, nil
    235 }
    236 
    237 func (st *State) Same() {
    238 	st.Moves += 1
    239 }
    240 
    241 // Previous moves to the next sink page index.
    242 //
    243 // Fails if try to move beyond index 0.
    244 func (st *State) Previous() (uint16, error) {
    245 	if len(st.ExecPath) == 0 {
    246 		return 0, fmt.Errorf("state root node not yet defined")
    247 	}
    248 	if st.SizeIdx == 0 {
    249 		return 0, IndexError
    250 	}
    251 	st.SizeIdx -= 1
    252 	s, idx := st.Where()
    253 	logg.Debugf("previous page", "location", s, "index", idx)
    254 	st.Moves += 1
    255 	st.lastMove = 4
    256 	return st.SizeIdx, nil
    257 }
    258 
    259 // Sides informs the caller which index page options will currently succeed.
    260 //
    261 // 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.
    262 func (st *State) Sides() (bool, bool) {
    263 	if len(st.ExecPath) == 0 {
    264 		return false, false
    265 	}
    266 	next := true
    267 	logg.Tracef("sides", "index", st.SizeIdx)
    268 	if st.SizeIdx == 0 {
    269 		return next, false
    270 	}
    271 	return next, true
    272 }
    273 
    274 // Top returns true if currently at topmode node.
    275 //
    276 // Fails if first Down() was never called.
    277 func (st *State) Top() (bool, error) {
    278 	if len(st.ExecPath) == 0 {
    279 		return false, fmt.Errorf("state root node not yet defined")
    280 	}
    281 	return len(st.ExecPath) == 1, nil
    282 }
    283 
    284 // Down adds the given symbol to the command stack.
    285 //
    286 // Clears mapping and sink.
    287 func (st *State) Down(input string) error {
    288 	var n uint16
    289 	l := len(st.ExecPath)
    290 	if l > MaxLevel {
    291 		panic("maxlevel")
    292 		return fmt.Errorf("max levels exceeded (%d)", n)
    293 	}
    294 	if l > 0 {
    295 		if st.ExecPath[l-1] == input {
    296 			s := fmt.Sprintf("down into same node as previous: %v -> '%s'", st.ExecPath, input)
    297 			panic(s)
    298 		}
    299 	}
    300 	st.ExecPath = append(st.ExecPath, input)
    301 	st.SizeIdx = 0
    302 	st.Moves += 1
    303 	st.lastMove = 0
    304 	return nil
    305 }
    306 
    307 // Up removes the latest symbol to the command stack, and make the previous symbol current.
    308 //
    309 // Frees all symbols and associated values loaded at the previous stack level. Cache capacity is increased by the corresponding amount.
    310 //
    311 // Clears mapping and sink.
    312 //
    313 // Fails if called at top frame.
    314 func (st *State) Up() (string, error) {
    315 	l := len(st.ExecPath)
    316 	if l == 0 {
    317 		return "", fmt.Errorf("exit called beyond top frame")
    318 	}
    319 	logg.Tracef("execpath before", "path", st.ExecPath)
    320 	st.ExecPath = st.ExecPath[:l-1]
    321 	sym := ""
    322 	if len(st.ExecPath) > 0 {
    323 		sym = st.ExecPath[len(st.ExecPath)-1]
    324 	}
    325 	st.SizeIdx = 0
    326 	logg.Tracef("execpath after", "path", st.ExecPath)
    327 	st.Moves += 1
    328 	st.lastMove = 1
    329 	return sym, nil
    330 }
    331 
    332 // Depth returns the current call stack depth.
    333 func (st *State) Depth() int {
    334 	return len(st.ExecPath) - 1
    335 }
    336 
    337 // Appendcode adds the given bytecode to the end of the existing code.
    338 func (st *State) AppendCode(b []byte) error {
    339 	st.Code = append(st.Code, b...)
    340 	logg.Debugf("code changed (append)", "code", b)
    341 	return nil
    342 }
    343 
    344 // SetCode replaces the current bytecode with the given bytecode.
    345 func (st *State) SetCode(b []byte) {
    346 	logg.Debugf("code changed (set)", "code", b)
    347 	st.Code = b
    348 }
    349 
    350 // Get the remaning cached bytecode
    351 func (st *State) GetCode() ([]byte, error) {
    352 	b := st.Code
    353 	st.Code = []byte{}
    354 	return b, nil
    355 }
    356 
    357 // GetInput gets the most recent client input.
    358 func (st *State) GetInput() ([]byte, error) {
    359 	if st.input == nil {
    360 		return nil, fmt.Errorf("no input has been set")
    361 	}
    362 	return st.input, nil
    363 }
    364 
    365 // SetInput is used to record the latest client input.
    366 func (st *State) SetInput(input []byte) error {
    367 	l := len(input)
    368 	if l > INPUT_LIMIT {
    369 		return fmt.Errorf("input size %v too large (limit %v)", l, 255)
    370 	}
    371 	st.input = input
    372 	return nil
    373 }
    374 
    375 // Reset re-initializes the state to run from top node with accumulated client state.
    376 func (st *State) Restart() error {
    377 	var err error
    378 	if len(st.ExecPath) == 0 {
    379 		return fmt.Errorf("Restart called but no root set")
    380 	}
    381 	st.resetBaseFlags()
    382 	st.Moves = 0
    383 	st.SizeIdx = 0
    384 	st.input = []byte{}
    385 	st.ExecPath = st.ExecPath[:1]
    386 	st.lastMove = 0
    387 	return err
    388 }
    389 
    390 // SetLanguage validates and sets language according to the given ISO639 language code.
    391 func (st *State) SetLanguage(code string) error {
    392 	if code == "" {
    393 		st.Language = nil
    394 	}
    395 	l, err := lang.LanguageFromCode(code)
    396 	if err != nil {
    397 		return err
    398 	}
    399 	st.Language = &l
    400 	logg.Infof("language set", "language", l)
    401 	return nil
    402 }
    403 
    404 func (st *State) CloneEmpty() *State {
    405 	flagCount := st.BitSize - 8
    406 	return NewState(flagCount)
    407 }
    408 
    409 // String implements String interface
    410 func (st *State) String() string {
    411 	var flags string
    412 	if st.debug {
    413 		flags = FlagDebugger.AsString(st.Flags, st.BitSize-8)
    414 	} else {
    415 		flags = fmt.Sprintf("0x%x", st.Flags)
    416 	}
    417 	var lang string
    418 	if st.Language == nil {
    419 		lang = "(default)"
    420 	} else {
    421 		lang = fmt.Sprintf("%s", *st.Language)
    422 	}
    423 	return fmt.Sprintf("state @%p moves: %v idx: %v flags: %s path: %s lang: %s", st, st.Moves, st.SizeIdx, flags, strings.Join(st.ExecPath, "/"), lang)
    424 }
    425 
    426 // initializes all flags not in control of client.
    427 func (st *State) resetBaseFlags() {
    428 	st.Flags[0] = 0
    429 }