From f8482381fdc24141464d20eb9a70113b7c98b9ee Mon Sep 17 00:00:00 2001 From: Alex Vanin Date: Fri, 14 Aug 2020 16:32:19 +0300 Subject: [PATCH] Add stable marshaller helper for ints Signed-off-by: Alex Vanin --- util/proto/marshal.go | 54 +++++++++++ util/proto/marshal_test.go | 190 ++++++++++++++++++++++++++++++++++++- util/proto/test/test.pb.go | 167 ++++++++++++++++++++++++++++++-- util/proto/test/test.proto | 4 + 4 files changed, 406 insertions(+), 9 deletions(-) diff --git a/util/proto/marshal.go b/util/proto/marshal.go index 8570f6a..3200176 100644 --- a/util/proto/marshal.go +++ b/util/proto/marshal.go @@ -67,10 +67,64 @@ func BoolSize(field int, v bool) int { } prefix := field << 3 + 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'. func VarUIntSize(x uint64) int { return (bits.Len64(x|1) + 6) / 7 } + +func NestedStructurePrefixSize(field int64) int { + return VarUIntSize(uint64(field<<3 | 0x02)) +} diff --git a/util/proto/marshal_test.go b/util/proto/marshal_test.go index 36847dd..0abcef9 100644 --- a/util/proto/marshal_test.go +++ b/util/proto/marshal_test.go @@ -1,6 +1,7 @@ package proto_test import ( + "math" "testing" "github.com/nspcc-dev/neofs-api-go/util/proto" @@ -13,6 +14,10 @@ type stablePrimitives struct { FieldA []byte FieldB string FieldC bool + FieldD int32 + FieldE uint32 + FieldF int64 + FieldG uint64 } 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 + 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 } func (s *stablePrimitives) stableSize() int { return proto.BytesSize(1, s.FieldA) + 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) { @@ -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) { var ( wire []byte @@ -210,3 +313,88 @@ func testBoolMarshal(t *testing.T, b bool, wrongField bool) { 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) + } +} diff --git a/util/proto/test/test.pb.go b/util/proto/test/test.pb.go index 910812c..6790854 100644 --- a/util/proto/test/test.pb.go +++ b/util/proto/test/test.pb.go @@ -26,6 +26,10 @@ type Primitives struct { 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"` 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_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -85,6 +89,34 @@ func (m *Primitives) GetFieldC() bool { 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() { proto.RegisterType((*Primitives)(nil), "test.Primitives") } @@ -92,16 +124,19 @@ func init() { func init() { proto.RegisterFile("util/proto/test/test.proto", fileDescriptor_998ad0e1a3de8558) } 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, 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, - 0x5a, 0x2c, 0x24, 0xce, 0xc5, 0x9e, 0x96, 0x99, 0x9a, 0x93, 0x12, 0x9f, 0x28, 0xc1, 0xa8, 0xc0, - 0xa8, 0xc1, 0x13, 0xc4, 0x06, 0xe6, 0x3a, 0x22, 0x24, 0x92, 0x24, 0x98, 0x14, 0x18, 0x35, 0x38, - 0xa1, 0x12, 0x4e, 0x42, 0x12, 0x30, 0x89, 0x64, 0x89, 0x13, 0x20, 0x2d, 0x1c, 0x50, 0x19, 0x67, - 0x27, 0x81, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc6, - 0x63, 0x39, 0x86, 0x24, 0x36, 0xb0, 0xc5, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1a, 0x10, - 0x20, 0x0a, 0x96, 0x00, 0x00, 0x00, + 0x10, 0x0b, 0x88, 0xad, 0x74, 0x80, 0x91, 0x8b, 0x2b, 0xa0, 0x28, 0x33, 0x37, 0xb3, 0x24, 0xb3, + 0x2c, 0xb5, 0x58, 0x48, 0x9c, 0x8b, 0x3d, 0x2d, 0x33, 0x35, 0x27, 0x25, 0x3e, 0x51, 0x82, 0x51, + 0x81, 0x51, 0x83, 0x27, 0x88, 0x0d, 0xcc, 0x75, 0x44, 0x48, 0x24, 0x49, 0x30, 0x29, 0x30, 0x6a, + 0x70, 0x42, 0x25, 0x9c, 0x84, 0x24, 0x60, 0x12, 0xc9, 0x12, 0x27, 0x40, 0x5a, 0x38, 0xa0, 0x32, + 0xce, 0x08, 0x99, 0x14, 0x89, 0x93, 0x20, 0x19, 0x56, 0xa8, 0x8c, 0x0b, 0x42, 0x26, 0x55, 0xe2, + 0x14, 0x48, 0x86, 0x17, 0x2a, 0xe3, 0x8a, 0x90, 0x49, 0x93, 0x38, 0x0d, 0x92, 0x61, 0x86, 0xca, + 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) { @@ -128,6 +163,34 @@ func (m *Primitives) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(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 { i-- if m.FieldC { @@ -185,6 +248,18 @@ func (m *Primitives) Size() (n int) { if m.FieldC { 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 { n += len(m.XXX_unrecognized) } @@ -312,6 +387,82 @@ func (m *Primitives) Unmarshal(dAtA []byte) error { } } 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: iNdEx = preIndex skippy, err := skipTest(dAtA[iNdEx:]) diff --git a/util/proto/test/test.proto b/util/proto/test/test.proto index 4be07a4..d3b43e6 100644 --- a/util/proto/test/test.proto +++ b/util/proto/test/test.proto @@ -6,4 +6,8 @@ message Primitives { bytes field_a = 1; string field_b = 2; bool field_c = 200; + int32 field_d = 201; + uint32 field_e = 202; + int64 field_f = 203; + uint64 field_g = 204; } \ No newline at end of file