[#79] Implement type for subnet ID

Create `subnet` package. Define `ID` type of subnet identifiers.
Implement encoding and support NeoFS API V2 protocol. Provide method to
init instance from integer. Implement function which checks if `ID`
instance refers to zero subnet.

Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
Leonard Lyubich 2021-11-23 15:41:55 +03:00 committed by LeL
parent d9317cbea1
commit b8989e3abb
5 changed files with 200 additions and 1 deletions

2
go.mod
View file

@ -9,7 +9,7 @@ require (
github.com/hashicorp/golang-lru v0.5.4
github.com/mr-tron/base58 v1.2.0
github.com/nspcc-dev/hrw v1.0.9
github.com/nspcc-dev/neofs-api-go/v2 v2.11.0-pre.0.20211118144033-580f6c5554ff
github.com/nspcc-dev/neofs-api-go/v2 v2.11.0-pre.0.20211124141318-d93828f46514
github.com/nspcc-dev/neofs-crypto v0.3.0
github.com/nspcc-dev/rfc6979 v0.2.0
github.com/stretchr/testify v1.7.0

BIN
go.sum

Binary file not shown.

89
subnet/id/id.go Normal file
View file

@ -0,0 +1,89 @@
package subnetid
import (
"fmt"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
)
// ID represents NeoFS subnet identifier.
//
// The type is compatible with the corresponding message from NeoFS API V2 protocol.
//
// Zero value and nil pointer is equivalent to zero subnet ID.
type ID refs.SubnetID
// FromV2 initializes ID from refs.SubnetID message structure. Must not be called on nil.
//
// Note: nil refs.SubnetID corresponds to zero ID value or nil pointer to it.
func (x *ID) FromV2(msg refs.SubnetID) {
*x = ID(msg)
}
// WriteToV2 writes ID to refs.SubnetID message structure. The message must not be nil.
//
// Note: nil ID corresponds to zero refs.SubnetID value or nil pointer to it.
func (x ID) WriteToV2(msg *refs.SubnetID) {
*msg = refs.SubnetID(x)
}
// Equals returns true iff both instances identify the same subnet.
//
// Method is NPE-safe: nil pointer equals to pointer to zero value.
func (x *ID) Equals(x2 *ID) bool {
return (*refs.SubnetID)(x).GetValue() == (*refs.SubnetID)(x2).GetValue()
}
// MarshalText encodes ID into text format according to particular NeoFS API protocol.
// Supported versions:
// * V2 (see refs.SubnetID type).
//
// Implements encoding.TextMarshaler.
func (x *ID) MarshalText() ([]byte, error) {
return (*refs.SubnetID)(x).MarshalText()
}
// UnmarshalText decodes ID from the text according to particular NeoFS API protocol.
// Must not be called on nil. Supported versions:
// * V2 (see refs.SubnetID type).
//
// Implements encoding.TextUnmarshaler.
func (x *ID) UnmarshalText(text []byte) error {
return (*refs.SubnetID)(x).UnmarshalText(text)
}
// String returns string representation of ID using MarshalText.
// Returns string with message on error.
//
// Implements fmt.Stringer.
func (x *ID) String() string {
text, err := x.MarshalText()
if err != nil {
return fmt.Sprintf("<invalid> %v", err)
}
return string(text)
}
// Marshal encodes ID into a binary format of NeoFS API V2 protocol (Protocol Buffers with direct field order).
func (x *ID) Marshal() ([]byte, error) {
return (*refs.SubnetID)(x).StableMarshal(nil)
}
// Unmarshal decodes ID from NeoFS API V2 binary format (see Marshal). Must not be called on nil.
//
// Note: empty data corresponds to zero ID value or nil pointer to it.
func (x *ID) Unmarshal(data []byte) error {
return (*refs.SubnetID)(x).Unmarshal(data)
}
// SetNumber sets ID value in uint32 format. Must not be called on nil.
// By default, number is 0 which refers to zero subnet.
func (x *ID) SetNumber(num uint32) {
(*refs.SubnetID)(x).SetValue(num)
}
// IsZero returns true iff the ID refers to zero subnet.
func IsZero(id ID) bool {
return id.Equals(nil)
}

94
subnet/id/id_test.go Normal file
View file

@ -0,0 +1,94 @@
package subnetid_test
import (
"testing"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
subnetidtest "github.com/nspcc-dev/neofs-sdk-go/subnet/id/test"
"github.com/stretchr/testify/require"
)
func TestIsZero(t *testing.T) {
var id subnetid.ID
require.True(t, subnetid.IsZero(id))
id.SetNumber(13)
require.False(t, subnetid.IsZero(id))
id.SetNumber(0)
require.True(t, subnetid.IsZero(id))
}
func TestID_FromV2(t *testing.T) {
const num = 13
var id1 subnetid.ID
id1.SetNumber(num)
var idv2 refs.SubnetID
idv2.SetValue(num)
var id2 subnetid.ID
id2.FromV2(idv2)
require.True(t, id1.Equals(&id2))
}
func TestID_WriteToV2(t *testing.T) {
const num = 13
var (
id subnetid.ID
idv2 refs.SubnetID
)
id.WriteToV2(&idv2)
require.Zero(t, idv2.GetValue())
id.SetNumber(num)
id.WriteToV2(&idv2)
require.EqualValues(t, num, idv2.GetValue())
}
func TestID_Equals(t *testing.T) {
const num = 13
var id1, id2, idOther, id0 subnetid.ID
id0.Equals(nil)
id1.SetNumber(num)
id2.SetNumber(num)
idOther.SetNumber(num + 1)
require.True(t, id1.Equals(&id2))
require.False(t, id1.Equals(&idOther))
require.False(t, id2.Equals(&idOther))
}
func TestSubnetIDEncoding(t *testing.T) {
id := subnetidtest.GenerateID()
t.Run("binary", func(t *testing.T) {
data, err := id.Marshal()
require.NoError(t, err)
var id2 subnetid.ID
require.NoError(t, id2.Unmarshal(data))
require.True(t, id2.Equals(id))
})
t.Run("text", func(t *testing.T) {
data, err := id.MarshalText()
require.NoError(t, err)
var id2 subnetid.ID
require.NoError(t, id2.UnmarshalText(data))
require.True(t, id2.Equals(id))
})
}

16
subnet/id/test/id.go Normal file
View file

@ -0,0 +1,16 @@
package subnetidtest
import (
"math/rand"
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
)
// GenerateID generates and returns random subnetid.ID using math/rand.Uint32.
func GenerateID() *subnetid.ID {
var id subnetid.ID
id.SetNumber(rand.Uint32())
return &id
}