diff --git a/api/netmap/types.go b/api/netmap/types.go index 877357d..ef1888d 100644 --- a/api/netmap/types.go +++ b/api/netmap/types.go @@ -1,6 +1,9 @@ package netmap import ( + "bytes" + "slices" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/refs" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/session" ) @@ -382,6 +385,15 @@ func (a *Attribute) SetParents(parent []string) { a.parents = parent } +// Clone returns a copy of Attribute. +func (a *Attribute) Clone() *Attribute { + return &Attribute{ + parents: slices.Clone(a.parents), + value: a.value, + key: a.key, + } +} + func (ni *NodeInfo) GetPublicKey() []byte { if ni != nil { return ni.publicKey @@ -465,6 +477,20 @@ func (ni *NodeInfo) SetState(state NodeState) { ni.state = state } +// Clone returns a copy of NodeInfo. +func (ni *NodeInfo) Clone() *NodeInfo { + dst := NodeInfo{ + addresses: slices.Clone(ni.addresses), + publicKey: bytes.Clone(ni.publicKey), + state: ni.state, + attributes: make([]Attribute, len(ni.attributes)), + } + for i, v := range ni.attributes { + dst.attributes[i] = *v.Clone() + } + return &dst +} + func (l *LocalNodeInfoResponseBody) GetVersion() *refs.Version { if l != nil { return l.version diff --git a/api/netmap/types_test.go b/api/netmap/types_test.go new file mode 100644 index 0000000..47d9d7b --- /dev/null +++ b/api/netmap/types_test.go @@ -0,0 +1,48 @@ +package netmap + +import ( + "bytes" + "slices" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNodeInfo_Clone(t *testing.T) { + var ni NodeInfo + ni.publicKey = []byte{2} + attr := Attribute{ + key: "key", + value: "value", + parents: []string{"parent", "parent2"}, + } + ni.attributes = []Attribute{attr} + ni.addresses = []string{"5", "6"} + + c := ni.Clone() + + require.True(t, c != &ni) + require.True(t, bytes.Equal(c.publicKey, ni.publicKey)) + require.True(t, &(c.publicKey[0]) != &(ni.publicKey[0])) + require.True(t, &(c.attributes[0]) != &(ni.attributes[0])) + require.True(t, slices.Compare(c.addresses, ni.addresses) == 0) + require.True(t, &(c.addresses[0]) != &(ni.addresses[0])) +} + +func TestAttribute_Clone(t *testing.T) { + attr := Attribute{ + key: "key", + value: "value", + parents: []string{"parent1", "parent2"}, + } + + c := attr.Clone() + + require.True(t, c != &attr) + require.True(t, c.key == attr.key) + require.True(t, &(c.key) != &(attr.key)) + require.True(t, &(c.value) != &(attr.value)) + require.True(t, c.value == attr.value) + require.True(t, &(c.parents[0]) != &(attr.parents[0])) + require.True(t, slices.Compare(c.parents, attr.parents) == 0) +} diff --git a/netmap/netmap.go b/netmap/netmap.go index 0d3b668..9ece18b 100644 --- a/netmap/netmap.go +++ b/netmap/netmap.go @@ -96,6 +96,18 @@ func (m NetMap) Epoch() uint64 { return m.epoch } +// Clone returns a copy of NetMap. +func (m *NetMap) Clone() *NetMap { + dst := NetMap{ + epoch: m.epoch, + nodes: make([]NodeInfo, len(m.nodes)), + } + for i, node := range m.nodes { + dst.nodes[i] = *node.Clone() + } + return &dst +} + // nodes is a slice of NodeInfo instances needed for HRW sorting. type nodes []NodeInfo diff --git a/netmap/netmap_test.go b/netmap/netmap_test.go index 2aab542..5be5412 100644 --- a/netmap/netmap_test.go +++ b/netmap/netmap_test.go @@ -1,6 +1,7 @@ package netmap_test import ( + "bytes" "testing" v2netmap "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/netmap" @@ -45,3 +46,28 @@ func TestNetMap_SetEpoch(t *testing.T) { require.EqualValues(t, e, m.Epoch()) } + +func TestNetMap_Clone(t *testing.T) { + nm := new(netmap.NetMap) + nm.SetEpoch(1) + var ni netmap.NodeInfo + ni.SetPublicKey([]byte{1, 2, 3}) + nm.SetNodes([]netmap.NodeInfo{ni}) + + clone := nm.Clone() + + require.True(t, clone != nm) + require.True(t, &(clone.Nodes()[0]) != &(nm.Nodes()[0])) + + var clonev2 v2netmap.NetMap + clone.WriteToV2(&clonev2) + var bufClone []byte + bufClone = clonev2.StableMarshal(bufClone) + + var nmv2 v2netmap.NetMap + nm.WriteToV2(&nmv2) + var bufNM []byte + bufNM = nmv2.StableMarshal(bufNM) + + require.True(t, bytes.Equal(bufClone, bufNM)) +} diff --git a/netmap/node_info.go b/netmap/node_info.go index 0d250ce..5274099 100644 --- a/netmap/node_info.go +++ b/netmap/node_info.go @@ -563,6 +563,14 @@ func (x *NodeInfo) SetStatus(state NodeState) { x.m.SetState(netmap.NodeState(state)) } +// Clone returns a copy of NodeInfo. +func (x *NodeInfo) Clone() *NodeInfo { + return &NodeInfo{ + hash: x.hash, + m: *x.m.Clone(), + } +} + // String implements fmt.Stringer. // // String is designed to be human-readable, and its format MAY differ between diff --git a/netmap/node_info_test.go b/netmap/node_info_test.go index 54c78b2..965213e 100644 --- a/netmap/node_info_test.go +++ b/netmap/node_info_test.go @@ -108,3 +108,12 @@ func TestNodeInfo_ExternalAddr(t *testing.T) { n.SetExternalAddresses(addr[1:]...) require.Equal(t, addr[1:], n.ExternalAddresses()) } + +func TestNodeInfo_Clone(t *testing.T) { + var ni NodeInfo + ni.SetPublicKey([]byte{2, 3}) + + c := ni.Clone() + require.True(t, c != &ni) + require.True(t, &(c.PublicKey()[0]) != &(ni.PublicKey()[0])) +}