From fadd47f4fb43b8d4f2f358b96afd78431f2f3631 Mon Sep 17 00:00:00 2001
From: Evgenii Stratonikov <evgeniy@nspcc.ru>
Date: Mon, 28 Feb 2022 15:06:51 +0300
Subject: [PATCH] [#376] object: remove pointer from Attribute slice
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

```
name                           old time/op    new time/op    delta
AttributesMarshal/marshal-8      4.44µs ± 9%    2.72µs ± 0%  -38.79%  (p=0.000 n=10+9)
AttributesMarshal/unmarshal-8    3.16µs ±13%    0.55µs ± 4%  -82.60%  (p=0.000 n=10+10)

name                           old alloc/op   new alloc/op   delta
AttributesMarshal/marshal-8      4.42kB ± 0%    4.42kB ± 0%     ~     (all equal)
AttributesMarshal/unmarshal-8    2.02kB ± 0%    1.79kB ± 0%  -11.11%  (p=0.000 n=10+10)

name                           old allocs/op  new allocs/op  delta
AttributesMarshal/marshal-8        51.0 ± 0%      51.0 ± 0%     ~     (all equal)
AttributesMarshal/unmarshal-8      51.0 ± 0%       1.0 ± 0%  -98.04%  (p=0.000 n=10+10)
```

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
---
 object/attributes.go      | 22 ++++++++--------------
 object/attributes_test.go |  2 +-
 object/bench_test.go      |  6 ++++--
 object/convert.go         | 14 ++++----------
 object/marshal.go         |  4 ++--
 object/test/generate.go   |  8 ++++----
 object/types.go           |  6 +++---
 7 files changed, 26 insertions(+), 36 deletions(-)

diff --git a/object/attributes.go b/object/attributes.go
index 2e701bc7..cc828738 100644
--- a/object/attributes.go
+++ b/object/attributes.go
@@ -108,26 +108,20 @@ func WriteNotificationInfo(o *Object, ni NotificationInfo) {
 		attrs = attrs[:len(attrs)-1]
 	}
 
-	notifyAttrs := make([]*Attribute, 0, 2)
-
 	if !changedEpoch {
-		eAttr := new(Attribute)
-		eAttr.SetKey(SysAttributeTickEpoch)
-		eAttr.SetValue(epoch)
-
-		notifyAttrs = append(notifyAttrs, eAttr)
+		index := len(attrs)
+		attrs = append(attrs, Attribute{})
+		attrs[index].SetKey(SysAttributeTickEpoch)
+		attrs[index].SetValue(epoch)
 	}
 
 	if !changedTopic && topic != "" {
-		tAttr := new(Attribute)
-		tAttr.SetKey(SysAttributeTickTopic)
-		tAttr.SetValue(topic)
-
-		notifyAttrs = append(notifyAttrs, tAttr)
+		index := len(attrs)
+		attrs = append(attrs, Attribute{})
+		attrs[index].SetKey(SysAttributeTickTopic)
+		attrs[index].SetValue(topic)
 	}
 
-	attrs = append(attrs, notifyAttrs...)
-
 	h.SetAttributes(attrs)
 }
 
diff --git a/object/attributes_test.go b/object/attributes_test.go
index 8556f130..5bf411a5 100644
--- a/object/attributes_test.go
+++ b/object/attributes_test.go
@@ -42,7 +42,7 @@ func TestSetNotification(t *testing.T) {
 func TestGetNotification(t *testing.T) {
 	o := new(Object)
 
-	attr := []*Attribute{
+	attr := []Attribute{
 		{SysAttributeTickEpoch, "10"},
 		{SysAttributeTickTopic, "test"},
 	}
diff --git a/object/bench_test.go b/object/bench_test.go
index 417c0dfb..90fab730 100644
--- a/object/bench_test.go
+++ b/object/bench_test.go
@@ -3,6 +3,8 @@ package object
 import (
 	"math/rand"
 	"testing"
+
+	"github.com/stretchr/testify/require"
 )
 
 func randString(n int) string {
@@ -14,13 +16,13 @@ func randString(n int) string {
 }
 
 func BenchmarkAttributesMarshal(b *testing.B) {
-	attrs := make([]*Attribute, 50)
+	attrs := make([]Attribute, 50)
 	for i := range attrs {
-		attrs[i] = new(Attribute)
 		attrs[i].key = SysAttributePrefix + randString(10)
 		attrs[i].val = randString(10)
 	}
 	raw := AttributesToGRPC(attrs)
+	require.Equal(b, len(raw), len(attrs))
 
 	b.Run("marshal", func(b *testing.B) {
 		b.ReportAllocs()
diff --git a/object/convert.go b/object/convert.go
index 80213df6..d574e79b 100644
--- a/object/convert.go
+++ b/object/convert.go
@@ -142,7 +142,7 @@ func (a *Attribute) FromGRPCMessage(m grpc.Message) error {
 	return nil
 }
 
-func AttributesToGRPC(xs []*Attribute) (res []*object.Header_Attribute) {
+func AttributesToGRPC(xs []Attribute) (res []*object.Header_Attribute) {
 	if xs != nil {
 		res = make([]*object.Header_Attribute, 0, len(xs))
 
@@ -154,23 +154,17 @@ func AttributesToGRPC(xs []*Attribute) (res []*object.Header_Attribute) {
 	return
 }
 
-func AttributesFromGRPC(xs []*object.Header_Attribute) (res []*Attribute, err error) {
+func AttributesFromGRPC(xs []*object.Header_Attribute) (res []Attribute, err error) {
 	if xs != nil {
-		res = make([]*Attribute, 0, len(xs))
+		res = make([]Attribute, len(xs))
 
 		for i := range xs {
-			var x *Attribute
-
 			if xs[i] != nil {
-				x = new(Attribute)
-
-				err = x.FromGRPCMessage(xs[i])
+				err = res[i].FromGRPCMessage(xs[i])
 				if err != nil {
 					return
 				}
 			}
-
-			res = append(res, x)
 		}
 	}
 
diff --git a/object/marshal.go b/object/marshal.go
index 44783f02..24d8d86b 100644
--- a/object/marshal.go
+++ b/object/marshal.go
@@ -394,7 +394,7 @@ func (h *Header) StableMarshal(buf []byte) ([]byte, error) {
 	offset += n
 
 	for i := range h.attr {
-		n, err = proto.NestedStructureMarshal(hdrAttributesField, buf[offset:], h.attr[i])
+		n, err = proto.NestedStructureMarshal(hdrAttributesField, buf[offset:], &h.attr[i])
 		if err != nil {
 			return nil, err
 		}
@@ -425,7 +425,7 @@ func (h *Header) StableSize() (size int) {
 	size += proto.NestedStructureSize(hdrHomomorphicHashField, h.homoHash)
 	size += proto.NestedStructureSize(hdrSessionTokenField, h.sessionToken)
 	for i := range h.attr {
-		size += proto.NestedStructureSize(hdrAttributesField, h.attr[i])
+		size += proto.NestedStructureSize(hdrAttributesField, &h.attr[i])
 	}
 	size += proto.NestedStructureSize(hdrSplitField, h.split)
 
diff --git a/object/test/generate.go b/object/test/generate.go
index d2f80cb3..298bc9a9 100644
--- a/object/test/generate.go
+++ b/object/test/generate.go
@@ -35,13 +35,13 @@ func GenerateAttribute(empty bool) *object.Attribute {
 	return m
 }
 
-func GenerateAttributes(empty bool) []*object.Attribute {
-	var res []*object.Attribute
+func GenerateAttributes(empty bool) []object.Attribute {
+	var res []object.Attribute
 
 	if !empty {
 		res = append(res,
-			GenerateAttribute(false),
-			GenerateAttribute(false),
+			*GenerateAttribute(false),
+			*GenerateAttribute(false),
 		)
 	}
 
diff --git a/object/types.go b/object/types.go
index ed3cdffe..596ed3ec 100644
--- a/object/types.go
+++ b/object/types.go
@@ -56,7 +56,7 @@ type Header struct {
 
 	sessionToken *session.Token
 
-	attr []*Attribute
+	attr []Attribute
 
 	split *SplitHeader
 }
@@ -654,7 +654,7 @@ func (h *Header) SetSessionToken(v *session.Token) {
 	}
 }
 
-func (h *Header) GetAttributes() []*Attribute {
+func (h *Header) GetAttributes() []Attribute {
 	if h != nil {
 		return h.attr
 	}
@@ -662,7 +662,7 @@ func (h *Header) GetAttributes() []*Attribute {
 	return nil
 }
 
-func (h *Header) SetAttributes(v []*Attribute) {
+func (h *Header) SetAttributes(v []Attribute) {
 	if h != nil {
 		h.attr = v
 	}