Add persisted state engine runner
This commit is contained in:
parent
064418cb83
commit
a2d947e106
@ -21,7 +21,7 @@ func main() {
|
|||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
en := engine.NewSizedEngine(dir, uint32(size))
|
en := engine.NewSizedEngine(dir, uint32(size))
|
||||||
err := engine.Loop(&en, root, ctx, os.Stdin, os.Stdout)
|
err := engine.Loop(&en, os.Stdin, os.Stdout, ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "loop exited with error: %v", err)
|
fmt.Fprintf(os.Stderr, "loop exited with error: %v", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package engine
|
package engine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"git.defalsify.org/festive/cache"
|
"git.defalsify.org/festive/cache"
|
||||||
"git.defalsify.org/festive/resource"
|
"git.defalsify.org/festive/resource"
|
||||||
"git.defalsify.org/festive/state"
|
"git.defalsify.org/festive/state"
|
||||||
@ -11,7 +13,11 @@ func NewDefaultEngine(dir string) Engine {
|
|||||||
st := state.NewState(0)
|
st := state.NewState(0)
|
||||||
rs := resource.NewFsResource(dir)
|
rs := resource.NewFsResource(dir)
|
||||||
ca := cache.NewCache()
|
ca := cache.NewCache()
|
||||||
return NewEngine(Config{}, &st, &rs, ca)
|
cfg := Config{
|
||||||
|
Root: "root",
|
||||||
|
}
|
||||||
|
ctx := context.TODO()
|
||||||
|
return NewEngine(cfg, &st, &rs, ca, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSizedEngine is a convenience function to instantiate a filesystem-backed engine with a specified output constraint.
|
// NewSizedEngine is a convenience function to instantiate a filesystem-backed engine with a specified output constraint.
|
||||||
@ -21,6 +27,8 @@ func NewSizedEngine(dir string, size uint32) Engine {
|
|||||||
ca := cache.NewCache()
|
ca := cache.NewCache()
|
||||||
cfg := Config{
|
cfg := Config{
|
||||||
OutputSize: size,
|
OutputSize: size,
|
||||||
|
Root: "root",
|
||||||
}
|
}
|
||||||
return NewEngine(cfg, &st, &rs, ca)
|
ctx := context.TODO()
|
||||||
|
return NewEngine(cfg, &st, &rs, ca, ctx)
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,10 @@ import (
|
|||||||
// Config globally defines behavior of all components driven by the engine.
|
// Config globally defines behavior of all components driven by the engine.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
OutputSize uint32 // Maximum size of output from a single rendered page
|
OutputSize uint32 // Maximum size of output from a single rendered page
|
||||||
|
SessionId string
|
||||||
|
Root string
|
||||||
|
FlagCount uint32
|
||||||
|
CacheSize uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
// Engine is an execution engine that handles top-level errors when running client inputs against code in the bytecode buffer.
|
// Engine is an execution engine that handles top-level errors when running client inputs against code in the bytecode buffer.
|
||||||
@ -24,10 +28,11 @@ type Engine struct {
|
|||||||
rs resource.Resource
|
rs resource.Resource
|
||||||
ca cache.Memory
|
ca cache.Memory
|
||||||
vm *vm.Vm
|
vm *vm.Vm
|
||||||
|
initd bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEngine creates a new Engine
|
// NewEngine creates a new Engine
|
||||||
func NewEngine(cfg Config, st *state.State, rs resource.Resource, ca cache.Memory) Engine {
|
func NewEngine(cfg Config, st *state.State, rs resource.Resource, ca cache.Memory, ctx context.Context) Engine {
|
||||||
var szr *render.Sizer
|
var szr *render.Sizer
|
||||||
if cfg.OutputSize > 0 {
|
if cfg.OutputSize > 0 {
|
||||||
szr = render.NewSizer(cfg.OutputSize)
|
szr = render.NewSizer(cfg.OutputSize)
|
||||||
@ -38,6 +43,9 @@ func NewEngine(cfg Config, st *state.State, rs resource.Resource, ca cache.Memor
|
|||||||
ca: ca,
|
ca: ca,
|
||||||
vm: vm.NewVm(st, rs, ca, szr),
|
vm: vm.NewVm(st, rs, ca, szr),
|
||||||
}
|
}
|
||||||
|
if cfg.Root != "" {
|
||||||
|
engine.Init(cfg.Root, ctx)
|
||||||
|
}
|
||||||
return engine
|
return engine
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,6 +53,13 @@ func NewEngine(cfg Config, st *state.State, rs resource.Resource, ca cache.Memor
|
|||||||
//
|
//
|
||||||
// It loads and executes code for the start node.
|
// It loads and executes code for the start node.
|
||||||
func(en *Engine) Init(sym string, ctx context.Context) error {
|
func(en *Engine) Init(sym string, ctx context.Context) error {
|
||||||
|
if en.initd {
|
||||||
|
log.Printf("already initialized")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if sym == "" {
|
||||||
|
return fmt.Errorf("start sym empty")
|
||||||
|
}
|
||||||
err := en.st.SetInput([]byte{})
|
err := en.st.SetInput([]byte{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -55,6 +70,7 @@ func(en *Engine) Init(sym string, ctx context.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
en.st.SetCode(b)
|
en.st.SetCode(b)
|
||||||
|
en.initd = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,12 +40,18 @@ func(fs FsWrapper) inky(sym string, ctx context.Context) (string, error) {
|
|||||||
return "tinkywinky", nil
|
return "tinkywinky", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func(fs FsWrapper) pinky(sym string, ctx context.Context) (string, error) {
|
||||||
|
return "xyzzy", nil
|
||||||
|
}
|
||||||
|
|
||||||
func(fs FsWrapper) FuncFor(sym string) (resource.EntryFunc, error) {
|
func(fs FsWrapper) FuncFor(sym string) (resource.EntryFunc, error) {
|
||||||
switch sym {
|
switch sym {
|
||||||
case "one":
|
case "one":
|
||||||
return fs.one, nil
|
return fs.one, nil
|
||||||
case "inky":
|
case "inky":
|
||||||
return fs.inky, nil
|
return fs.inky, nil
|
||||||
|
case "pinky":
|
||||||
|
return fs.pinky, nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("function for %v not found", sym)
|
return nil, fmt.Errorf("function for %v not found", sym)
|
||||||
}
|
}
|
||||||
@ -75,7 +81,7 @@ func TestEngineInit(t *testing.T) {
|
|||||||
rs := NewFsWrapper(dataDir, &st)
|
rs := NewFsWrapper(dataDir, &st)
|
||||||
ca := cache.NewCache().WithCacheSize(1024)
|
ca := cache.NewCache().WithCacheSize(1024)
|
||||||
|
|
||||||
en := NewEngine(Config{}, &st, &rs, ca)
|
en := NewEngine(Config{}, &st, &rs, ca, ctx)
|
||||||
err := en.Init("root", ctx)
|
err := en.Init("root", ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -129,7 +135,7 @@ func TestEngineExecInvalidInput(t *testing.T) {
|
|||||||
rs := NewFsWrapper(dataDir, &st)
|
rs := NewFsWrapper(dataDir, &st)
|
||||||
ca := cache.NewCache().WithCacheSize(1024)
|
ca := cache.NewCache().WithCacheSize(1024)
|
||||||
|
|
||||||
en := NewEngine(Config{}, &st, &rs, ca)
|
en := NewEngine(Config{}, &st, &rs, ca, ctx)
|
||||||
err := en.Init("root", ctx)
|
err := en.Init("root", ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -18,13 +18,8 @@ import (
|
|||||||
// Any error not handled by the engine will terminate the oop and return an error.
|
// Any error not handled by the engine will terminate the oop and return an error.
|
||||||
//
|
//
|
||||||
// Rendered output is written to the provided writer.
|
// Rendered output is written to the provided writer.
|
||||||
func Loop(en *Engine, startSym string, ctx context.Context, reader io.Reader, writer io.Writer) error {
|
func Loop(en *Engine, reader io.Reader, writer io.Writer, ctx context.Context) error {
|
||||||
err := en.Init(startSym, ctx)
|
err := en.WriteResult(writer, ctx)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot init: %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = en.WriteResult(writer, ctx)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -19,8 +19,11 @@ func TestLoopTop(t *testing.T) {
|
|||||||
st := state.NewState(0)
|
st := state.NewState(0)
|
||||||
rs := resource.NewFsResource(dataDir)
|
rs := resource.NewFsResource(dataDir)
|
||||||
ca := cache.NewCache().WithCacheSize(1024)
|
ca := cache.NewCache().WithCacheSize(1024)
|
||||||
|
|
||||||
en := NewEngine(Config{}, &st, &rs, ca)
|
cfg := Config{
|
||||||
|
Root: "root",
|
||||||
|
}
|
||||||
|
en := NewEngine(cfg, &st, &rs, ca, ctx)
|
||||||
err := en.Init("root", ctx)
|
err := en.Init("root", ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -36,7 +39,7 @@ func TestLoopTop(t *testing.T) {
|
|||||||
outputBuf := bytes.NewBuffer(nil)
|
outputBuf := bytes.NewBuffer(nil)
|
||||||
log.Printf("running with input: %s", inputBuf.Bytes())
|
log.Printf("running with input: %s", inputBuf.Bytes())
|
||||||
|
|
||||||
err = Loop(&en, "root", ctx, inputBuf, outputBuf)
|
err = Loop(&en, inputBuf, outputBuf, ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -53,7 +56,10 @@ func TestLoopBackForth(t *testing.T) {
|
|||||||
rs := resource.NewFsResource(dataDir)
|
rs := resource.NewFsResource(dataDir)
|
||||||
ca := cache.NewCache().WithCacheSize(1024)
|
ca := cache.NewCache().WithCacheSize(1024)
|
||||||
|
|
||||||
en := NewEngine(Config{}, &st, &rs, ca)
|
cfg := Config{
|
||||||
|
Root: "root",
|
||||||
|
}
|
||||||
|
en := NewEngine(cfg, &st, &rs, ca, ctx)
|
||||||
err := en.Init("root", ctx)
|
err := en.Init("root", ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -70,7 +76,7 @@ func TestLoopBackForth(t *testing.T) {
|
|||||||
outputBuf := bytes.NewBuffer(nil)
|
outputBuf := bytes.NewBuffer(nil)
|
||||||
log.Printf("running with input: %s", inputBuf.Bytes())
|
log.Printf("running with input: %s", inputBuf.Bytes())
|
||||||
|
|
||||||
err = Loop(&en, "root", ctx, inputBuf, outputBuf)
|
err = Loop(&en, inputBuf, outputBuf, ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -85,8 +91,9 @@ func TestLoopBrowse(t *testing.T) {
|
|||||||
|
|
||||||
cfg := Config{
|
cfg := Config{
|
||||||
OutputSize: 68,
|
OutputSize: 68,
|
||||||
|
Root: "root",
|
||||||
}
|
}
|
||||||
en := NewEngine(cfg, &st, &rs, ca)
|
en := NewEngine(cfg, &st, &rs, ca, ctx)
|
||||||
err := en.Init("root", ctx)
|
err := en.Init("root", ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -104,7 +111,7 @@ func TestLoopBrowse(t *testing.T) {
|
|||||||
outputBuf := bytes.NewBuffer(nil)
|
outputBuf := bytes.NewBuffer(nil)
|
||||||
log.Printf("running with input: %s", inputBuf.Bytes())
|
log.Printf("running with input: %s", inputBuf.Bytes())
|
||||||
|
|
||||||
err = Loop(&en, "root", ctx, inputBuf, outputBuf)
|
err = Loop(&en, inputBuf, outputBuf, ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
28
engine/persist.go
Normal file
28
engine/persist.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package engine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"git.defalsify.org/festive/persist"
|
||||||
|
"git.defalsify.org/festive/resource"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RunPersisted(cfg Config, rs resource.Resource, pr persist.Persister, input []byte, w io.Writer, ctx context.Context) error {
|
||||||
|
err := pr.Load(cfg.SessionId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
st := pr.GetState()
|
||||||
|
log.Printf("st %v", st)
|
||||||
|
en := NewEngine(cfg, pr.GetState(), rs, pr.GetMemory(), ctx)
|
||||||
|
|
||||||
|
if len(input) > 0 {
|
||||||
|
_, err = en.Exec(input, ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
68
engine/persist_test.go
Normal file
68
engine/persist_test.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package engine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.defalsify.org/festive/cache"
|
||||||
|
"git.defalsify.org/festive/persist"
|
||||||
|
"git.defalsify.org/festive/state"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPersist(t *testing.T) {
|
||||||
|
generateTestData(t)
|
||||||
|
cfg := Config{
|
||||||
|
OutputSize: 128,
|
||||||
|
SessionId: "xyzzy",
|
||||||
|
Root: "root",
|
||||||
|
}
|
||||||
|
rs := NewFsWrapper(dataDir, nil)
|
||||||
|
|
||||||
|
persistDir, err := ioutil.TempDir("", "festive_engine_persist")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
st := state.NewState(3)
|
||||||
|
ca := cache.NewCache().WithCacheSize(1024)
|
||||||
|
pr := persist.NewFsPersister(persistDir).WithContent(&st, ca)
|
||||||
|
|
||||||
|
w := bytes.NewBuffer(nil)
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
|
|
||||||
|
err = RunPersisted(cfg, rs, pr, []byte{}, w, ctx)
|
||||||
|
if err != nil {
|
||||||
|
if !errors.Is(err, os.ErrNotExist) {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
st := state.NewState(cfg.FlagCount)
|
||||||
|
ca := cache.NewCache()
|
||||||
|
if cfg.CacheSize > 0 {
|
||||||
|
ca = ca.WithCacheSize(cfg.CacheSize)
|
||||||
|
}
|
||||||
|
pr = persist.NewFsPersister(persistDir).WithContent(&st, ca)
|
||||||
|
err = pr.Save(cfg.SessionId)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pr = persist.NewFsPersister(persistDir)
|
||||||
|
inputs := []string{
|
||||||
|
"",
|
||||||
|
"1",
|
||||||
|
"2",
|
||||||
|
"00",
|
||||||
|
}
|
||||||
|
for _, v := range inputs {
|
||||||
|
err = RunPersisted(cfg, rs, pr, []byte(v), w, ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@ package persist
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"github.com/fxamacker/cbor/v2"
|
"github.com/fxamacker/cbor/v2"
|
||||||
@ -32,6 +33,14 @@ func(p *FsPersister) WithContent(st *state.State, ca *cache.Cache) *FsPersister
|
|||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func(p *FsPersister) GetState() *state.State {
|
||||||
|
return p.State
|
||||||
|
}
|
||||||
|
|
||||||
|
func(p *FsPersister) GetMemory() cache.Memory {
|
||||||
|
return p.Memory
|
||||||
|
}
|
||||||
|
|
||||||
func(p *FsPersister) Serialize() ([]byte, error) {
|
func(p *FsPersister) Serialize() ([]byte, error) {
|
||||||
return cbor.Marshal(p)
|
return cbor.Marshal(p)
|
||||||
}
|
}
|
||||||
@ -47,6 +56,7 @@ func(p *FsPersister) Save(key string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fp := path.Join(p.dir, key)
|
fp := path.Join(p.dir, key)
|
||||||
|
log.Printf("saved key %v", key)
|
||||||
return ioutil.WriteFile(fp, b, 0600)
|
return ioutil.WriteFile(fp, b, 0600)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,5 +67,6 @@ func(p *FsPersister) Load(key string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = p.Deserialize(b)
|
err = p.Deserialize(b)
|
||||||
|
log.Printf("loaded key %v", key)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,16 @@
|
|||||||
package persist
|
package persist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.defalsify.org/festive/cache"
|
||||||
|
"git.defalsify.org/festive/state"
|
||||||
|
)
|
||||||
|
|
||||||
type Persister interface {
|
type Persister interface {
|
||||||
Serialize() ([]byte, error)
|
Serialize() ([]byte, error)
|
||||||
Deserialize(b []byte) error
|
Deserialize(b []byte) error
|
||||||
Save(key string) error
|
Save(key string) error
|
||||||
Load(key string) error
|
Load(key string) error
|
||||||
|
GetState() *state.State
|
||||||
|
GetMemory() cache.Memory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ type State struct {
|
|||||||
ExecPath []string // Command symbols stack
|
ExecPath []string // Command symbols stack
|
||||||
BitSize uint32 // size of (32-bit capacity) bit flag byte array
|
BitSize uint32 // size of (32-bit capacity) bit flag byte array
|
||||||
SizeIdx uint16
|
SizeIdx uint16
|
||||||
flags []byte // Error state
|
Flags []byte // Error state
|
||||||
input []byte // Last input
|
input []byte // Last input
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,9 +64,9 @@ func NewState(BitSize uint32) State {
|
|||||||
}
|
}
|
||||||
byteSize := toByteSize(BitSize + 8)
|
byteSize := toByteSize(BitSize + 8)
|
||||||
if byteSize > 0 {
|
if byteSize > 0 {
|
||||||
st.flags = make([]byte, byteSize)
|
st.Flags = make([]byte, byteSize)
|
||||||
} else {
|
} else {
|
||||||
st.flags = []byte{}
|
st.Flags = []byte{}
|
||||||
}
|
}
|
||||||
return st
|
return st
|
||||||
}
|
}
|
||||||
@ -80,14 +80,14 @@ func(st *State) SetFlag(bitIndex uint32) (bool, error) {
|
|||||||
if bitIndex + 1 > st.BitSize {
|
if bitIndex + 1 > st.BitSize {
|
||||||
return false, fmt.Errorf("bit index %v is out of range of bitfield size %v", bitIndex, st.BitSize)
|
return false, fmt.Errorf("bit index %v is out of range of bitfield size %v", bitIndex, st.BitSize)
|
||||||
}
|
}
|
||||||
r := getFlag(bitIndex, st.flags)
|
r := getFlag(bitIndex, st.Flags)
|
||||||
if r {
|
if r {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
byteIndex := bitIndex / 8
|
byteIndex := bitIndex / 8
|
||||||
localBitIndex := bitIndex % 8
|
localBitIndex := bitIndex % 8
|
||||||
b := st.flags[byteIndex]
|
b := st.Flags[byteIndex]
|
||||||
st.flags[byteIndex] = b | (1 << localBitIndex)
|
st.Flags[byteIndex] = b | (1 << localBitIndex)
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,14 +101,14 @@ func(st *State) ResetFlag(bitIndex uint32) (bool, error) {
|
|||||||
if bitIndex + 1 > st.BitSize {
|
if bitIndex + 1 > st.BitSize {
|
||||||
return false, fmt.Errorf("bit index %v is out of range of bitfield size %v", bitIndex, st.BitSize)
|
return false, fmt.Errorf("bit index %v is out of range of bitfield size %v", bitIndex, st.BitSize)
|
||||||
}
|
}
|
||||||
r := getFlag(bitIndex, st.flags)
|
r := getFlag(bitIndex, st.Flags)
|
||||||
if !r {
|
if !r {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
byteIndex := bitIndex / 8
|
byteIndex := bitIndex / 8
|
||||||
localBitIndex := bitIndex % 8
|
localBitIndex := bitIndex % 8
|
||||||
b := st.flags[byteIndex]
|
b := st.Flags[byteIndex]
|
||||||
st.flags[byteIndex] = b & (^(1 << localBitIndex))
|
st.Flags[byteIndex] = b & (^(1 << localBitIndex))
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +119,7 @@ func(st *State) GetFlag(bitIndex uint32) (bool, error) {
|
|||||||
if bitIndex + 1 > st.BitSize {
|
if bitIndex + 1 > st.BitSize {
|
||||||
return false, fmt.Errorf("bit index %v is out of range of bitfield size %v", bitIndex, st.BitSize)
|
return false, fmt.Errorf("bit index %v is out of range of bitfield size %v", bitIndex, st.BitSize)
|
||||||
}
|
}
|
||||||
return getFlag(bitIndex, st.flags), nil
|
return getFlag(bitIndex, st.Flags), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FlagBitSize reports the amount of bits available in the bit field index.
|
// FlagBitSize reports the amount of bits available in the bit field index.
|
||||||
@ -129,7 +129,7 @@ func(st *State) FlagBitSize() uint32 {
|
|||||||
|
|
||||||
// FlagBitSize reports the amount of bits available in the bit field index.
|
// FlagBitSize reports the amount of bits available in the bit field index.
|
||||||
func(st *State) FlagByteSize() uint8 {
|
func(st *State) FlagByteSize() uint8 {
|
||||||
return uint8(len(st.flags))
|
return uint8(len(st.Flags))
|
||||||
}
|
}
|
||||||
|
|
||||||
// MatchFlag matches the current state of the given flag.
|
// MatchFlag matches the current state of the given flag.
|
||||||
@ -169,7 +169,7 @@ func(st *State) GetIndex(flags []byte) bool {
|
|||||||
var i uint32
|
var i uint32
|
||||||
for i = 0; i < st.BitSize; i++ {
|
for i = 0; i < st.BitSize; i++ {
|
||||||
testVal := flags[byteIndex] & (1 << localIndex)
|
testVal := flags[byteIndex] & (1 << localIndex)
|
||||||
if (testVal & st.flags[byteIndex]) > 0 {
|
if (testVal & st.Flags[byteIndex]) > 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
globalIndex += 1
|
globalIndex += 1
|
||||||
|
@ -8,16 +8,16 @@ import (
|
|||||||
// Check creation
|
// Check creation
|
||||||
func TestNewState(t *testing.T) {
|
func TestNewState(t *testing.T) {
|
||||||
st := NewState(5)
|
st := NewState(5)
|
||||||
if len(st.flags) != 2 {
|
if len(st.Flags) != 2 {
|
||||||
t.Fatalf("invalid state flag length: %v", len(st.flags))
|
t.Fatalf("invalid state flag length: %v", len(st.Flags))
|
||||||
}
|
}
|
||||||
st = NewState(8)
|
st = NewState(8)
|
||||||
if len(st.flags) != 2 {
|
if len(st.Flags) != 2 {
|
||||||
t.Fatalf("invalid state flag length: %v", len(st.flags))
|
t.Fatalf("invalid state flag length: %v", len(st.Flags))
|
||||||
}
|
}
|
||||||
st = NewState(17)
|
st = NewState(17)
|
||||||
if len(st.flags) != 4 {
|
if len(st.Flags) != 4 {
|
||||||
t.Fatalf("invalid state flag length: %v", len(st.flags))
|
t.Fatalf("invalid state flag length: %v", len(st.Flags))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,8 +98,8 @@ func TestStateflags(t *testing.T) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("Expected out of range for bit index 17")
|
t.Fatalf("Expected out of range for bit index 17")
|
||||||
}
|
}
|
||||||
if !bytes.Equal(st.flags[:3], []byte{0x04, 0x04, 0x01}) {
|
if !bytes.Equal(st.Flags[:3], []byte{0x04, 0x04, 0x01}) {
|
||||||
t.Fatalf("Expected 0x040401, got %v", st.flags[:3])
|
t.Fatalf("Expected 0x040401, got %v", st.Flags[:3])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user