diff --git a/v2/storagegroup/marshal.go b/v2/storagegroup/marshal.go new file mode 100644 index 0000000..6a655a6 --- /dev/null +++ b/v2/storagegroup/marshal.go @@ -0,0 +1,83 @@ +package storagegroup + +import ( + "encoding/binary" + + "github.com/nspcc-dev/neofs-api-go/util/proto" +) + +const ( + SizeField = 1 + HashField = 2 + ExpirationField = 3 + ObjectIDsField = 4 +) + +func (s *StorageGroup) StableMarshal(buf []byte) ([]byte, error) { + if s == nil { + return []byte{}, nil + } + + if buf == nil { + buf = make([]byte, s.StableSize()) + } + + var ( + offset, n int + prefix uint64 + err error + ) + + n, err = proto.UInt64Marshal(SizeField, buf, s.size) + if err != nil { + return nil, err + } + offset += n + + n, err = proto.BytesMarshal(HashField, buf[offset:], s.hash) + if err != nil { + return nil, err + } + offset += n + + n, err = proto.UInt64Marshal(ExpirationField, buf[offset:], s.exp) + if err != nil { + return nil, err + } + offset += n + + prefix, _ = proto.NestedStructurePrefix(ObjectIDsField) + for i := range s.members { + offset += binary.PutUvarint(buf[offset:], prefix) + + n = s.members[i].StableSize() + offset += binary.PutUvarint(buf[offset:], uint64(n)) + + _, err = s.members[i].StableMarshal(buf[offset:]) + if err != nil { + return nil, err + } + + offset += n + } + + return buf, nil +} + +func (s *StorageGroup) StableSize() (size int) { + if s == nil { + return 0 + } + + size += proto.UInt64Size(SizeField, s.size) + size += proto.BytesSize(HashField, s.hash) + size += proto.UInt64Size(ExpirationField, s.exp) + + _, ln := proto.NestedStructurePrefix(ObjectIDsField) + for i := range s.members { + n := s.members[i].StableSize() + size += ln + proto.VarUIntSize(uint64(n)) + n + } + + return size +} diff --git a/v2/storagegroup/marshal_test.go b/v2/storagegroup/marshal_test.go new file mode 100644 index 0000000..e0db5c5 --- /dev/null +++ b/v2/storagegroup/marshal_test.go @@ -0,0 +1,36 @@ +package storagegroup_test + +import ( + "testing" + + "github.com/nspcc-dev/neofs-api-go/v2/refs" + "github.com/nspcc-dev/neofs-api-go/v2/storagegroup" + grpc "github.com/nspcc-dev/neofs-api-go/v2/storagegroup/grpc" + "github.com/stretchr/testify/require" +) + +func TestStorageGroup_StableMarshal(t *testing.T) { + ownerID1 := new(refs.ObjectID) + ownerID1.SetValue([]byte("Object ID 1")) + ownerID2 := new(refs.ObjectID) + ownerID2.SetValue([]byte("Object ID 2")) + + storageGroupFrom := new(storagegroup.StorageGroup) + transport := new(grpc.StorageGroup) + + t.Run("non empty", func(t *testing.T) { + storageGroupFrom.SetValidationDataSize(300) + storageGroupFrom.SetValidationHash([]byte("Homomorphic hash value")) + storageGroupFrom.SetExpirationEpoch(100) + storageGroupFrom.SetMembers([]*refs.ObjectID{ownerID1, ownerID2}) + + wire, err := storageGroupFrom.StableMarshal(nil) + require.NoError(t, err) + + err = transport.Unmarshal(wire) + require.NoError(t, err) + + storageGroupTo := storagegroup.StorageGroupFromGRPCMessage(transport) + require.Equal(t, storageGroupFrom, storageGroupTo) + }) +}