From 21bdc82fb5f74e89dfa9a13f821ffa9e37faaaa2 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Thu, 1 Oct 2020 20:12:53 +0300 Subject: [PATCH] [#64] core/object: Implement tombstone content messages Signed-off-by: Leonard Lyubich --- pkg/core/object/tombstone.go | 163 ++++++++++++++++++++++++++++++ pkg/core/object/tombstone_test.go | 43 ++++++++ 2 files changed, 206 insertions(+) create mode 100644 pkg/core/object/tombstone.go create mode 100644 pkg/core/object/tombstone_test.go diff --git a/pkg/core/object/tombstone.go b/pkg/core/object/tombstone.go new file mode 100644 index 00000000..924d4690 --- /dev/null +++ b/pkg/core/object/tombstone.go @@ -0,0 +1,163 @@ +package object + +import ( + "encoding/binary" + "fmt" + "io" + + "github.com/nspcc-dev/neofs-api-go/pkg/object" + "github.com/nspcc-dev/neofs-api-go/util/proto" + "github.com/nspcc-dev/neofs-api-go/v2/refs" + "github.com/pkg/errors" +) + +// FIXME: replace this code to neofs-api-go + +const addrListField = 1 + +type TombstoneContent TombstoneContentV2 + +type TombstoneContentV2 struct { + addrList []*refs.Address +} + +func NewTombstoneContentFromV2(cV2 *TombstoneContentV2) *TombstoneContent { + return (*TombstoneContent)(cV2) +} + +func NewTombstoneContent() *TombstoneContent { + return NewTombstoneContentFromV2(new(TombstoneContentV2)) +} + +func (c *TombstoneContent) MarshalBinary() ([]byte, error) { + return c.ToV2().StableMarshal(nil) +} + +func (c *TombstoneContent) SetAddressList(v ...*object.Address) { + if c != nil { + addrV2 := make([]*refs.Address, 0, len(v)) + + for i := range v { + addrV2 = append(addrV2, v[i].ToV2()) + } + + (*TombstoneContentV2)(c).SetAddressList(addrV2) + } +} + +func (c *TombstoneContent) GetAddressList() []*object.Address { + if c != nil { + addrV2 := (*TombstoneContentV2)(c).GetAddressList() + addr := make([]*object.Address, 0, len(addrV2)) + + for i := range addrV2 { + addr = append(addr, object.NewAddressFromV2(addrV2[i])) + } + + return addr + } + + return nil +} + +func (c *TombstoneContent) ToV2() *TombstoneContentV2 { + return (*TombstoneContentV2)(c) +} + +func (c *TombstoneContentV2) StableMarshal(buf []byte) ([]byte, error) { + if c == nil { + return make([]byte, 0), nil + } + + if buf == nil { + buf = make([]byte, c.StableSize()) + } + + offset := 0 + + for i := range c.addrList { + n, err := proto.NestedStructureMarshal(addrListField, buf[offset:], c.addrList[i]) + if err != nil { + return nil, err + } + + offset += n + } + + return buf, nil +} + +func (c *TombstoneContentV2) StableSize() (sz int) { + if c == nil { + return + } + + for i := range c.addrList { + sz += proto.NestedStructureSize(addrListField, c.addrList[i]) + } + + return +} + +func (c *TombstoneContentV2) StableUnmarshal(data []byte) error { + c.addrList = c.addrList[:0] + + for len(data) > 0 { + expPrefix, _ := proto.NestedStructurePrefix(addrListField) + + prefix, ln := binary.Uvarint(data) + if ln <= 0 { + return io.ErrUnexpectedEOF + } else if expPrefix != prefix { + return errors.Errorf("wrong prefix %d", prefix) + } + + data = data[ln:] + + sz, ln := binary.Uvarint(data) + if ln <= 0 { + return io.ErrUnexpectedEOF + } + + data = data[ln:] + + addr := new(refs.Address) + if err := addr.StableUnmarshal(data[:sz]); err != nil { + fmt.Println(err) + return err + } + + c.addrList = append(c.addrList, addr) + + data = data[sz:] + } + + return nil +} + +func (c *TombstoneContentV2) SetAddressList(v []*refs.Address) { + if c != nil { + c.addrList = v + } +} + +func (c *TombstoneContentV2) GetAddressList() []*refs.Address { + if c != nil { + return c.addrList + } + + return nil +} + +func TombstoneContentFromBytes(data []byte) (*TombstoneContent, error) { + if len(data) == 0 { + return nil, nil + } + + cV2 := new(TombstoneContentV2) + if err := cV2.StableUnmarshal(data); err != nil { + return nil, err + } + + return NewTombstoneContentFromV2(cV2), nil +} diff --git a/pkg/core/object/tombstone_test.go b/pkg/core/object/tombstone_test.go new file mode 100644 index 00000000..7ca935c6 --- /dev/null +++ b/pkg/core/object/tombstone_test.go @@ -0,0 +1,43 @@ +package object + +import ( + "crypto/sha256" + "testing" + + "github.com/nspcc-dev/neofs-api-go/pkg/container" + "github.com/nspcc-dev/neofs-api-go/pkg/object" + "github.com/stretchr/testify/require" +) + +func TestTombstoneContent_MarshalBinary(t *testing.T) { + cid1 := container.NewID() + cid1.SetSHA256([sha256.Size]byte{1, 2}) + + id1 := object.NewID() + id1.SetSHA256([sha256.Size]byte{3, 4}) + + addr1 := object.NewAddress() + addr1.SetObjectID(id1) + addr1.SetContainerID(cid1) + + cid2 := container.NewID() + cid2.SetSHA256([sha256.Size]byte{5, 6}) + + id2 := object.NewID() + id2.SetSHA256([sha256.Size]byte{7, 8}) + + addr2 := object.NewAddress() + addr2.SetObjectID(id2) + addr2.SetContainerID(cid2) + + c := NewTombstoneContent() + c.SetAddressList(addr1, addr2) + + data, err := c.MarshalBinary() + require.NoError(t, err) + + c2, err := TombstoneContentFromBytes(data) + require.NoError(t, err) + + require.Equal(t, c, c2) +}