go-vise

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

persist.go (3493B)


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