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 }