go-vise

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

state.go (10884B)


      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 }
     46 
     47 // number of bytes necessary to represent a bitfield of the given size.
     48 func toByteSize(BitSize uint32) uint8 {
     49 	if BitSize == 0 {
     50 		return 0
     51 	}
     52 	n := BitSize % 8
     53 	if n > 0 {
     54 		BitSize += (8 - n)
     55 	}
     56 	return uint8(BitSize / 8)
     57 }
     58 
     59 // Invalidate marks a state as invalid.
     60 //
     61 // An invalid state should not be persisted or propagated
     62 func(st *State) Invalidate() {
     63 	st.invalid = true
     64 }
     65 
     66 // Invalid returns true if state is invalid.
     67 //
     68 // An invalid state should not be persisted or propagated
     69 func(st *State) Invalid() bool {
     70 	return st.invalid
     71 }
     72 
     73 //// Retrieve the state of a state flag
     74 //func getFlag(bitIndex uint32, bitField []byte) bool {
     75 //	byteIndex := bitIndex / 8
     76 //	localBitIndex := bitIndex % 8
     77 //	b := bitField[byteIndex]
     78 //	return (b & (1 << localBitIndex)) > 0
     79 //}
     80 
     81 // NewState creates a new State object with BitSize number of error condition states in ADDITION to the 8 builtin flags.
     82 func NewState(BitSize uint32) *State {
     83 	st := &State{
     84 		BitSize: BitSize + 8,
     85 	}
     86 	byteSize := toByteSize(BitSize + 8)
     87 	if byteSize > 0 {
     88 		st.Flags = make([]byte, byteSize) 
     89 	} else {
     90 		st.Flags = []byte{}
     91 	}
     92 	return st
     93 }
     94 
     95 // UseDebug enables rendering of registered string values of state flags in the string representation.
     96 func(st *State) UseDebug() {
     97 	st.debug = true
     98 }
     99 
    100 // SetFlag sets the flag at the given bit field index
    101 //
    102 // Returns true if bit state was changed.
    103 //
    104 // Fails if bitindex is out of range.
    105 func(st *State) SetFlag(bitIndex uint32) bool {
    106 	if bitIndex + 1 > st.BitSize {
    107 		panic(fmt.Sprintf("bit index %v is out of range of bitfield size %v", bitIndex, st.BitSize))
    108 	}
    109 	r := getFlag(bitIndex, st.Flags)
    110 	if r {
    111 		return false
    112 	}
    113 	byteIndex := bitIndex / 8
    114 	localBitIndex := bitIndex % 8
    115 	b := st.Flags[byteIndex] 
    116 	st.Flags[byteIndex] = b | (1 << localBitIndex)
    117 	return true
    118 }
    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 // Next moves to the next sink page index.
    215 func(st *State) Next() (uint16, error) {
    216 	if len(st.ExecPath) == 0 {
    217 		return 0, fmt.Errorf("state root node not yet defined")
    218 	}
    219 	st.SizeIdx += 1
    220 	s, idx := st.Where()
    221 	logg.Debugf("next page", "location", s, "index", idx)
    222 	st.Moves += 1
    223 	return st.SizeIdx, nil
    224 }
    225 
    226 func(st *State) Same() {
    227 	st.Moves += 1
    228 }
    229 
    230 // Previous moves to the next sink page index.
    231 //
    232 // Fails if try to move beyond index 0.
    233 func(st *State) Previous() (uint16, error) {
    234 	if len(st.ExecPath) == 0 {
    235 		return 0, fmt.Errorf("state root node not yet defined")
    236 	}
    237 	if st.SizeIdx == 0 {
    238 		return 0, IndexError
    239 	}
    240 	st.SizeIdx -= 1
    241 	s, idx := st.Where()
    242 	logg.Debugf("previous page", "location", s, "index", idx)
    243 	st.Moves += 1
    244 	return st.SizeIdx, nil
    245 }
    246 
    247 // Sides informs the caller which index page options will currently succeed.
    248 //
    249 // 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.
    250 func(st *State) Sides() (bool, bool) {
    251 	if len(st.ExecPath) == 0 {
    252 		return false, false
    253 	}
    254 	next := true
    255 	logg.Tracef("sides", "index", st.SizeIdx)
    256 	if st.SizeIdx == 0 {
    257 		return next, false	
    258 	}
    259 	return next, true
    260 }
    261 
    262 // Top returns true if currently at topmode node.
    263 //
    264 // Fails if first Down() was never called.
    265 func(st *State) Top() (bool, error) {
    266 	if len(st.ExecPath) == 0 {
    267 		return false, fmt.Errorf("state root node not yet defined")
    268 	}
    269 	return len(st.ExecPath) == 1, nil
    270 }
    271 
    272 // Down adds the given symbol to the command stack.
    273 //
    274 // Clears mapping and sink.
    275 func(st *State) Down(input string) error {
    276 	var n uint16
    277 	l := len(st.ExecPath)
    278 	if l > MaxLevel {
    279 		panic("maxlevel")
    280 		return fmt.Errorf("max levels exceeded (%d)", n)
    281 	}
    282 	if l > 0 {
    283 		if st.ExecPath[l-1] == input {
    284 			s := fmt.Sprintf("down into same node as previous: %v -> '%s'", st.ExecPath, input)
    285 			panic(s)
    286 		}
    287 	}
    288 	st.ExecPath = append(st.ExecPath, input)
    289 	st.SizeIdx = 0
    290 	st.Moves += 1
    291 	return nil
    292 }
    293 
    294 // Up removes the latest symbol to the command stack, and make the previous symbol current.
    295 //
    296 // Frees all symbols and associated values loaded at the previous stack level. Cache capacity is increased by the corresponding amount.
    297 //
    298 // Clears mapping and sink.
    299 //
    300 // Fails if called at top frame.
    301 func(st *State) Up() (string, error) {
    302 	l := len(st.ExecPath)
    303 	if l == 0 {
    304 		return "", fmt.Errorf("exit called beyond top frame")
    305 	}
    306 	logg.Tracef("execpath before", "path", st.ExecPath)
    307 	st.ExecPath = st.ExecPath[:l-1]
    308 	sym := ""
    309 	if len(st.ExecPath) > 0 {
    310 		sym = st.ExecPath[len(st.ExecPath)-1]
    311 	}
    312 	st.SizeIdx = 0
    313 	logg.Tracef("execpath after", "path", st.ExecPath)
    314 	st.Moves += 1
    315 	return sym, nil
    316 }
    317 
    318 // Depth returns the current call stack depth.
    319 func(st *State) Depth() int {
    320 	return len(st.ExecPath)-1
    321 }
    322 
    323 // Appendcode adds the given bytecode to the end of the existing code.
    324 func(st *State) AppendCode(b []byte) error {
    325 	st.Code = append(st.Code, b...)
    326 	logg.Debugf("code changed (append)", "code", b)
    327 	return nil
    328 }
    329 
    330 // SetCode replaces the current bytecode with the given bytecode.
    331 func(st *State) SetCode(b []byte) {
    332 	logg.Debugf("code changed (set)", "code", b)
    333 	st.Code = b
    334 }
    335 
    336 // Get the remaning cached bytecode
    337 func(st *State) GetCode() ([]byte, error) {
    338 	b := st.Code
    339 	st.Code = []byte{}
    340 	return b, nil
    341 }
    342 
    343 // GetInput gets the most recent client input.
    344 func(st *State) GetInput() ([]byte, error) {
    345 	if st.input == nil {
    346 		return nil, fmt.Errorf("no input has been set")
    347 	}
    348 	return st.input, nil
    349 }
    350 
    351 // SetInput is used to record the latest client input.
    352 func(st *State) SetInput(input []byte) error {
    353 	l := len(input)
    354 	if l > INPUT_LIMIT {
    355 		return fmt.Errorf("input size %v too large (limit %v)", l, 255)
    356 	}
    357 	st.input = input
    358 	return nil
    359 }
    360 
    361 // Reset re-initializes the state to run from top node with accumulated client state.
    362 func(st *State) Restart() error {
    363 	var err error
    364 	if len(st.ExecPath) == 0 {
    365 		return fmt.Errorf("Restart called but no root set")
    366 	}
    367 	st.resetBaseFlags()
    368 	st.Moves = 0
    369 	st.SizeIdx = 0
    370 	st.input = []byte{}
    371 	st.ExecPath = st.ExecPath[:1]
    372 	return err
    373 }
    374 
    375 // SetLanguage validates and sets language according to the given ISO639 language code.
    376 func(st *State) SetLanguage(code string) error {
    377 	if code == "" {
    378 		st.Language = nil
    379 	}
    380 	l, err := lang.LanguageFromCode(code)
    381 	if err != nil {
    382 		return err
    383 	}
    384 	st.Language = &l
    385 	logg.Infof("language set", "language", l)
    386 	return nil
    387 }
    388 
    389 func(st *State) CloneEmpty() *State {
    390 	flagCount := st.BitSize - 8
    391 	return NewState(flagCount)
    392 }
    393 
    394 // String implements String interface
    395 func(st *State) String() string {
    396 	var flags string
    397 	if st.debug {
    398 		flags = FlagDebugger.AsString(st.Flags, st.BitSize - 8)
    399 	} else {
    400 		flags = fmt.Sprintf("0x%x", st.Flags)
    401 	}
    402 	var lang string
    403 	if st.Language == nil {
    404 		lang = "(default)"
    405 	} else {
    406 		lang = fmt.Sprintf("%s", *st.Language)
    407 	}
    408 	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)
    409 }
    410 
    411 // initializes all flags not in control of client.
    412 func(st *State) resetBaseFlags() {
    413 	st.Flags[0] = 0
    414 }