Add stable marshaller helper for ints

Signed-off-by: Alex Vanin <alexey@nspcc.ru>
This commit is contained in:
Alex Vanin 2020-08-14 16:32:19 +03:00 committed by Stanislav Bogatyrev
parent 89bd8f3915
commit f8482381fd
4 changed files with 406 additions and 9 deletions

View file

@ -67,10 +67,64 @@ func BoolSize(field int, v bool) int {
} }
prefix := field << 3 prefix := field << 3
return VarUIntSize(uint64(prefix)) + 1 // bool is always 1 byte long return VarUIntSize(uint64(prefix)) + 1 // bool is always 1 byte long
} }
func UInt64Marshal(field int, buf []byte, v uint64) (int, error) {
if v == 0 {
return 0, nil
}
prefix := field << 3
// buf length check can prevent panic at PutUvarint, but it will make
// marshaller a bit slower.
i := binary.PutUvarint(buf, uint64(prefix))
i += binary.PutUvarint(buf[i:], v)
return i, nil
}
func UInt64Size(field int, v uint64) int {
if v == 0 {
return 0
}
prefix := field << 3
return VarUIntSize(uint64(prefix)) + VarUIntSize(v)
}
func Int64Marshal(field int, buf []byte, v int64) (int, error) {
return UInt64Marshal(field, buf, uint64(v))
}
func Int64Size(field int, v int64) int {
return UInt64Size(field, uint64(v))
}
func UInt32Marshal(field int, buf []byte, v uint32) (int, error) {
return UInt64Marshal(field, buf, uint64(v))
}
func UInt32Size(field int, v uint32) int {
return UInt64Size(field, uint64(v))
}
func Int32Marshal(field int, buf []byte, v int32) (int, error) {
return UInt64Marshal(field, buf, uint64(v))
}
func Int32Size(field int, v int32) int {
return UInt64Size(field, uint64(v))
}
// varUIntSize returns length of varint byte sequence for uint64 value 'x'. // varUIntSize returns length of varint byte sequence for uint64 value 'x'.
func VarUIntSize(x uint64) int { func VarUIntSize(x uint64) int {
return (bits.Len64(x|1) + 6) / 7 return (bits.Len64(x|1) + 6) / 7
} }
func NestedStructurePrefixSize(field int64) int {
return VarUIntSize(uint64(field<<3 | 0x02))
}

View file

@ -1,6 +1,7 @@
package proto_test package proto_test
import ( import (
"math"
"testing" "testing"
"github.com/nspcc-dev/neofs-api-go/util/proto" "github.com/nspcc-dev/neofs-api-go/util/proto"
@ -13,6 +14,10 @@ type stablePrimitives struct {
FieldA []byte FieldA []byte
FieldB string FieldB string
FieldC bool FieldC bool
FieldD int32
FieldE uint32
FieldF int64
FieldG uint64
} }
func (s *stablePrimitives) stableMarshal(buf []byte, wrongField bool) ([]byte, error) { func (s *stablePrimitives) stableMarshal(buf []byte, wrongField bool) ([]byte, error) {
@ -58,13 +63,57 @@ func (s *stablePrimitives) stableMarshal(buf []byte, wrongField bool) ([]byte, e
} }
i += offset i += offset
fieldNum = 201
if wrongField {
fieldNum++
}
offset, err = proto.Int32Marshal(fieldNum, buf, s.FieldD)
if err != nil {
return nil, errors.Wrap(err, "can't marshal field d")
}
i += offset
fieldNum = 202
if wrongField {
fieldNum++
}
offset, err = proto.UInt32Marshal(fieldNum, buf, s.FieldE)
if err != nil {
return nil, errors.Wrap(err, "can't marshal field e")
}
i += offset
fieldNum = 203
if wrongField {
fieldNum++
}
offset, err = proto.Int64Marshal(fieldNum, buf, s.FieldF)
if err != nil {
return nil, errors.Wrap(err, "can't marshal field f")
}
i += offset
fieldNum = 204
if wrongField {
fieldNum++
}
offset, err = proto.UInt64Marshal(fieldNum, buf, s.FieldG)
if err != nil {
return nil, errors.Wrap(err, "can't marshal field g")
}
i += offset
return buf, nil return buf, nil
} }
func (s *stablePrimitives) stableSize() int { func (s *stablePrimitives) stableSize() int {
return proto.BytesSize(1, s.FieldA) + return proto.BytesSize(1, s.FieldA) +
proto.StringSize(2, s.FieldB) + proto.StringSize(2, s.FieldB) +
proto.BoolSize(200, s.FieldC) proto.BoolSize(200, s.FieldC) +
proto.Int32Size(201, s.FieldD) +
proto.UInt32Size(202, s.FieldE) +
proto.Int64Size(203, s.FieldF) +
proto.UInt64Size(204, s.FieldG)
} }
func TestBytesMarshal(t *testing.T) { func TestBytesMarshal(t *testing.T) {
@ -106,6 +155,60 @@ func TestBoolMarshal(t *testing.T) {
}) })
} }
func TestInt32Marshal(t *testing.T) {
t.Run("zero", func(t *testing.T) {
testInt32Marshal(t, 0, false)
})
t.Run("positive", func(t *testing.T) {
testInt32Marshal(t, math.MaxInt32, false)
testInt32Marshal(t, math.MaxInt32, true)
})
t.Run("negative", func(t *testing.T) {
testInt32Marshal(t, math.MinInt32, false)
testInt32Marshal(t, math.MinInt32, true)
})
}
func TestUInt32Marshal(t *testing.T) {
t.Run("zero", func(t *testing.T) {
testUInt32Marshal(t, 0, false)
})
t.Run("non zero", func(t *testing.T) {
testUInt32Marshal(t, math.MaxUint32, false)
testUInt32Marshal(t, math.MaxUint32, true)
})
}
func TestInt64Marshal(t *testing.T) {
t.Run("zero", func(t *testing.T) {
testInt32Marshal(t, 0, false)
})
t.Run("positive", func(t *testing.T) {
testInt64Marshal(t, math.MaxInt64, false)
testInt64Marshal(t, math.MaxInt64, true)
})
t.Run("negative", func(t *testing.T) {
testInt64Marshal(t, math.MinInt64, false)
testInt64Marshal(t, math.MinInt64, true)
})
}
func TestUInt64Marshal(t *testing.T) {
t.Run("zero", func(t *testing.T) {
testUInt64Marshal(t, 0, false)
})
t.Run("non zero", func(t *testing.T) {
testUInt64Marshal(t, math.MaxUint64, false)
testUInt64Marshal(t, math.MaxUint64, true)
})
}
func testBytesMarshal(t *testing.T, data []byte, wrongField bool) { func testBytesMarshal(t *testing.T, data []byte, wrongField bool) {
var ( var (
wire []byte wire []byte
@ -210,3 +313,88 @@ func testBoolMarshal(t *testing.T, b bool, wrongField bool) {
require.False(t, false, result.FieldC) require.False(t, false, result.FieldC)
} }
} }
func testIntMarshal(t *testing.T, c stablePrimitives, tr test.Primitives, wrongField bool) *test.Primitives {
var (
wire []byte
err error
)
wire, err = c.stableMarshal(nil, wrongField)
require.NoError(t, err)
wireGen, err := tr.Marshal()
require.NoError(t, err)
if !wrongField {
// we can check equality because single field cannot be unstable marshalled
require.Equal(t, wireGen, wire)
} else {
require.NotEqual(t, wireGen, wire)
}
result := new(test.Primitives)
err = result.Unmarshal(wire)
require.NoError(t, err)
return result
}
func testInt32Marshal(t *testing.T, n int32, wrongField bool) {
var (
custom = stablePrimitives{FieldD: n}
transport = test.Primitives{FieldD: n}
)
result := testIntMarshal(t, custom, transport, wrongField)
if !wrongField {
require.Equal(t, n, result.FieldD)
} else {
require.EqualValues(t, 0, result.FieldD)
}
}
func testUInt32Marshal(t *testing.T, n uint32, wrongField bool) {
var (
custom = stablePrimitives{FieldE: n}
transport = test.Primitives{FieldE: n}
)
result := testIntMarshal(t, custom, transport, wrongField)
if !wrongField {
require.Equal(t, n, result.FieldE)
} else {
require.EqualValues(t, 0, result.FieldE)
}
}
func testInt64Marshal(t *testing.T, n int64, wrongField bool) {
var (
custom = stablePrimitives{FieldF: n}
transport = test.Primitives{FieldF: n}
)
result := testIntMarshal(t, custom, transport, wrongField)
if !wrongField {
require.Equal(t, n, result.FieldF)
} else {
require.EqualValues(t, 0, result.FieldF)
}
}
func testUInt64Marshal(t *testing.T, n uint64, wrongField bool) {
var (
custom = stablePrimitives{FieldG: n}
transport = test.Primitives{FieldG: n}
)
result := testIntMarshal(t, custom, transport, wrongField)
if !wrongField {
require.Equal(t, n, result.FieldG)
} else {
require.EqualValues(t, 0, result.FieldG)
}
}

View file

@ -26,6 +26,10 @@ type Primitives struct {
FieldA []byte `protobuf:"bytes,1,opt,name=field_a,json=fieldA,proto3" json:"field_a,omitempty"` FieldA []byte `protobuf:"bytes,1,opt,name=field_a,json=fieldA,proto3" json:"field_a,omitempty"`
FieldB string `protobuf:"bytes,2,opt,name=field_b,json=fieldB,proto3" json:"field_b,omitempty"` FieldB string `protobuf:"bytes,2,opt,name=field_b,json=fieldB,proto3" json:"field_b,omitempty"`
FieldC bool `protobuf:"varint,200,opt,name=field_c,json=fieldC,proto3" json:"field_c,omitempty"` FieldC bool `protobuf:"varint,200,opt,name=field_c,json=fieldC,proto3" json:"field_c,omitempty"`
FieldD int32 `protobuf:"varint,201,opt,name=field_d,json=fieldD,proto3" json:"field_d,omitempty"`
FieldE uint32 `protobuf:"varint,202,opt,name=field_e,json=fieldE,proto3" json:"field_e,omitempty"`
FieldF int64 `protobuf:"varint,203,opt,name=field_f,json=fieldF,proto3" json:"field_f,omitempty"`
FieldG uint64 `protobuf:"varint,204,opt,name=field_g,json=fieldG,proto3" json:"field_g,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"` XXX_sizecache int32 `json:"-"`
@ -85,6 +89,34 @@ func (m *Primitives) GetFieldC() bool {
return false return false
} }
func (m *Primitives) GetFieldD() int32 {
if m != nil {
return m.FieldD
}
return 0
}
func (m *Primitives) GetFieldE() uint32 {
if m != nil {
return m.FieldE
}
return 0
}
func (m *Primitives) GetFieldF() int64 {
if m != nil {
return m.FieldF
}
return 0
}
func (m *Primitives) GetFieldG() uint64 {
if m != nil {
return m.FieldG
}
return 0
}
func init() { func init() {
proto.RegisterType((*Primitives)(nil), "test.Primitives") proto.RegisterType((*Primitives)(nil), "test.Primitives")
} }
@ -92,16 +124,19 @@ func init() {
func init() { proto.RegisterFile("util/proto/test/test.proto", fileDescriptor_998ad0e1a3de8558) } func init() { proto.RegisterFile("util/proto/test/test.proto", fileDescriptor_998ad0e1a3de8558) }
var fileDescriptor_998ad0e1a3de8558 = []byte{ var fileDescriptor_998ad0e1a3de8558 = []byte{
// 134 bytes of a gzipped FileDescriptorProto // 178 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x2a, 0x2d, 0xc9, 0xcc, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x2a, 0x2d, 0xc9, 0xcc,
0xd1, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0xd7, 0x2f, 0x49, 0x2d, 0x2e, 0x01, 0x13, 0x7a, 0x60, 0xbe, 0xd1, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0xd7, 0x2f, 0x49, 0x2d, 0x2e, 0x01, 0x13, 0x7a, 0x60, 0xbe,
0x10, 0x0b, 0x88, 0xad, 0x14, 0xc1, 0xc5, 0x15, 0x50, 0x94, 0x99, 0x9b, 0x59, 0x92, 0x59, 0x96, 0x10, 0x0b, 0x88, 0xad, 0x74, 0x80, 0x91, 0x8b, 0x2b, 0xa0, 0x28, 0x33, 0x37, 0xb3, 0x24, 0xb3,
0x5a, 0x2c, 0x24, 0xce, 0xc5, 0x9e, 0x96, 0x99, 0x9a, 0x93, 0x12, 0x9f, 0x28, 0xc1, 0xa8, 0xc0, 0x2c, 0xb5, 0x58, 0x48, 0x9c, 0x8b, 0x3d, 0x2d, 0x33, 0x35, 0x27, 0x25, 0x3e, 0x51, 0x82, 0x51,
0xa8, 0xc1, 0x13, 0xc4, 0x06, 0xe6, 0x3a, 0x22, 0x24, 0x92, 0x24, 0x98, 0x14, 0x18, 0x35, 0x38, 0x81, 0x51, 0x83, 0x27, 0x88, 0x0d, 0xcc, 0x75, 0x44, 0x48, 0x24, 0x49, 0x30, 0x29, 0x30, 0x6a,
0xa1, 0x12, 0x4e, 0x42, 0x12, 0x30, 0x89, 0x64, 0x89, 0x13, 0x20, 0x2d, 0x1c, 0x50, 0x19, 0x67, 0x70, 0x42, 0x25, 0x9c, 0x84, 0x24, 0x60, 0x12, 0xc9, 0x12, 0x27, 0x40, 0x5a, 0x38, 0xa0, 0x32,
0x27, 0x81, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc6, 0xce, 0x08, 0x99, 0x14, 0x89, 0x93, 0x20, 0x19, 0x56, 0xa8, 0x8c, 0x0b, 0x42, 0x26, 0x55, 0xe2,
0x63, 0x39, 0x86, 0x24, 0x36, 0xb0, 0xc5, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1a, 0x10, 0x14, 0x48, 0x86, 0x17, 0x2a, 0xe3, 0x8a, 0x90, 0x49, 0x93, 0x38, 0x0d, 0x92, 0x61, 0x86, 0xca,
0x20, 0x0a, 0x96, 0x00, 0x00, 0x00, 0xb8, 0x21, 0x64, 0xd2, 0x25, 0xce, 0x80, 0x64, 0x58, 0xa0, 0x32, 0xee, 0x4e, 0x02, 0x27, 0x1e,
0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x8c, 0xc7, 0x72, 0x0c, 0x49,
0x6c, 0x60, 0x1f, 0x1a, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xee, 0x55, 0x82, 0xff, 0x00,
0x00, 0x00,
} }
func (m *Primitives) Marshal() (dAtA []byte, err error) { func (m *Primitives) Marshal() (dAtA []byte, err error) {
@ -128,6 +163,34 @@ func (m *Primitives) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i -= len(m.XXX_unrecognized) i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized)
} }
if m.FieldG != 0 {
i = encodeVarintTest(dAtA, i, uint64(m.FieldG))
i--
dAtA[i] = 0xc
i--
dAtA[i] = 0xe0
}
if m.FieldF != 0 {
i = encodeVarintTest(dAtA, i, uint64(m.FieldF))
i--
dAtA[i] = 0xc
i--
dAtA[i] = 0xd8
}
if m.FieldE != 0 {
i = encodeVarintTest(dAtA, i, uint64(m.FieldE))
i--
dAtA[i] = 0xc
i--
dAtA[i] = 0xd0
}
if m.FieldD != 0 {
i = encodeVarintTest(dAtA, i, uint64(m.FieldD))
i--
dAtA[i] = 0xc
i--
dAtA[i] = 0xc8
}
if m.FieldC { if m.FieldC {
i-- i--
if m.FieldC { if m.FieldC {
@ -185,6 +248,18 @@ func (m *Primitives) Size() (n int) {
if m.FieldC { if m.FieldC {
n += 3 n += 3
} }
if m.FieldD != 0 {
n += 2 + sovTest(uint64(m.FieldD))
}
if m.FieldE != 0 {
n += 2 + sovTest(uint64(m.FieldE))
}
if m.FieldF != 0 {
n += 2 + sovTest(uint64(m.FieldF))
}
if m.FieldG != 0 {
n += 2 + sovTest(uint64(m.FieldG))
}
if m.XXX_unrecognized != nil { if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized) n += len(m.XXX_unrecognized)
} }
@ -312,6 +387,82 @@ func (m *Primitives) Unmarshal(dAtA []byte) error {
} }
} }
m.FieldC = bool(v != 0) m.FieldC = bool(v != 0)
case 201:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field FieldD", wireType)
}
m.FieldD = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowTest
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.FieldD |= int32(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 202:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field FieldE", wireType)
}
m.FieldE = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowTest
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.FieldE |= uint32(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 203:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field FieldF", wireType)
}
m.FieldF = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowTest
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.FieldF |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 204:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field FieldG", wireType)
}
m.FieldG = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowTest
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.FieldG |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default: default:
iNdEx = preIndex iNdEx = preIndex
skippy, err := skipTest(dAtA[iNdEx:]) skippy, err := skipTest(dAtA[iNdEx:])

View file

@ -6,4 +6,8 @@ message Primitives {
bytes field_a = 1; bytes field_a = 1;
string field_b = 2; string field_b = 2;
bool field_c = 200; bool field_c = 200;
int32 field_d = 201;
uint32 field_e = 202;
int64 field_f = 203;
uint64 field_g = 204;
} }