188 lines
4.7 KiB
Go
188 lines
4.7 KiB
Go
|
package object
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"strconv"
|
||
|
)
|
||
|
|
||
|
// SysAttributePrefix is a prefix of key to system attribute.
|
||
|
const SysAttributePrefix = "__SYSTEM__"
|
||
|
|
||
|
const (
|
||
|
// SysAttributeUploadID marks smaller parts of a split bigger object.
|
||
|
SysAttributeUploadID = SysAttributePrefix + "UPLOAD_ID"
|
||
|
|
||
|
// SysAttributeExpEpoch tells GC to delete object after that epoch.
|
||
|
SysAttributeExpEpoch = SysAttributePrefix + "EXPIRATION_EPOCH"
|
||
|
|
||
|
// SysAttributeTickEpoch defines what epoch must produce object
|
||
|
// notification.
|
||
|
SysAttributeTickEpoch = SysAttributePrefix + "TICK_EPOCH"
|
||
|
|
||
|
// SysAttributeTickTopic defines what topic object notification
|
||
|
// must be sent to.
|
||
|
SysAttributeTickTopic = SysAttributePrefix + "TICK_TOPIC"
|
||
|
)
|
||
|
|
||
|
// SysAttributePrefixNeoFS is a prefix of key to system attribute.
|
||
|
// Deprecated: use SysAttributePrefix.
|
||
|
const SysAttributePrefixNeoFS = "__NEOFS__"
|
||
|
|
||
|
const (
|
||
|
// SysAttributeUploadIDNeoFS marks smaller parts of a split bigger object.
|
||
|
// Deprecated: use SysAttributeUploadID.
|
||
|
SysAttributeUploadIDNeoFS = SysAttributePrefixNeoFS + "UPLOAD_ID"
|
||
|
|
||
|
// SysAttributeExpEpochNeoFS tells GC to delete object after that epoch.
|
||
|
// Deprecated: use SysAttributeExpEpoch.
|
||
|
SysAttributeExpEpochNeoFS = SysAttributePrefixNeoFS + "EXPIRATION_EPOCH"
|
||
|
|
||
|
// SysAttributeTickEpochNeoFS defines what epoch must produce object
|
||
|
// notification.
|
||
|
// Deprecated: use SysAttributeTickEpoch.
|
||
|
SysAttributeTickEpochNeoFS = SysAttributePrefixNeoFS + "TICK_EPOCH"
|
||
|
|
||
|
// SysAttributeTickTopicNeoFS defines what topic object notification
|
||
|
// must be sent to.
|
||
|
// Deprecated: use SysAttributeTickTopic.
|
||
|
SysAttributeTickTopicNeoFS = SysAttributePrefixNeoFS + "TICK_TOPIC"
|
||
|
)
|
||
|
|
||
|
// NotificationInfo groups information about object notification
|
||
|
// that can be written to object.
|
||
|
//
|
||
|
// Topic is an optional field.
|
||
|
type NotificationInfo struct {
|
||
|
epoch uint64
|
||
|
topic string
|
||
|
}
|
||
|
|
||
|
// Epoch returns object notification tick
|
||
|
// epoch.
|
||
|
func (n NotificationInfo) Epoch() uint64 {
|
||
|
return n.epoch
|
||
|
}
|
||
|
|
||
|
// SetEpoch sets object notification tick
|
||
|
// epoch.
|
||
|
func (n *NotificationInfo) SetEpoch(epoch uint64) {
|
||
|
n.epoch = epoch
|
||
|
}
|
||
|
|
||
|
// Topic return optional object notification
|
||
|
// topic.
|
||
|
func (n NotificationInfo) Topic() string {
|
||
|
return n.topic
|
||
|
}
|
||
|
|
||
|
// SetTopic sets optional object notification
|
||
|
// topic.
|
||
|
func (n *NotificationInfo) SetTopic(topic string) {
|
||
|
n.topic = topic
|
||
|
}
|
||
|
|
||
|
// WriteNotificationInfo writes NotificationInfo to the Object via attributes. Object must not be nil.
|
||
|
//
|
||
|
// Existing notification attributes are expected to be key-unique, otherwise undefined behavior.
|
||
|
func WriteNotificationInfo(o *Object, ni NotificationInfo) {
|
||
|
h := o.GetHeader()
|
||
|
if h == nil {
|
||
|
h = new(Header)
|
||
|
o.SetHeader(h)
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
attrs = h.GetAttributes()
|
||
|
|
||
|
epoch = strconv.FormatUint(ni.Epoch(), 10)
|
||
|
topic = ni.Topic()
|
||
|
|
||
|
changedEpoch bool
|
||
|
changedTopic bool
|
||
|
deleteIndex = -1
|
||
|
)
|
||
|
|
||
|
for i := range attrs {
|
||
|
switch attrs[i].GetKey() {
|
||
|
case SysAttributeTickEpoch, SysAttributeTickEpochNeoFS:
|
||
|
attrs[i].SetValue(epoch)
|
||
|
changedEpoch = true
|
||
|
case SysAttributeTickTopic, SysAttributeTickTopicNeoFS:
|
||
|
changedTopic = true
|
||
|
|
||
|
if topic == "" {
|
||
|
deleteIndex = i
|
||
|
break
|
||
|
}
|
||
|
|
||
|
attrs[i].SetValue(topic)
|
||
|
}
|
||
|
|
||
|
if changedEpoch && changedTopic {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if deleteIndex != -1 {
|
||
|
// approach without allocation/waste
|
||
|
// coping works since the attributes
|
||
|
// order is not important
|
||
|
attrs[deleteIndex] = attrs[len(attrs)-1]
|
||
|
attrs = attrs[:len(attrs)-1]
|
||
|
}
|
||
|
|
||
|
if !changedEpoch {
|
||
|
index := len(attrs)
|
||
|
attrs = append(attrs, Attribute{})
|
||
|
attrs[index].SetKey(SysAttributeTickEpoch)
|
||
|
attrs[index].SetValue(epoch)
|
||
|
}
|
||
|
|
||
|
if !changedTopic && topic != "" {
|
||
|
index := len(attrs)
|
||
|
attrs = append(attrs, Attribute{})
|
||
|
attrs[index].SetKey(SysAttributeTickTopic)
|
||
|
attrs[index].SetValue(topic)
|
||
|
}
|
||
|
|
||
|
h.SetAttributes(attrs)
|
||
|
}
|
||
|
|
||
|
// ErrNotificationNotSet means that object does not have notification.
|
||
|
var ErrNotificationNotSet = errors.New("notification for object is not set")
|
||
|
|
||
|
// GetNotificationInfo looks for object notification attributes. Object must not be nil.
|
||
|
// Returns ErrNotificationNotSet if no corresponding attributes
|
||
|
// were found.
|
||
|
//
|
||
|
// Existing notification attributes are expected to be key-unique, otherwise undefined behavior.
|
||
|
func GetNotificationInfo(o *Object) (*NotificationInfo, error) {
|
||
|
var (
|
||
|
foundEpoch bool
|
||
|
ni = new(NotificationInfo)
|
||
|
)
|
||
|
|
||
|
for _, attr := range o.GetHeader().GetAttributes() {
|
||
|
switch key := attr.GetKey(); key {
|
||
|
case SysAttributeTickEpoch, SysAttributeTickEpochNeoFS:
|
||
|
epoch, err := strconv.ParseUint(attr.GetValue(), 10, 64)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("could not parse epoch: %w", err)
|
||
|
}
|
||
|
|
||
|
ni.SetEpoch(epoch)
|
||
|
|
||
|
foundEpoch = true
|
||
|
case SysAttributeTickTopic, SysAttributeTickTopicNeoFS:
|
||
|
ni.SetTopic(attr.GetValue())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if !foundEpoch {
|
||
|
return nil, ErrNotificationNotSet
|
||
|
}
|
||
|
|
||
|
return ni, nil
|
||
|
}
|