frostfs-node/lib/transformer/restore.go
alexvanin dadfd90dcd Initial commit
Initial public review release v0.10.0
2020-07-10 17:45:00 +03:00

126 lines
2.5 KiB
Go

package transformer
import (
"context"
"sync"
"github.com/nspcc-dev/neofs-api-go/object"
"github.com/pkg/errors"
)
type (
// ObjectRestorer is an interface of object restorer.
ObjectRestorer interface {
Type() object.Transform_Type
Restore(context.Context, ...Object) ([]Object, error)
}
restorePipeline struct {
ObjectRestorer
*sync.RWMutex
items map[object.Transform_Type]ObjectRestorer
}
splitRestorer struct{}
)
var errEmptyObjList = errors.New("object list is empty")
var errMissingParentLink = errors.New("missing parent link")
func (s *restorePipeline) Restore(ctx context.Context, srcObjs ...Object) ([]Object, error) {
if len(srcObjs) == 0 {
return nil, errEmptyInput
}
s.RLock()
defer s.RUnlock()
var (
objs = srcObjs
err error
)
for {
_, th := objs[0].LastHeader(object.HeaderType(object.TransformHdr))
if th == nil {
break
}
transform := th.Value.(*object.Header_Transform).Transform
tr, ok := s.items[transform.Type]
if !ok {
return nil, errors.Errorf("missing restorer (%s)", transform.Type)
}
if objs, err = tr.Restore(ctx, objs...); err != nil {
return nil, errors.Wrapf(err, "restoration failed (%s)", transform.Type)
}
}
return objs, nil
}
// NewRestorePipeline is a constructor of the pipeline of object restorers.
func NewRestorePipeline(t ...ObjectRestorer) ObjectRestorer {
m := make(map[object.Transform_Type]ObjectRestorer, len(t))
for i := range t {
m[t[i].Type()] = t[i]
}
return &restorePipeline{
RWMutex: new(sync.RWMutex),
items: m,
}
}
func (*splitRestorer) Type() object.Transform_Type {
return object.Transform_Split
}
func (*splitRestorer) Restore(ctx context.Context, objs ...Object) ([]Object, error) {
if len(objs) == 0 {
return nil, errEmptyObjList
}
chain, err := GetChain(objs...)
if err != nil {
return nil, errors.Wrap(err, "could not get chain of objects")
}
obj := chain[len(chain)-1]
var (
size uint64
p = make([]byte, 0, len(chain[0].Payload)*len(chain))
)
for j := 0; j < len(chain); j++ {
p = append(p, chain[j].Payload...)
size += chain[j].SystemHeader.PayloadLength
}
obj.SystemHeader.PayloadLength = size
obj.Payload = p
parent, err := lastLink(&obj, object.Link_Parent)
if err != nil {
return nil, errMissingParentLink
}
obj.SystemHeader.ID = parent
err = deleteTransformer(&obj, object.Transform_Split)
if err != nil {
return nil, err
}
return []Object{obj}, nil
}
// SplitRestorer is a splitted object restorer's constructor.
func SplitRestorer() ObjectRestorer {
return new(splitRestorer)
}