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 | Bin 9911 -> 13326 bytes util/proto/test/test.proto | 4 + 4 files changed, 247 insertions(+), 1 deletion(-) diff --git a/util/proto/marshal.go b/util/proto/marshal.go index 8570f6a8..32001766 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 36847dd3..0abcef9c 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 910812c7c94b28728577b5b7217e10a91d4596ed..67908540c2ee967d5de4b5b9ab021df99b49540f 100644 GIT binary patch delta 2032 zcma)-zi-n(6vvgOm13h(5EM$Os%t8tghuH6BZ)Ias8VbTB(_S3sU-HL1T{(3q>u_w zRwfqjtc(<8Kw@I%zhGhIA7J3_`<%*kLraI#`QCe<_kCYG*RM}Lyg1S4SG|td4;$?4 zU#B;;%;+S$vTl_62g6dY*%kG+%G?c0F9-b|Jv5?BcKLo->UTRs(LER*=b!5ufbLly zJ0jwj?Co_kO%w^DtB6H0gXpD*5?HYz(Mki`37`jw?HNEn1ym9xQAxDZ#JvR3SH#TT z_>YHgs1e#0IDg_gseN5kj_C3EsH` z-H#D*hJQBWsrGBG>P#N$3z>1%E-^lGxLRul_ARxVSerOE9d%+^SgUGnVQ->V?sZKT z1PGMXT1J75wT5#CI4e|96D#4y779#IP3$cJ1Zgqjz3SlLs7wJ8fHDVtIH&=-RROXg zk5rIUyQXj!4Tb%f9x6@DP({Q>QxX%a2Z}#scctoC1 zXDi`k4HH(#ZGZwYsf8pHfeOTj3BzbY`kp9CAH%5)avT8{Y_*O@oQfgR5abd@x9D{v zw@N+HEkJ4;CjwT{cWc0&ctY$cvx){BCvsTzB~|dC-sCMlGTO>w1Dt delta 607 zcmYk3Jx;?w5QT-o2&|+?fe=)YC`qw@ws%VoK!@lPw!#1Xgv z5;x!kRCvFMM4HjtnVmOp-aOyGUB4Cw@hiX0ht1?Wvo1bO^SxfX#awlbCYC8`VyO}i zR6_u-QY;0V9b7Gn8ZHZP?Kxah!E{0B4*5KYE&w804+;lh;JFv*JSmH6Df?Kd?F;vYR>)?V|sO5jn z08^*3|6if+JWIFmi6%qF!V#EXv29Ie%^Zh%kH#9hM$RB#Ekiw)hz%CJqFGKdHq2Gl v`Qc-FI=sv#7aH%hm@VrlQgSvm_QucIRW>}F9mb!N&3=45|1IMC<;~F_0AQCn diff --git a/util/proto/test/test.proto b/util/proto/test/test.proto index 4be07a45..d3b43e64 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