go-vise

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

persist.go (3474B)


      1 package persist
      2 
      3 import (
      4 	"context"
      5 	"fmt"
      6 
      7 	"github.com/fxamacker/cbor/v2"
      8 
      9 	"git.defalsify.org/vise.git/db"
     10 	"git.defalsify.org/vise.git/state"
     11 	"git.defalsify.org/vise.git/cache"
     12 )
     13 
     14 // Persister abstracts storage and retrieval of state and cache.
     15 type Persister struct {
     16 	State *state.State
     17 	Memory *cache.Cache
     18 	ctx context.Context
     19 	db db.Db
     20 	flush bool
     21 }
     22 
     23 // NewPersister creates a new Persister instance.
     24 func NewPersister(db db.Db) *Persister {
     25 	return &Persister{
     26 		db: db,
     27 		ctx: context.Background(),
     28 	}
     29 }
     30 
     31 // WithSession is a chainable function that sets the current golang context of the persister.
     32 func(p *Persister) WithContext(ctx context.Context) *Persister {
     33 	p.ctx = ctx
     34 	return p
     35 }
     36 
     37 // WithSession is a chainable function that sets the current session context of the persister.
     38 func(p *Persister) WithSession(sessionId string) *Persister {
     39 	p.db.SetSession(sessionId)
     40 	return p
     41 }
     42 
     43 
     44 // WithContent is a chainable function that sets a current State and Cache object.
     45 //
     46 // This method is normally called before Serialize / Save.
     47 func(p *Persister) WithContent(st *state.State, ca *cache.Cache) *Persister {
     48 	p.State = st
     49 	p.Memory = ca
     50 	return p
     51 }
     52 
     53 // WithFlush is a chainable function that instructs the persister to flush its memory and state
     54 // after successful Save.
     55 func(p *Persister) WithFlush() *Persister {
     56 	p.flush = true
     57 	return p
     58 }
     59 
     60 // Invalid checks if the underlying state has been invalidated.
     61 //
     62 // An invalid state will cause Save to panic.
     63 func(p *Persister) Invalid() bool {
     64 	return p.GetState().Invalid() || p.GetMemory().Invalid()
     65 }
     66 
     67 // GetState returns the state enclosed by the Persister.
     68 func(p *Persister) GetState() *state.State {
     69 	return p.State
     70 }
     71 
     72 // GetMemory returns the cache (memory) enclosed by the Persister.
     73 func(p *Persister) GetMemory() cache.Memory {
     74 	return p.Memory
     75 }
     76 
     77 // Serialize encodes the state and cache into byte form for storage.
     78 func(p *Persister) Serialize() ([]byte, error) {
     79 	return cbor.Marshal(p)
     80 }
     81 
     82 // Deserialize decodes the state and cache from storage, and applies them to the persister.
     83 func(p *Persister) Deserialize(b []byte) error {
     84 	err := cbor.Unmarshal(b, p)
     85 	return err
     86 }
     87 
     88 // Save perists the state and cache to the db.Db backend.
     89 //
     90 // If save is successful and WithFlush() has been called, the state and memory
     91 // will be empty when the method returns.
     92 func(p *Persister) Save(key string) error {
     93 	if p.Invalid() {
     94 		panic("persister has been invalidated")
     95 	}
     96 	b, err := p.Serialize()
     97 	if err != nil {
     98 		return err
     99 	}
    100 	p.db.SetPrefix(db.DATATYPE_STATE)
    101 	logg.Debugf("saving state and cache", "self", p, "key", key, "state", p.State)
    102 	logg.Tracef("saving bytecode", "code", p.State.Code)
    103 	err = p.db.Put(p.ctx, []byte(key), b)
    104 	if err != nil {
    105 		return err
    106 	}
    107 	if p.flush {
    108 		logg.Tracef("state and cache flushed from persister")
    109 		p.Memory.Reset()
    110 		p.Memory.Pop()
    111 		p.State = p.State.CloneEmpty()
    112 	}
    113 	return nil
    114 }
    115 
    116 // Load retrieves state and cache from the db.Db backend.
    117 func(p *Persister) Load(key string) error {
    118 	p.db.SetPrefix(db.DATATYPE_STATE)
    119 	b, err := p.db.Get(p.ctx, []byte(key))
    120 	if err != nil {
    121 		return err
    122 	}
    123 	err = p.Deserialize(b)
    124 	if err != nil {
    125 		return err
    126 	}
    127 	logg.Debugf("loaded state and cache", "self", p, "key", key, "state", p.State)
    128 	logg.Tracef("loaded bytecode", "code", p.State.Code)
    129 	return nil
    130 }
    131 
    132 // String implements the String interface
    133 func(p *Persister) String() string {
    134 	return fmt.Sprintf("perister @%p state:%p cache:%p", p, p.State, p.Memory)
    135 }