go-vise

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

commit 30ddf1494884233ebe36d112a3ec9a2034d57db0
parent 326bdb5018ae81ee493371b56bd4564a8b35da59
Author: lash <dev@holbrook.no>
Date:   Fri, 30 Aug 2024 19:02:42 +0100

Implement db in persister

Diffstat:
Mdb/db.go | 8++++++--
Adb/log.go | 9+++++++++
Mdb/mem.go | 19++++++++++++++++---
Dpersist/fs.go | 91-------------------------------------------------------------------------------
Mpersist/fs_test.go | 94++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Mpersist/persist.go | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
6 files changed, 163 insertions(+), 147 deletions(-)

diff --git a/db/db.go b/db/db.go @@ -25,6 +25,10 @@ type Db interface { Get(ctx context.Context, key []byte) ([]byte, error) // Put stores a value under a key. Any existing value will be replaced. Errors if the value could not be stored. Put(ctx context.Context, key []byte, val []byte) error + // SetPrefix sets the storage context prefix to use for consecutive Get and Put operations. + SetPrefix(pfx uint8) + // SetSession sets the session context to use for consecutive Get and Put operations. + SetSession(sessionId string) } // ToDbKey generates a key to use Db to store a value for a particular context. @@ -47,12 +51,12 @@ type BaseDb struct { sid []byte } -// SetPrefix sets the storage context prefix to use for consecutive Get and Put operations. +// SetPrefix implements Db. func(db *BaseDb) SetPrefix(pfx uint8) { db.pfx = pfx } -// SetSession sets the session context to use for consecutive Get and Put operations. +// SetSession implements Db. func(db *BaseDb) SetSession(sessionId string) { db.sid = append([]byte(sessionId), 0x2E) } diff --git a/db/log.go b/db/log.go @@ -0,0 +1,9 @@ +package db + +import ( + "git.defalsify.org/vise.git/logging" +) + +var ( + Logg logging.Logger = logging.NewVanilla().WithDomain("db") +) diff --git a/db/mem.go b/db/mem.go @@ -2,7 +2,7 @@ package db import ( "context" - "fmt" + "encoding/hex" ) // MemDb is a memory backend implementation of the Db interface. @@ -11,8 +11,18 @@ type MemDb struct { store map[string][]byte } +// NewMemDb returns an already allocated +func NewMemDb(ctx context.Context) *MemDb { + db := &MemDb{} + _ = db.Connect(ctx, "") + return db +} + // Connect implements Db func(mdb *MemDb) Connect(ctx context.Context, connStr string) error { + if mdb.store != nil { + return nil + } mdb.store = make(map[string][]byte) return nil } @@ -20,7 +30,7 @@ func(mdb *MemDb) Connect(ctx context.Context, connStr string) error { // convert to a supported map key type func(mdb *MemDb) toHexKey(key []byte) (string, error) { k, err := mdb.ToKey(key) - return fmt.Sprintf("%x", k), err + return hex.EncodeToString(k), err } // Get implements Db @@ -29,9 +39,11 @@ func(mdb *MemDb) Get(ctx context.Context, key []byte) ([]byte, error) { if err != nil { return nil, err } + Logg.TraceCtxf(ctx, "mem get", "k", k) v, ok := mdb.store[k] if !ok { - return nil, NewErrNotFound([]byte(k)) + b, _ := hex.DecodeString(k) + return nil, NewErrNotFound(b) } return v, nil } @@ -43,6 +55,7 @@ func(mdb *MemDb) Put(ctx context.Context, key []byte, val []byte) error { return err } mdb.store[k] = val + Logg.TraceCtxf(ctx, "mem put", "k", k, "v", val) return nil } diff --git a/persist/fs.go b/persist/fs.go @@ -1,91 +0,0 @@ -package persist - -import ( - "io/ioutil" - "path" - "path/filepath" - "github.com/fxamacker/cbor/v2" - - "git.defalsify.org/vise.git/cache" - "git.defalsify.org/vise.git/state" -) - -// FsPersister is an implementation of Persister that saves state to the file system. -type FsPersister struct { - State *state.State - Memory *cache.Cache - dir string -} - -// NewFsPersister creates a new FsPersister. -// -// The filesystem store will be at the given directory. The directory must exist. -func NewFsPersister(dir string) *FsPersister { - fp, err := filepath.Abs(dir) - if err != nil { - panic(err) - } - return &FsPersister{ - dir: fp, - } -} - -// WithContent sets a current State and Cache object. -// -// This method is normally called before Serialize / Save. -func(p *FsPersister) WithContent(st *state.State, ca *cache.Cache) *FsPersister { - p.State = st - p.Memory = ca - return p -} - -// GetState implements the Persister interface. -func(p *FsPersister) GetState() *state.State { - return p.State -} - -// GetMemory implements the Persister interface. -func(p *FsPersister) GetMemory() cache.Memory { - return p.Memory -} - -// Serialize implements the Persister interface. -func(p *FsPersister) Serialize() ([]byte, error) { - return cbor.Marshal(p) -} - -// Deserialize implements the Persister interface. -func(p *FsPersister) Deserialize(b []byte) error { - err := cbor.Unmarshal(b, p) - return err -} - -// Save implements the Persister interface. -func(p *FsPersister) Save(key string) error { - b, err := p.Serialize() - if err != nil { - return err - } - fp := path.Join(p.dir, key) - err = ioutil.WriteFile(fp, b, 0600) - if err != nil { - return err - } - Logg.Debugf("saved state and cache", "key", key, "bytecode", p.State.Code, "flags", p.State.Flags) - return nil -} - -// Load implements the Persister interface. -func(p *FsPersister) Load(key string) error { - fp := path.Join(p.dir, key) - b, err := ioutil.ReadFile(fp) - if err != nil { - return err - } - err = p.Deserialize(b) - if err != nil { - return err - } - Logg.Debugf("loaded state and cache", "key", key, "bytecode", p.State.Code) - return nil -} diff --git a/persist/fs_test.go b/persist/fs_test.go @@ -2,13 +2,14 @@ package persist import ( "bytes" - "io/ioutil" + "context" "reflect" "testing" "git.defalsify.org/vise.git/cache" "git.defalsify.org/vise.git/state" "git.defalsify.org/vise.git/vm" + "git.defalsify.org/vise.git/db" ) func TestSerializeState(t *testing.T) { @@ -27,31 +28,38 @@ func TestSerializeState(t *testing.T) { ca.Add("inky", "pinky", 13) ca.Add("blinky", "clyde", 42) - pr := NewFsPersister(".").WithContent(&st, ca) + ctx := context.Background() + store := db.NewMemDb(ctx) + pr := NewPersister(store).WithContext(context.Background()).WithSession("xyzzy").WithContent(&st, ca) v, err := pr.Serialize() if err != nil { t.Error(err) } - prnew := NewFsPersister(".") + prnew := NewPersister(store).WithSession("xyzzy") err = prnew.Deserialize(v) if err != nil { t.Fatal(err) - } - if !reflect.DeepEqual(prnew.State.ExecPath, pr.State.ExecPath) { - t.Fatalf("expected %s, got %s", prnew.State.ExecPath, pr.State.ExecPath) } - if !bytes.Equal(prnew.State.Code, pr.State.Code) { - t.Fatalf("expected %x, got %x", prnew.State.Code, pr.State.Code) + stNew := prnew.GetState() + stOld := pr.GetState() + caNew := prnew.GetMemory() + caOld := pr.GetMemory() + + if !reflect.DeepEqual(stNew.ExecPath, stOld.ExecPath) { + t.Fatalf("expected %s, got %s", stNew.ExecPath, stOld.ExecPath) + } + if !bytes.Equal(stNew.Code, stOld.Code) { + t.Fatalf("expected %x, got %x", stNew.Code, stOld.Code) } - if prnew.State.BitSize != pr.State.BitSize { - t.Fatalf("expected %v, got %v", prnew.State.BitSize, pr.State.BitSize) + if stNew.BitSize != stOld.BitSize { + t.Fatalf("expected %v, got %v", stNew.BitSize, stOld.BitSize) } - if prnew.State.SizeIdx != pr.State.SizeIdx { - t.Fatalf("expected %v, got %v", prnew.State.SizeIdx, pr.State.SizeIdx) + if stNew.SizeIdx != stOld.SizeIdx { + t.Fatalf("expected %v, got %v", stNew.SizeIdx, stOld.SizeIdx) } - if !reflect.DeepEqual(prnew.Memory, pr.Memory) { - t.Fatalf("expected %v, got %v", prnew.Memory, pr.Memory) + if !reflect.DeepEqual(caNew, caOld) { + t.Fatalf("expected %v, got %v", caNew, caOld) } } @@ -71,35 +79,38 @@ func TestSaveLoad(t *testing.T) { ca.Add("inky", "pinky", 13) ca.Add("blinky", "clyde", 42) - dir, err := ioutil.TempDir("", "vise_persist") - if err != nil { - t.Error(err) - } - pr := NewFsPersister(dir).WithContent(&st, ca) - err = pr.Save("xyzzy") + ctx := context.Background() + store := db.NewMemDb(ctx) + pr := NewPersister(store).WithContent(&st, ca) + err := pr.Save("xyzzy") if err != nil { - t.Error(err) + t.Fatal(err) } - prnew := NewFsPersister(dir) + prnew := NewPersister(store) err = prnew.Load("xyzzy") if err != nil { - t.Error(err) + t.Fatal(err) } - if !reflect.DeepEqual(prnew.State.ExecPath, pr.State.ExecPath) { - t.Fatalf("expected %s, got %s", prnew.State.ExecPath, pr.State.ExecPath) + stNew := prnew.GetState() + stOld := pr.GetState() + caNew := prnew.GetMemory() + caOld := pr.GetMemory() + + if !reflect.DeepEqual(stNew.ExecPath, stOld.ExecPath) { + t.Fatalf("expected %s, got %s", stNew.ExecPath, stOld.ExecPath) } - if !bytes.Equal(prnew.State.Code, pr.State.Code) { - t.Fatalf("expected %x, got %x", prnew.State.Code, pr.State.Code) + if !bytes.Equal(stNew.Code, stOld.Code) { + t.Fatalf("expected %x, got %x", stNew.Code, stOld.Code) } - if prnew.State.BitSize != pr.State.BitSize { - t.Fatalf("expected %v, got %v", prnew.State.BitSize, pr.State.BitSize) + if stNew.BitSize != stOld.BitSize { + t.Fatalf("expected %v, got %v", stNew.BitSize, stOld.BitSize) } - if prnew.State.SizeIdx != pr.State.SizeIdx { - t.Fatalf("expected %v, got %v", prnew.State.SizeIdx, pr.State.SizeIdx) + if stNew.SizeIdx != stOld.SizeIdx { + t.Fatalf("expected %v, got %v", stNew.SizeIdx, stOld.SizeIdx) } - if !reflect.DeepEqual(prnew.Memory, pr.Memory) { - t.Fatalf("expected %v, got %v", prnew.Memory, pr.Memory) + if !reflect.DeepEqual(caNew, caOld) { + t.Fatalf("expected %v, got %v", caNew, caOld) } } @@ -108,20 +119,19 @@ func TestSaveLoadFlags(t *testing.T) { st.SetFlag(8) ca := cache.NewCache() - dir, err := ioutil.TempDir("", "vise_persist") - if err != nil { - t.Error(err) - } - pr := NewFsPersister(dir).WithContent(&st, ca) - err = pr.Save("xyzzy") + ctx := context.Background() + store := db.NewMemDb(ctx) + pr := NewPersister(store).WithContent(&st, ca) + err := pr.Save("xyzzy") if err != nil { - t.Error(err) + t.Fatal(err) } - prnew := NewFsPersister(dir) + prnew := NewPersister(store) + err = prnew.Load("xyzzy") if err != nil { - t.Error(err) + t.Fatal(err) } stnew := prnew.GetState() if !stnew.GetFlag(8) { diff --git a/persist/persist.go b/persist/persist.go @@ -1,17 +1,88 @@ package persist import ( - "git.defalsify.org/vise.git/cache" + "context" + + "github.com/fxamacker/cbor/v2" + + "git.defalsify.org/vise.git/db" "git.defalsify.org/vise.git/state" + "git.defalsify.org/vise.git/cache" ) -// Persister interface defines the methods needed for a component that can store the execution state to a storage location. -type Persister interface { - Serialize() ([]byte, error) // Output serializes representation of the state. - Deserialize(b []byte) error // Restore state from a serialized state. - Save(key string) error // Serialize and commit the state representation to persisted storage. - Load(key string) error // Load the state representation from persisted storage and Deserialize. - GetState() *state.State // Get the currently loaded State object. - GetMemory() cache.Memory // Get the currently loaded Cache object. +type Persister struct { + State *state.State + Memory *cache.Cache + ctx context.Context + db db.Db +} + +func NewPersister(db db.Db) *Persister { + return &Persister{ + db: db, + ctx: context.Background(), + } +} + +func(p *Persister) WithContext(ctx context.Context) *Persister { + p.ctx = ctx + return p +} + +func(p *Persister) WithSession(sessionId string) *Persister { + p.db.SetSession(sessionId) + return p +} + +// WithContent sets a current State and Cache object. +// +// This method is normally called before Serialize / Save. +func(p *Persister) WithContent(st *state.State, ca *cache.Cache) *Persister { + p.State = st + p.Memory = ca + return p } +// GetState implements the Persister interface. +func(p *Persister) GetState() *state.State { + return p.State +} + +// GetMemory implements the Persister interface. +func(p *Persister) GetMemory() cache.Memory { + return p.Memory +} + +// Serialize implements the Persister interface. +func(p *Persister) Serialize() ([]byte, error) { + return cbor.Marshal(p) +} + +// Deserialize implements the Persister interface. +func(p *Persister) Deserialize(b []byte) error { + err := cbor.Unmarshal(b, p) + return err +} + +func(p *Persister) Save(key string) error { + b, err := p.Serialize() + if err != nil { + return err + } + p.db.SetPrefix(db.DATATYPE_STATE) + return p.db.Put(p.ctx, []byte(key), b) +} + +func(p *Persister) Load(key string) error { + p.db.SetPrefix(db.DATATYPE_STATE) + b, err := p.db.Get(p.ctx, []byte(key)) + if err != nil { + return err + } + err = p.Deserialize(b) + if err != nil { + return err + } + Logg.Debugf("loaded state and cache", "key", key, "bytecode", p.State.Code) + return nil +}