2025-03-19 13:42:12 +03:00
|
|
|
package meta
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr"
|
2025-03-20 14:57:24 +03:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/util/proto"
|
2025-03-19 13:42:12 +03:00
|
|
|
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) {
|
2025-03-20 14:57:24 +03:00
|
|
|
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))
|
|
|
|
}
|
2025-03-19 13:42:12 +03:00
|
|
|
|
2025-03-20 14:57:24 +03:00
|
|
|
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)
|
|
|
|
}
|
2025-03-19 13:42:12 +03:00
|
|
|
}
|
2025-03-20 14:57:24 +03:00
|
|
|
return nil, logicerr.Wrap(new(apistatus.ObjectNotFound))
|
|
|
|
}
|
|
|
|
|
|
|
|
func parentFromSplitHeader(fc easyproto.FieldContext) ([]byte, error) {
|
|
|
|
iter := newProtoIter(fc.MessageData())
|
2025-03-19 13:42:12 +03:00
|
|
|
|
2025-03-20 14:57:24 +03:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
2025-03-19 13:42:12 +03:00
|
|
|
|
2025-03-20 14:57:24 +03:00
|
|
|
if parentSig == nil || parentHdr == nil {
|
2025-03-19 13:42:12 +03:00
|
|
|
return nil, logicerr.Wrap(new(apistatus.ObjectNotFound))
|
|
|
|
}
|
|
|
|
|
2025-03-20 14:57:24 +03:00
|
|
|
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)]
|
2025-03-19 13:42:12 +03:00
|
|
|
}
|