From d45548c43b8f4eb69ad95445d340ce77e1518902 Mon Sep 17 00:00:00 2001 From: Evgeniy Kulikov Date: Fri, 3 Jul 2020 09:12:02 +0300 Subject: [PATCH] Implementing proto.Clone --- container/types.go | 8 ++++++++ hash/hash.go | 8 ++++++++ internal/proto.go | 3 +++ object/types.go | 26 ++++++++++++++++++++------ object/types_test.go | 21 +++++++++++++++++---- refs/address.go | 12 ++++++++++++ refs/cid.go | 8 ++++++++ refs/owner.go | 8 ++++++++ refs/types_test.go | 29 +++++++++++++++++++++++++++++ refs/uuid.go | 8 ++++++++ service/verify.go | 8 ++++++++ 11 files changed, 129 insertions(+), 10 deletions(-) diff --git a/container/types.go b/container/types.go index 38bdcff..be06b53 100644 --- a/container/types.go +++ b/container/types.go @@ -3,6 +3,7 @@ package container import ( "bytes" + "github.com/gogo/protobuf/proto" "github.com/google/uuid" "github.com/nspcc-dev/neofs-api-go/internal" "github.com/nspcc-dev/neofs-api-go/refs" @@ -63,6 +64,13 @@ func (m *Container) ID() (CID, error) { return refs.CIDForBytes(data), nil } +// Merge used by proto.Clone +func (m *Container) Merge(src proto.Message) { + if tmp, ok := src.(*Container); ok { + *m = *tmp + } +} + // Empty checks that container is empty. func (m *Container) Empty() bool { return m.Capacity == 0 || bytes.Equal(m.Salt.Bytes(), emptySalt) || bytes.Equal(m.OwnerID.Bytes(), emptyOwner) diff --git a/hash/hash.go b/hash/hash.go index 9d75278..4e2fb05 100644 --- a/hash/hash.go +++ b/hash/hash.go @@ -3,6 +3,7 @@ package hash import ( "bytes" + "github.com/gogo/protobuf/proto" "github.com/mr-tron/base58" "github.com/nspcc-dev/neofs-api-go/internal" "github.com/nspcc-dev/tzhash/tz" @@ -78,6 +79,13 @@ func (h Hash) Validate(hashes []Hash) bool { return err == nil && ok } +// Merge used by proto.Clone +func (h *Hash) Merge(src proto.Message) { + if tmp, ok := src.(*Hash); ok { + *h = *tmp + } +} + // Sum returns Tillich-ZĂ©mor checksum of data. func Sum(data []byte) Hash { return tz.Sum(data) } diff --git a/internal/proto.go b/internal/proto.go index 951168b..9a924b5 100644 --- a/internal/proto.go +++ b/internal/proto.go @@ -13,4 +13,7 @@ type Custom interface { MarshalTo(data []byte) (int, error) Unmarshal(data []byte) error proto.Message + + // Should contains for proto.Clone + proto.Merger } diff --git a/object/types.go b/object/types.go index 058cd70..392e624 100644 --- a/object/types.go +++ b/object/types.go @@ -163,6 +163,13 @@ func (m *Object) SetHeader(h *Header) { m.AddHeader(h) } +// Merge used by proto.Clone +func (m *Object) Merge(src proto.Message) { + if tmp, ok := src.(*Object); ok { + tmp.CopyTo(m) + } +} + func (m Header) typeOf(t isHeader_Value) (ok bool) { switch t.(type) { case *Header_Link: @@ -233,8 +240,15 @@ func (m *Object) Copy() (obj *Object) { // This function creates copies on every available data slice. func (m *Object) CopyTo(o *Object) { o.SystemHeader = m.SystemHeader - o.Headers = make([]Header, len(m.Headers)) - o.Payload = make([]byte, len(m.Payload)) + + if m.Headers != nil { + o.Headers = make([]Header, len(m.Headers)) + } + + if m.Payload != nil { + o.Payload = make([]byte, len(m.Payload)) + copy(o.Payload, m.Payload) + } for i := range m.Headers { switch v := m.Headers[i].Value.(type) { @@ -246,23 +260,23 @@ func (m *Object) CopyTo(o *Object) { }, } case *Header_HomoHash: + hash := proto.Clone(&v.HomoHash).(*Hash) o.Headers[i] = Header{ Value: &Header_HomoHash{ - HomoHash: v.HomoHash, + HomoHash: *hash, }, } case *Header_Token: + token := *v.Token o.Headers[i] = Header{ Value: &Header_Token{ - Token: v.Token, + Token: &token, }, } default: o.Headers[i] = *proto.Clone(&m.Headers[i]).(*Header) } } - - copy(o.Payload, m.Payload) } // Address returns object's address. diff --git a/object/types_test.go b/object/types_test.go index 0a7085f..18c13f1 100644 --- a/object/types_test.go +++ b/object/types_test.go @@ -4,6 +4,7 @@ import ( "bytes" "testing" + "github.com/gogo/protobuf/proto" "github.com/nspcc-dev/neofs-api-go/refs" "github.com/nspcc-dev/neofs-api-go/service" "github.com/nspcc-dev/neofs-api-go/storagegroup" @@ -192,11 +193,23 @@ func TestObject_Copy(t *testing.T) { }, }) - cp := obj.Copy() + { // Copying + cp := obj.Copy() - _, h := cp.LastHeader(HeaderType(TokenHdr)) - require.NotNil(t, h) - require.Equal(t, token, h.GetValue().(*Header_Token).Token) + _, h := cp.LastHeader(HeaderType(TokenHdr)) + require.NotNil(t, h) + require.Equal(t, token, h.GetValue().(*Header_Token).Token) + } + + { // Cloning + cl := proto.Clone(obj).(*Object) + require.Equal(t, obj, cl) + + _, h := cl.LastHeader(HeaderType(TokenHdr)) + h.GetToken().SetID(service.TokenID{3, 2, 1}) + + require.NotEqual(t, token, h.GetToken()) + } }) } diff --git a/refs/address.go b/refs/address.go index f07e317..ad5f420 100644 --- a/refs/address.go +++ b/refs/address.go @@ -4,6 +4,7 @@ import ( "crypto/sha256" "strings" + "github.com/gogo/protobuf/proto" "github.com/nspcc-dev/neofs-api-go/internal" ) @@ -66,3 +67,14 @@ func (m Address) Hash() ([]byte, error) { h := sha256.Sum256(append(m.ObjectID.Bytes(), m.CID.Bytes()...)) return h[:], nil } + +// Merge used by proto.Clone +func (m *Address) Merge(src proto.Message) { + if addr, ok := src.(*Address); ok { + cid := proto.Clone(&addr.CID).(*CID) + oid := proto.Clone(&addr.ObjectID).(*ObjectID) + + m.CID = *cid + m.ObjectID = *oid + } +} diff --git a/refs/cid.go b/refs/cid.go index 4f0cf39..83450a9 100644 --- a/refs/cid.go +++ b/refs/cid.go @@ -4,6 +4,7 @@ import ( "bytes" "crypto/sha256" + "github.com/gogo/protobuf/proto" "github.com/mr-tron/base58" "github.com/pkg/errors" ) @@ -94,3 +95,10 @@ func (c CID) Verify(data []byte) error { } return nil } + +// Merge used by proto.Clone +func (c *CID) Merge(src proto.Message) { + if cid, ok := src.(*CID); ok { + *c = *cid + } +} diff --git a/refs/owner.go b/refs/owner.go index 1aed00c..6534c35 100644 --- a/refs/owner.go +++ b/refs/owner.go @@ -4,6 +4,7 @@ import ( "bytes" "crypto/ecdsa" + "github.com/gogo/protobuf/proto" "github.com/mr-tron/base58" "github.com/nspcc-dev/neofs-api-go/chain" "github.com/pkg/errors" @@ -63,3 +64,10 @@ func (o *OwnerID) Unmarshal(data []byte) error { copy((*o)[:], data) return nil } + +// Merge used by proto.Clone +func (o *OwnerID) Merge(src proto.Message) { + if uid, ok := src.(*OwnerID); ok { + *o = *uid + } +} diff --git a/refs/types_test.go b/refs/types_test.go index 2fd3ced..5bba5b8 100644 --- a/refs/types_test.go +++ b/refs/types_test.go @@ -23,6 +23,19 @@ func TestSGID(t *testing.T) { require.NoError(t, sgid2.Unmarshal(data)) require.Equal(t, sgid1, sgid2) }) + + t.Run("check that proto.Clone works like expected", func(t *testing.T) { + var ( + sgid1 UUID + sgid2 *UUID + ) + + sgid1, err := NewSGID() + require.NoError(t, err) + + sgid2 = proto.Clone(&sgid1).(*SGID) + require.Equal(t, sgid1, *sgid2) + }) } func TestUUID(t *testing.T) { @@ -80,6 +93,18 @@ func TestOwnerID(t *testing.T) { require.NoError(t, u2.Unmarshal(data)) require.Equal(t, u1, u2) }) + + t.Run("check that proto.Clone works like expected", func(t *testing.T) { + var u2 *OwnerID + + key := test.DecodeKey(0) + + u1, err := NewOwnerID(&key.PublicKey) + require.NoError(t, err) + + u2 = proto.Clone(&u1).(*OwnerID) + require.Equal(t, u1, *u2) + }) } func TestAddress(t *testing.T) { @@ -109,4 +134,8 @@ func TestAddress(t *testing.T) { actual, err := ParseAddress(expect) require.NoError(t, err) require.Equal(t, expect, actual.String()) + + addr := proto.Clone(actual).(*Address) + require.Equal(t, actual, addr) + require.Equal(t, expect, addr.String()) } diff --git a/refs/uuid.go b/refs/uuid.go index 2ffc525..5a49b92 100644 --- a/refs/uuid.go +++ b/refs/uuid.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/hex" + "github.com/gogo/protobuf/proto" "github.com/google/uuid" "github.com/pkg/errors" ) @@ -74,3 +75,10 @@ func (u *UUID) Parse(id string) error { copy((*u)[:], tmp[:]) return nil } + +// Merge used by proto.Clone +func (u *UUID) Merge(src proto.Message) { + if tmp, ok := src.(*UUID); ok { + *u = *tmp + } +} diff --git a/service/verify.go b/service/verify.go index e1caa06..4a6ec3f 100644 --- a/service/verify.go +++ b/service/verify.go @@ -3,6 +3,7 @@ package service import ( "crypto/ecdsa" + "github.com/gogo/protobuf/proto" "github.com/nspcc-dev/neofs-api-go/internal" crypto "github.com/nspcc-dev/neofs-crypto" ) @@ -104,6 +105,13 @@ func (t testCustomField) MarshalTo(data []byte) (int, error) { return 0, nil } // Marshal skip, it's for test usage only. func (t testCustomField) Marshal() ([]byte, error) { return nil, nil } +// Merge used by proto.Clone +func (t *testCustomField) Merge(src proto.Message) { + if tmp, ok := src.(*testCustomField); ok { + *t = *tmp + } +} + // GetBearerToken wraps Bearer field and return BearerToken interface. // // If Bearer field value is nil, nil returns.