diff --git a/v2/netmap/convert.go b/v2/netmap/convert.go index 5d126f8..b8713fe 100644 --- a/v2/netmap/convert.go +++ b/v2/netmap/convert.go @@ -21,3 +21,73 @@ func PlacementPolicyFromGRPCMessage(m *netmap.PlacementPolicy) *PlacementPolicy // TODO: fill me return nil } + +func AttributeToGRPCMessage(a *Attribute) *netmap.NodeInfo_Attribute { + if a == nil { + return nil + } + + m := new(netmap.NodeInfo_Attribute) + + m.SetKey(a.GetKey()) + m.SetValue(a.GetValue()) + + return m +} + +func AttributeFromGRPCMessage(m *netmap.NodeInfo_Attribute) *Attribute { + if m == nil { + return nil + } + + a := new(Attribute) + + a.SetKey(m.GetKey()) + a.SetValue(m.GetValue()) + + return a +} + +func NodeInfoToGRPCMessage(n *NodeInfo) *netmap.NodeInfo { + if n == nil { + return nil + } + + m := new(netmap.NodeInfo) + + m.SetPublicKey(n.GetPublicKey()) + m.SetAddress(n.GetAddress()) + + attr := n.GetAttributes() + attrMsg := make([]*netmap.NodeInfo_Attribute, 0, len(attr)) + + for i := range attr { + attrMsg = append(attrMsg, AttributeToGRPCMessage(attr[i])) + } + + m.SetAttributes(attrMsg) + + return m +} + +func NodeInfoFromGRPCMessage(m *netmap.NodeInfo) *NodeInfo { + if m == nil { + return nil + } + + a := new(NodeInfo) + + a.SetPublicKey(m.GetPublicKey()) + a.SetAddress(m.GetAddress()) + + attrMsg := m.GetAttributes() + attr := make([]*Attribute, 0, len(attrMsg)) + + for i := range attrMsg { + attr = append(attr, AttributeFromGRPCMessage(attrMsg[i])) + } + + a.SetAttributes(attr) + + return a +} diff --git a/v2/netmap/marshal.go b/v2/netmap/marshal.go index 99a24b4..b5025f5 100644 --- a/v2/netmap/marshal.go +++ b/v2/netmap/marshal.go @@ -1,5 +1,18 @@ package netmap +import ( + "github.com/nspcc-dev/neofs-api-go/util/proto" +) + +const ( + keyAttributeField = 1 + valueAttributeField = 2 + + keyNodeInfoField = 1 + addressNodeInfoField = 2 + attributesNodeInfoField = 3 +) + func (p *PlacementPolicy) StableMarshal(buf []byte) ([]byte, error) { // todo: implement me return nil, nil @@ -9,3 +22,97 @@ func (p *PlacementPolicy) StableSize() (size int) { // todo: implement me return 0 } + +func (a *Attribute) StableMarshal(buf []byte) ([]byte, error) { + if a == nil { + return []byte{}, nil + } + + if buf == nil { + buf = make([]byte, a.StableSize()) + } + + var ( + offset, n int + err error + ) + + n, err = proto.StringMarshal(keyAttributeField, buf[offset:], a.key) + if err != nil { + return nil, err + } + + offset += n + + n, err = proto.StringMarshal(valueAttributeField, buf[offset:], a.value) + if err != nil { + return nil, err + } + + return buf, nil +} + +func (a *Attribute) StableSize() (size int) { + if a == nil { + return 0 + } + + size += proto.StringSize(keyAttributeField, a.key) + size += proto.StringSize(valueAttributeField, a.value) + + return size +} + +func (ni *NodeInfo) StableMarshal(buf []byte) ([]byte, error) { + if ni == nil { + return []byte{}, nil + } + + if buf == nil { + buf = make([]byte, ni.StableSize()) + } + + var ( + offset, n int + err error + ) + + n, err = proto.BytesMarshal(keyNodeInfoField, buf[offset:], ni.publicKey) + if err != nil { + return nil, err + } + + offset += n + + n, err = proto.StringMarshal(addressNodeInfoField, buf[offset:], ni.address) + if err != nil { + return nil, err + } + + offset += n + + for i := range ni.attributes { + n, err = proto.NestedStructureMarshal(attributesNodeInfoField, buf[offset:], ni.attributes[i]) + if err != nil { + return nil, err + } + + offset += n + } + + return buf, nil +} + +func (ni *NodeInfo) StableSize() (size int) { + if ni == nil { + return 0 + } + + size += proto.BytesSize(keyNodeInfoField, ni.publicKey) + size += proto.StringSize(addressNodeInfoField, ni.address) + for i := range ni.attributes { + size += proto.NestedStructureSize(attributesNodeInfoField, ni.attributes[i]) + } + + return size +} diff --git a/v2/netmap/marshal_test.go b/v2/netmap/marshal_test.go new file mode 100644 index 0000000..1292c40 --- /dev/null +++ b/v2/netmap/marshal_test.go @@ -0,0 +1,66 @@ +package netmap_test + +import ( + "strconv" + "testing" + + "github.com/nspcc-dev/neofs-api-go/v2/netmap" + grpc "github.com/nspcc-dev/neofs-api-go/v2/netmap/grpc" + "github.com/stretchr/testify/require" +) + +func TestAttribute_StableMarshal(t *testing.T) { + from := generateAttribute("key", "value") + transport := new(grpc.NodeInfo_Attribute) + + t.Run("non empty", func(t *testing.T) { + wire, err := from.StableMarshal(nil) + require.NoError(t, err) + + err = transport.Unmarshal(wire) + require.NoError(t, err) + + to := netmap.AttributeFromGRPCMessage(transport) + require.Equal(t, from, to) + }) +} + +func TestNodeInfo(t *testing.T) { + from := generateNodeInfo("publicKey", "/multi/addr", 10) + transport := new(grpc.NodeInfo) + + t.Run("non empty", func(t *testing.T) { + wire, err := from.StableMarshal(nil) + require.NoError(t, err) + + err = transport.Unmarshal(wire) + require.NoError(t, err) + + to := netmap.NodeInfoFromGRPCMessage(transport) + require.Equal(t, from, to) + }) +} + +func generateAttribute(k, v string) *netmap.Attribute { + attr := new(netmap.Attribute) + attr.SetKey(k) + attr.SetValue(v) + + return attr +} + +func generateNodeInfo(key, addr string, n int) *netmap.NodeInfo { + nodeInfo := new(netmap.NodeInfo) + nodeInfo.SetPublicKey([]byte(key)) + nodeInfo.SetAddress(addr) + + attrs := make([]*netmap.Attribute, n) + for i := 0; i < n; i++ { + j := strconv.Itoa(n) + attrs[i] = generateAttribute("key"+j, "value"+j) + } + + nodeInfo.SetAttributes(attrs) + + return nodeInfo +} diff --git a/v2/netmap/types.go b/v2/netmap/types.go index 0f295c6..7df923c 100644 --- a/v2/netmap/types.go +++ b/v2/netmap/types.go @@ -3,3 +3,95 @@ package netmap type PlacementPolicy struct { // TODO: fill me } + +// Attribute of storage node. +type Attribute struct { + key string + value string +} + +// NodeInfo of storage node. +type NodeInfo struct { + publicKey []byte + address string + attributes []*Attribute +} + +// NodeState of storage node. +type NodeState uint32 + +const ( + Unspecified NodeState = iota + Online + Offline +) + +func (a *Attribute) GetKey() string { + if a != nil { + return a.key + } + + return "" +} + +func (a *Attribute) SetKey(v string) { + if a != nil { + a.key = v + } +} + +func (a *Attribute) GetValue() string { + if a != nil { + return a.value + } + + return "" +} + +func (a *Attribute) SetValue(v string) { + if a != nil { + a.value = v + } +} + +func (ni *NodeInfo) GetPublicKey() []byte { + if ni != nil { + return ni.publicKey + } + + return nil +} + +func (ni *NodeInfo) SetPublicKey(v []byte) { + if ni != nil { + ni.publicKey = v + } +} + +func (ni *NodeInfo) GetAddress() string { + if ni != nil { + return ni.address + } + + return "" +} + +func (ni *NodeInfo) SetAddress(v string) { + if ni != nil { + ni.address = v + } +} + +func (ni *NodeInfo) GetAttributes() []*Attribute { + if ni != nil { + return ni.attributes + } + + return nil +} + +func (ni *NodeInfo) SetAttributes(v []*Attribute) { + if ni != nil { + ni.attributes = v + } +}