diff --git a/v2/accounting/accounting.go b/v2/accounting/accounting.go index 1576a9a..1ca3d26 100644 --- a/v2/accounting/accounting.go +++ b/v2/accounting/accounting.go @@ -49,22 +49,6 @@ func (b *BalanceRequestBody) SetOwnerID(v *refs.OwnerID) { } } -func (r *BalanceRequestBody) StableMarshal(buf []byte) ([]byte, error) { - if r == nil { - return nil, nil - } - - // TODO: do not use hack - _, err := BalanceRequestBodyToGRPCMessage(r).MarshalTo(buf) - - return buf, err -} - -func (r *BalanceRequestBody) StableSize() int { - // TODO: do not use hack - return BalanceRequestBodyToGRPCMessage(r).Size() -} - func (b *BalanceRequest) GetBody() *BalanceRequestBody { if b != nil { return b.body diff --git a/v2/accounting/marshal.go b/v2/accounting/marshal.go new file mode 100644 index 0000000..a0d73b1 --- /dev/null +++ b/v2/accounting/marshal.go @@ -0,0 +1,116 @@ +package accounting + +import ( + "github.com/nspcc-dev/neofs-api-go/util/proto" +) + +const ( + decimalValueField = 1 + decimalPrecisionField = 2 + + balanceReqBodyOwnerField = 1 + + balanceRespBodyDecimalField = 1 +) + +func (d *Decimal) StableMarshal(buf []byte) ([]byte, error) { + if d == nil { + return []byte{}, nil + } + + if buf == nil { + buf = make([]byte, d.StableSize()) + } + + var ( + offset, n int + err error + ) + + n, err = proto.Int64Marshal(decimalValueField, buf[offset:], d.val) + if err != nil { + return nil, err + } + + offset += n + + n, err = proto.UInt32Marshal(decimalPrecisionField, buf[offset:], d.prec) + if err != nil { + return nil, err + } + + return buf, nil +} + +func (d *Decimal) StableSize() (size int) { + if d == nil { + return 0 + } + + size += proto.Int64Size(decimalValueField, d.val) + size += proto.UInt32Size(decimalPrecisionField, d.prec) + + return size +} + +func (b *BalanceRequestBody) StableMarshal(buf []byte) ([]byte, error) { + if b == nil { + return []byte{}, nil + } + + if buf == nil { + buf = make([]byte, b.StableSize()) + } + + if b.ownerID != nil { + _, err := proto.NestedStructureMarshal(balanceReqBodyOwnerField, buf, b.ownerID) + if err != nil { + return nil, err + } + } + + return buf, nil +} + +func (b *BalanceRequestBody) StableSize() (size int) { + if b == nil { + return 0 + } + + if b.ownerID != nil { + size = proto.NestedStructureSize(balanceReqBodyOwnerField, b.ownerID) + } + + return size +} + +func (br *BalanceResponseBody) StableMarshal(buf []byte) ([]byte, error) { + if br == nil { + return []byte{}, nil + } + + if buf == nil { + buf = make([]byte, br.StableSize()) + } + + if br.bal != nil { + _, err := proto.NestedStructureMarshal(balanceRespBodyDecimalField, buf, br.bal) + if err != nil { + return nil, err + } + } + + return buf, nil +} + +func (br *BalanceResponseBody) StableSize() (size int) { + if br == nil { + return 0 + } + + if br.bal != nil { + size = proto.NestedStructureSize(balanceRespBodyDecimalField, br.bal) + } + + return size +} diff --git a/v2/accounting/marshal_test.go b/v2/accounting/marshal_test.go new file mode 100644 index 0000000..5d708c5 --- /dev/null +++ b/v2/accounting/marshal_test.go @@ -0,0 +1,83 @@ +package accounting_test + +import ( + "testing" + + "github.com/nspcc-dev/neofs-api-go/v2/accounting" + grpc "github.com/nspcc-dev/neofs-api-go/v2/accounting/grpc" + "github.com/nspcc-dev/neofs-api-go/v2/refs" + "github.com/stretchr/testify/require" +) + +func TestDecimal_StableMarshal(t *testing.T) { + decimalFrom := generateDecimal(888) + transport := new(grpc.Decimal) + + t.Run("non empty", func(t *testing.T) { + wire, err := decimalFrom.StableMarshal(nil) + require.NoError(t, err) + + err = transport.Unmarshal(wire) + require.NoError(t, err) + + decimalTo := accounting.DecimalFromGRPCMessage(transport) + require.Equal(t, decimalFrom, decimalTo) + }) +} + +func TestBalanceRequestBody_StableMarshal(t *testing.T) { + requestBodyFrom := generateBalanceRequestBody("Owner ID") + transport := new(grpc.BalanceRequest_Body) + + t.Run("non empty", func(t *testing.T) { + wire, err := requestBodyFrom.StableMarshal(nil) + require.NoError(t, err) + + err = transport.Unmarshal(wire) + require.NoError(t, err) + + requestBodyTo := accounting.BalanceRequestBodyFromGRPCMessage(transport) + require.Equal(t, requestBodyFrom, requestBodyTo) + }) +} + +func TestBalanceResponseBody_StableMarshal(t *testing.T) { + responseBodyFrom := generateBalanceResponseBody(444) + transport := new(grpc.BalanceResponse_Body) + + t.Run("non empty", func(t *testing.T) { + wire, err := responseBodyFrom.StableMarshal(nil) + require.NoError(t, err) + + err = transport.Unmarshal(wire) + require.NoError(t, err) + + responseBodyTo := accounting.BalanceResponseBodyFromGRPCMessage(transport) + require.Equal(t, responseBodyFrom, responseBodyTo) + }) +} + +func generateDecimal(val int64) *accounting.Decimal { + decimal := new(accounting.Decimal) + decimal.SetValue(val) + decimal.SetPrecision(1000) + + return decimal +} + +func generateBalanceRequestBody(id string) *accounting.BalanceRequestBody { + owner := new(refs.OwnerID) + owner.SetValue([]byte(id)) + + request := new(accounting.BalanceRequestBody) + request.SetOwnerID(owner) + + return request +} + +func generateBalanceResponseBody(val int64) *accounting.BalanceResponseBody { + response := new(accounting.BalanceResponseBody) + response.SetBalance(generateDecimal(val)) + + return response +}