191 lines
4.1 KiB
Go
191 lines
4.1 KiB
Go
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()
|
|
}
|