package meta import ( "errors" "fmt" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" "github.com/VictoriaMetrics/easyproto" ) var errMalformedObject = errors.New("malformed object") type protoAttribute struct { found bool value string } type protoHeader struct { attribute protoAttribute ecHeader protoECHeader } type protoECHeader struct { parentID protoOID attribute protoAttribute } type protoOID oid.ID type protoIter struct { fc easyproto.FieldContext data []byte failed bool eof bool } func newProtoIter(data []byte, ok bool) protoIter { return protoIter{data: data, failed: !ok} } func (p *protoIter) err() error { if p.failed { return errMalformedObject } return nil } func (p *protoIter) finished() bool { return p.failed || p.eof } func (p *protoIter) next() { if p.failed { return } if len(p.data) == 0 { p.eof = true return } data, err := p.fc.NextField(p.data) if err != nil { p.failed = true return } p.data = data } func (p *protoOID) fill(fc easyproto.FieldContext) error { iter := newProtoIter(fc.MessageData()) for iter.next(); !iter.finished(); iter.next() { if iter.fc.FieldNum == 1 { // Wire number for `id` field. rawID, ok := iter.fc.Bytes() if !ok { return errMalformedObject } return (*oid.ID)(p).Decode(rawID) } } return iter.err() } func (p *protoAttribute) fill(fc easyproto.FieldContext, attribute string) error { var key, value string iter := newProtoIter(fc.MessageData()) for iter.next(); !iter.finished(); iter.next() { var ok bool switch iter.fc.FieldNum { case 1: // Wire number for `key` field. key, ok = iter.fc.String() case 2: // Wire number for `value` field. value, ok = iter.fc.String() default: continue } if !ok { return errMalformedObject } } if key == attribute { p.found = true p.value = value } return iter.err() } func (p *protoECHeader) fill(fc easyproto.FieldContext, attribute string) error { iter := newProtoIter(fc.MessageData()) for iter.next(); !iter.finished(); iter.next() { var err error switch iter.fc.FieldNum { case 1: // Wire number for `parent` field. err = p.parentID.fill(iter.fc) case 8: // Wire number for `parent_attributes` field. err = p.attribute.fill(iter.fc, attribute) } if err != nil { return err } } return iter.err() } func (p *protoHeader) fill(fc easyproto.FieldContext, attribute string) error { iter := newProtoIter(fc.MessageData()) for iter.next(); !iter.finished(); iter.next() { var err error switch iter.fc.FieldNum { case 10: // Wire number for `attributes` field. err = p.attribute.fill(iter.fc, attribute) case 12: // Wire number for `ec` field. err = p.ecHeader.fill(iter.fc, attribute) } if err != nil { return err } } return iter.err() } type attributeMatchResult struct { attribute protoAttribute isEC bool parentID oid.ID } func attributeValueRaw(src []byte, attribute string) (attributeMatchResult, error) { iter := newProtoIter(src, true) for iter.next(); !iter.finished(); iter.next() { if iter.fc.FieldNum == 3 { // Wire number for `header` field. var p protoHeader if err := p.fill(iter.fc, attribute); err != nil { return attributeMatchResult{}, err } if p.ecHeader.attribute.found { return attributeMatchResult{ attribute: p.ecHeader.attribute, isEC: true, parentID: oid.ID(p.ecHeader.parentID), }, nil } else { return attributeMatchResult{attribute: p.attribute}, nil } } } return attributeMatchResult{}, iter.err() } func parentFromChild(src []byte) ([]byte, error) { child := objectSDK.New() err := child.Unmarshal(src) if err != nil { return nil, fmt.Errorf("unmarshal child with parent: %w", err) } par := child.Parent() if par == nil { // this should never happen though return nil, logicerr.Wrap(new(apistatus.ObjectNotFound)) } return par.Marshal() }