[#226] v2/tombstone: Implement unified message structure with methods

Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
remotes/KirillovDenis/feature/refactor-sig-rpc
Leonard Lyubich 2020-12-10 11:30:14 +03:00 committed by Leonard Lyubich
parent 2f3e5742d3
commit 6037a26a35
7 changed files with 310 additions and 0 deletions

View File

@ -0,0 +1,53 @@
package tombstone
import (
"github.com/nspcc-dev/neofs-api-go/v2/refs"
refsGRPC "github.com/nspcc-dev/neofs-api-go/v2/refs/grpc"
tombstone "github.com/nspcc-dev/neofs-api-go/v2/tombstone/grpc"
)
// TombstoneToGRPCMessage converts unified tombstone message into gRPC message.
func TombstoneToGRPCMessage(t *Tombstone) *tombstone.Tombstone {
if t == nil {
return nil
}
m := new(tombstone.Tombstone)
m.SetExpirationEpoch(t.GetExpirationEpoch())
m.SetSplitId(t.GetSplitID())
members := t.GetMembers()
memberMsg := make([]*refsGRPC.ObjectID, 0, len(members))
for i := range members {
memberMsg = append(memberMsg, refs.ObjectIDToGRPCMessage(members[i]))
}
m.SetMembers(memberMsg)
return m
}
// TombstoneFromGRPCMessage converts gRPC message into unified tombstone message.
func TombstoneFromGRPCMessage(m *tombstone.Tombstone) *Tombstone {
if m == nil {
return nil
}
t := new(Tombstone)
t.SetExpirationEpoch(m.GetExpirationEpoch())
t.SetSplitID(m.GetSplitId())
memberMsg := m.GetMembers()
members := make([]*refs.ObjectID, 0, len(memberMsg))
for i := range memberMsg {
members = append(members, refs.ObjectIDFromGRPCMessage(memberMsg[i]))
}
t.SetMembers(members)
return t
}

View File

@ -0,0 +1,26 @@
package tombstone
import (
refs "github.com/nspcc-dev/neofs-api-go/v2/refs/grpc"
)
// SetExpirationEpoch sets number of tombstone expiration epoch.
func (x *Tombstone) SetExpirationEpoch(v uint64) {
if x != nil {
x.ExpirationEpoch = v
}
}
// SetSplitId sets identifier of split object hierarchy.
func (x *Tombstone) SetSplitId(v []byte) {
if x != nil {
x.SplitId = v
}
}
// SetMembers sets list of objects to be deleted.
func (x *Tombstone) SetMembers(v []*refs.ObjectID) {
if x != nil {
x.Members = v
}
}

View File

@ -0,0 +1,26 @@
package tombstone
import (
tombstone "github.com/nspcc-dev/neofs-api-go/v2/tombstone/grpc"
"google.golang.org/protobuf/encoding/protojson"
)
func (s *Tombstone) MarshalJSON() ([]byte, error) {
return protojson.MarshalOptions{
EmitUnpopulated: true,
}.Marshal(
TombstoneToGRPCMessage(s),
)
}
func (s *Tombstone) UnmarshalJSON(data []byte) error {
msg := new(tombstone.Tombstone)
if err := protojson.Unmarshal(data, msg); err != nil {
return err
}
*s = *TombstoneFromGRPCMessage(msg)
return nil
}

View File

@ -0,0 +1,20 @@
package tombstone_test
import (
"testing"
"github.com/nspcc-dev/neofs-api-go/v2/tombstone"
"github.com/stretchr/testify/require"
)
func TestTombstoneJSON(t *testing.T) {
from := generateTombstone()
data, err := from.MarshalJSON()
require.NoError(t, err)
to := new(tombstone.Tombstone)
require.NoError(t, to.UnmarshalJSON(data))
require.Equal(t, from, to)
}

View File

@ -0,0 +1,83 @@
package tombstone
import (
"github.com/nspcc-dev/neofs-api-go/util/proto"
tombstone "github.com/nspcc-dev/neofs-api-go/v2/tombstone/grpc"
goproto "google.golang.org/protobuf/proto"
)
const (
expFNum = 1
splitIDFNum = 2
membersFNum = 3
)
// StableMarshal marshals unified tombstone message in a protobuf
// compatible way without field order shuffle.
func (s *Tombstone) StableMarshal(buf []byte) ([]byte, error) {
if s == nil {
return []byte{}, nil
}
if buf == nil {
buf = make([]byte, s.StableSize())
}
var (
offset, n int
err error
)
n, err = proto.UInt64Marshal(expFNum, buf[offset:], s.exp)
if err != nil {
return nil, err
}
offset += n
n, err = proto.BytesMarshal(splitIDFNum, buf[offset:], s.splitID)
if err != nil {
return nil, err
}
offset += n
for i := range s.members {
n, err = proto.NestedStructureMarshal(membersFNum, buf[offset:], s.members[i])
if err != nil {
return nil, err
}
offset += n
}
return buf, nil
}
// StableSize returns size of tombstone message marshalled by StableMarshal function.
func (s *Tombstone) StableSize() (size int) {
if s == nil {
return 0
}
size += proto.UInt64Size(expFNum, s.exp)
size += proto.BytesSize(splitIDFNum, s.splitID)
for i := range s.members {
size += proto.NestedStructureSize(membersFNum, s.members[i])
}
return size
}
// Unmarshal unmarshal tombstone message from its binary representation.
func (s *Tombstone) Unmarshal(data []byte) error {
m := new(tombstone.Tombstone)
if err := goproto.Unmarshal(data, m); err != nil {
return err
}
*s = *TombstoneFromGRPCMessage(m)
return nil
}

View File

@ -0,0 +1,39 @@
package tombstone_test
import (
"testing"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
"github.com/nspcc-dev/neofs-api-go/v2/tombstone"
"github.com/stretchr/testify/require"
)
func TestTombstone_StableMarshal(t *testing.T) {
from := generateTombstone()
t.Run("non empty", func(t *testing.T) {
wire, err := from.StableMarshal(nil)
require.NoError(t, err)
to := new(tombstone.Tombstone)
require.NoError(t, to.Unmarshal(wire))
require.Equal(t, from, to)
})
}
func generateTombstone() *tombstone.Tombstone {
t := new(tombstone.Tombstone)
oid1 := new(refs.ObjectID)
oid1.SetValue([]byte("Object ID 1"))
oid2 := new(refs.ObjectID)
oid2.SetValue([]byte("Object ID 2"))
t.SetExpirationEpoch(100)
t.SetSplitID([]byte("split ID"))
t.SetMembers([]*refs.ObjectID{oid1, oid2})
return t
}

View File

@ -0,0 +1,63 @@
package tombstone
import (
"github.com/nspcc-dev/neofs-api-go/v2/refs"
)
// Tombstone is a unified structure of Tombstone
// message from proto definition.
type Tombstone struct {
exp uint64
splitID []byte
members []*refs.ObjectID
}
// GetExpirationEpoch returns number of tombstone expiration epoch.
func (s *Tombstone) GetExpirationEpoch() uint64 {
if s != nil {
return s.exp
}
return 0
}
// SetExpirationEpoch sets number of tombstone expiration epoch.
func (s *Tombstone) SetExpirationEpoch(v uint64) {
if s != nil {
s.exp = v
}
}
// GetSplitID returns identifier of split object hierarchy.
func (s *Tombstone) GetSplitID() []byte {
if s != nil {
return s.splitID
}
return nil
}
// SetSplitID sets identifier of split object hierarchy.
func (s *Tombstone) SetSplitID(v []byte) {
if s != nil {
s.splitID = v
}
}
// GetMembers returns list of objects to be deleted.
func (s *Tombstone) GetMembers() []*refs.ObjectID {
if s != nil {
return s.members
}
return nil
}
// SetMembers sets list of objects to be deleted.
func (s *Tombstone) SetMembers(v []*refs.ObjectID) {
if s != nil {
s.members = v
}
}