package meta import ( "errors" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/util/proto" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" 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) { iter := newProtoIter(src, true) for iter.next(); !iter.finished(); iter.next() { if iter.fc.FieldNum == 3 { return parentFromChildHeader(iter.fc) } } return nil, logicerr.Wrap(new(apistatus.ObjectNotFound)) } func parentFromChildHeader(fc easyproto.FieldContext) ([]byte, error) { iter := newProtoIter(fc.MessageData()) for iter.next(); !iter.finished(); iter.next() { if iter.fc.FieldNum == 11 { return parentFromSplitHeader(iter.fc) } } return nil, logicerr.Wrap(new(apistatus.ObjectNotFound)) } func parentFromSplitHeader(fc easyproto.FieldContext) ([]byte, error) { iter := newProtoIter(fc.MessageData()) var parentID, parentSig, parentHdr []byte for iter.next(); !iter.finished(); iter.next() { var ok bool switch iter.fc.FieldNum { case 1: // parent id parentID, ok = iter.fc.MessageData() case 3: // parent signature parentSig, ok = iter.fc.MessageData() case 4: // parent header parentHdr, ok = iter.fc.MessageData() default: continue } if !ok { return nil, errMalformedObject } } if parentSig == nil || parentHdr == nil { return nil, logicerr.Wrap(new(apistatus.ObjectNotFound)) } h := &header{ parentID: parentID, parentSig: parentSig, parentHdr: parentHdr, } return h.StableMarshal(make([]byte, h.StableSize())), nil } type ( header struct { parentID bs parentSig bs parentHdr bs } ) func (o *header) StableSize() int { var size int size += proto.NestedStructureSize(1, &o.parentID) size += proto.NestedStructureSize(2, &o.parentSig) size += proto.NestedStructureSize(3, &o.parentHdr) return size } func (o *header) StableMarshal(buf []byte) []byte { var offset int offset += proto.NestedStructureMarshal(1, buf[offset:], &o.parentID) offset += proto.NestedStructureMarshal(2, buf[offset:], &o.parentSig) offset += proto.NestedStructureMarshal(3, buf[offset:], &o.parentHdr) return buf[:offset] } type bs []byte func (b *bs) StableSize() int { return len(*b) } func (b *bs) StableMarshal(dst []byte) []byte { copy(dst, *b) return dst[:len(*b)] }