From a68252c9562bed733391ca01c9e8b1538955cbb4 Mon Sep 17 00:00:00 2001 From: Alex Vanin Date: Fri, 14 Aug 2020 17:22:47 +0300 Subject: [PATCH] Add stable marshal helpers for repeated fields Signed-off-by: Alex Vanin --- util/proto/marshal.go | 159 +++++++++++++ util/proto/marshal_test.go | 467 ++++++++++++++++++++++++++++--------- util/proto/test/test.pb.go | Bin 15480 -> 31073 bytes util/proto/test/test.proto | 9 + 4 files changed, 528 insertions(+), 107 deletions(-) diff --git a/util/proto/marshal.go b/util/proto/marshal.go index 025d9b4..cc055a5 100644 --- a/util/proto/marshal.go +++ b/util/proto/marshal.go @@ -128,6 +128,165 @@ func EnumSize(field int, v int32) int { return UInt64Size(field, uint64(v)) } +func RepeatedBytesMarshal(field int, buf []byte, v [][]byte) (int, error) { + var offset int + + for i := range v { + off, err := BytesMarshal(field, buf[offset:], v[i]) + if err != nil { + return 0, err + } + + offset += off + } + + return offset, nil +} + +func RepeatedBytesSize(field int, v [][]byte) (size int) { + for i := range v { + size += BytesSize(field, v[i]) + } + + return size +} + +func RepeatedStringMarshal(field int, buf []byte, v []string) (int, error) { + var offset int + + for i := range v { + off, err := StringMarshal(field, buf[offset:], v[i]) + if err != nil { + return 0, err + } + + offset += off + } + + return offset, nil +} + +func RepeatedStringSize(field int, v []string) (size int) { + for i := range v { + size += StringSize(field, v[i]) + } + + return size +} + +func RepeatedUInt64Marshal(field int, buf []byte, v []uint64) (int, error) { + if len(v) == 0 { + return 0, nil + } + + prefix := field<<3 | 0x02 + offset := binary.PutUvarint(buf, uint64(prefix)) + + _, arrSize := RepeatedUInt64Size(field, v) + offset += binary.PutUvarint(buf[offset:], uint64(arrSize)) + for i := range v { + offset += binary.PutUvarint(buf[offset:], v[i]) + } + + return offset, nil +} + +func RepeatedUInt64Size(field int, v []uint64) (size, arraySize int) { + if len(v) == 0 { + return 0, 0 + } + + for i := range v { + size += VarUIntSize(v[i]) + } + arraySize = size + + size += VarUIntSize(uint64(size)) + + prefix := field<<3 | 0x2 + size += VarUIntSize(uint64(prefix)) + + return size, arraySize +} + +func RepeatedInt64Marshal(field int, buf []byte, v []int64) (int, error) { + if len(v) == 0 { + return 0, nil + } + + convert := make([]uint64, len(v)) + for i := range v { + convert[i] = uint64(v[i]) + } + + return RepeatedUInt64Marshal(field, buf, convert) +} + +func RepeatedInt64Size(field int, v []int64) (size, arraySize int) { + if len(v) == 0 { + return 0, 0 + } + + convert := make([]uint64, len(v)) + for i := range v { + convert[i] = uint64(v[i]) + } + + return RepeatedUInt64Size(field, convert) +} + +func RepeatedUInt32Marshal(field int, buf []byte, v []uint32) (int, error) { + if len(v) == 0 { + return 0, nil + } + + convert := make([]uint64, len(v)) + for i := range v { + convert[i] = uint64(v[i]) + } + + return RepeatedUInt64Marshal(field, buf, convert) +} + +func RepeatedUInt32Size(field int, v []uint32) (size, arraySize int) { + if len(v) == 0 { + return 0, 0 + } + + convert := make([]uint64, len(v)) + for i := range v { + convert[i] = uint64(v[i]) + } + + return RepeatedUInt64Size(field, convert) +} + +func RepeatedInt32Marshal(field int, buf []byte, v []int32) (int, error) { + if len(v) == 0 { + return 0, nil + } + + convert := make([]uint64, len(v)) + for i := range v { + convert[i] = uint64(v[i]) + } + + return RepeatedUInt64Marshal(field, buf, convert) +} + +func RepeatedInt32Size(field int, v []int32) (size, arraySize int) { + if len(v) == 0 { + return 0, 0 + } + + convert := make([]uint64, len(v)) + for i := range v { + convert[i] = uint64(v[i]) + } + + return RepeatedUInt64Size(field, convert) +} + // varUIntSize returns length of varint byte sequence for uint64 value 'x'. func VarUIntSize(x uint64) int { return (bits.Len64(x|1) + 6) / 7 diff --git a/util/proto/marshal_test.go b/util/proto/marshal_test.go index 6802523..de7b1ca 100644 --- a/util/proto/marshal_test.go +++ b/util/proto/marshal_test.go @@ -23,6 +23,15 @@ type stablePrimitives struct { FieldH SomeEnum } +type stableRepPrimitives struct { + FieldA [][]byte + FieldB []string + FieldC []int32 + FieldD []uint32 + FieldE []int64 + FieldF []uint64 +} + const ( ENUM_UNKNOWN SomeEnum = 0 ENUM_POSITIVE = 1 @@ -118,7 +127,7 @@ func (s *stablePrimitives) stableMarshal(buf []byte, wrongField bool) ([]byte, e } offset, err = proto.EnumMarshal(fieldNum, buf, int32(s.FieldH)) if err != nil { - return nil, errors.Wrap(err, "can't marshal field g") + return nil, errors.Wrap(err, "can't marshal field h") } i += offset @@ -136,6 +145,93 @@ func (s *stablePrimitives) stableSize() int { proto.EnumSize(300, int32(s.FieldH)) } +func (s *stableRepPrimitives) stableMarshal(buf []byte, wrongField bool) ([]byte, error) { + if s == nil { + return []byte{}, nil + } + + if buf == nil { + buf = make([]byte, s.stableSize()) + } + + var ( + i, offset, fieldNum int + ) + + fieldNum = 1 + if wrongField { + fieldNum++ + } + offset, err := proto.RepeatedBytesMarshal(fieldNum, buf, s.FieldA) + if err != nil { + return nil, errors.Wrap(err, "can't marshal field a") + } + i += offset + + fieldNum = 2 + if wrongField { + fieldNum++ + } + offset, err = proto.RepeatedStringMarshal(fieldNum, buf, s.FieldB) + if err != nil { + return nil, errors.Wrap(err, "can't marshal field b") + } + i += offset + + fieldNum = 3 + if wrongField { + fieldNum++ + } + offset, err = proto.RepeatedInt32Marshal(fieldNum, buf, s.FieldC) + if err != nil { + return nil, errors.Wrap(err, "can't marshal field c") + } + i += offset + + fieldNum = 4 + if wrongField { + fieldNum++ + } + offset, err = proto.RepeatedUInt32Marshal(fieldNum, buf, s.FieldD) + if err != nil { + return nil, errors.Wrap(err, "can't marshal field d") + } + i += offset + + fieldNum = 5 + if wrongField { + fieldNum++ + } + offset, err = proto.RepeatedInt64Marshal(fieldNum, buf, s.FieldE) + if err != nil { + return nil, errors.Wrap(err, "can't marshal field e") + } + i += offset + + fieldNum = 6 + if wrongField { + fieldNum++ + } + offset, err = proto.RepeatedUInt64Marshal(fieldNum, buf, s.FieldF) + if err != nil { + return nil, errors.Wrap(err, "can't marshal field f") + } + i += offset + + return buf, nil +} + +func (s *stableRepPrimitives) stableSize() int { + f1 := proto.RepeatedBytesSize(1, s.FieldA) + f2 := proto.RepeatedStringSize(2, s.FieldB) + f3, _ := proto.RepeatedInt32Size(3, s.FieldC) + f4, _ := proto.RepeatedUInt32Size(4, s.FieldD) + f5, _ := proto.RepeatedInt64Size(5, s.FieldE) + f6, _ := proto.RepeatedUInt64Size(6, s.FieldF) + + return f1 + f2 + f3 + f4 + f5 + f6 +} + func TestBytesMarshal(t *testing.T) { t.Run("not empty", func(t *testing.T) { data := []byte("Hello World") @@ -237,112 +333,103 @@ func TestEnumMarshal(t *testing.T) { testEnumMarshal(t, ENUM_NEGATIVE, true) } -func testBytesMarshal(t *testing.T, data []byte, wrongField bool) { - var ( - wire []byte - err error +func TestRepeatedBytesMarshal(t *testing.T) { + t.Run("not empty", func(t *testing.T) { + data := [][]byte{[]byte("One"), []byte("Two"), []byte("Three")} + testRepeatedBytesMarshal(t, data, false) + testRepeatedBytesMarshal(t, data, true) + }) - custom = stablePrimitives{FieldA: data} - transport = test.Primitives{FieldA: data} - ) + t.Run("empty", func(t *testing.T) { + testRepeatedBytesMarshal(t, [][]byte{}, false) + }) - wire, err = custom.stableMarshal(nil, wrongField) - require.NoError(t, err) - - wireGen, err := transport.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) - - if !wrongField { - require.Len(t, result.FieldA, len(data)) - if len(data) > 0 { - require.Equal(t, data, result.FieldA) - } - } else { - require.Len(t, result.FieldA, 0) - } + t.Run("nil", func(t *testing.T) { + testRepeatedBytesMarshal(t, nil, false) + }) } -func testStringMarshal(t *testing.T, s string, wrongField bool) { - var ( - wire []byte - err error +func TestRepeatedStringMarshal(t *testing.T) { + t.Run("not empty", func(t *testing.T) { + data := []string{"One", "Two", "Three"} + testRepeatedStringMarshal(t, data, false) + testRepeatedStringMarshal(t, data, true) + }) - custom = stablePrimitives{FieldB: s} - transport = test.Primitives{FieldB: s} - ) + t.Run("empty", func(t *testing.T) { + testRepeatedStringMarshal(t, []string{}, false) + }) - wire, err = custom.stableMarshal(nil, wrongField) - require.NoError(t, err) - - wireGen, err := transport.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) - - if !wrongField { - require.Len(t, result.FieldB, len(s)) - if len(s) > 0 { - require.Equal(t, s, result.FieldB) - } - } else { - require.Len(t, result.FieldB, 0) - } + t.Run("nil", func(t *testing.T) { + testRepeatedStringMarshal(t, nil, false) + }) } -func testBoolMarshal(t *testing.T, b bool, wrongField bool) { - var ( - wire []byte - err error +func TestRepeatedInt32Marshal(t *testing.T) { + t.Run("not empty", func(t *testing.T) { + data := []int32{-1, 0, 1, 2, 3, 4, 5} + testRepeatedInt32Marshal(t, data, false) + testRepeatedInt32Marshal(t, data, true) + }) - custom = stablePrimitives{FieldC: b} - transport = test.Primitives{FieldC: b} - ) + t.Run("empty", func(t *testing.T) { + testRepeatedInt32Marshal(t, []int32{}, false) + }) - wire, err = custom.stableMarshal(nil, wrongField) - require.NoError(t, err) - - wireGen, err := transport.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) - - if !wrongField { - require.Equal(t, b, result.FieldC) - } else { - require.False(t, false, result.FieldC) - } + t.Run("nil", func(t *testing.T) { + testRepeatedInt32Marshal(t, nil, false) + }) } -func testIntMarshal(t *testing.T, c stablePrimitives, tr test.Primitives, wrongField bool) *test.Primitives { +func TestRepeatedUInt32Marshal(t *testing.T) { + t.Run("not empty", func(t *testing.T) { + data := []uint32{0, 1, 2, 3, 4, 5} + testRepeatedUInt32Marshal(t, data, false) + testRepeatedUInt32Marshal(t, data, true) + }) + + t.Run("empty", func(t *testing.T) { + testRepeatedUInt32Marshal(t, []uint32{}, false) + }) + + t.Run("nil", func(t *testing.T) { + testRepeatedUInt32Marshal(t, nil, false) + }) +} + +func TestRepeatedInt64Marshal(t *testing.T) { + t.Run("not empty", func(t *testing.T) { + data := []int64{-1, 0, 1, 2, 3, 4, 5} + testRepeatedInt64Marshal(t, data, false) + testRepeatedInt64Marshal(t, data, true) + }) + + t.Run("empty", func(t *testing.T) { + testRepeatedInt64Marshal(t, []int64{}, false) + }) + + t.Run("nil", func(t *testing.T) { + testRepeatedInt64Marshal(t, nil, false) + }) +} + +func TestRepeatedUInt64Marshal(t *testing.T) { + t.Run("not empty", func(t *testing.T) { + data := []uint64{0, 1, 2, 3, 4, 5} + testRepeatedUInt64Marshal(t, data, false) + testRepeatedUInt64Marshal(t, data, true) + }) + + t.Run("empty", func(t *testing.T) { + testRepeatedUInt64Marshal(t, []uint64{}, false) + }) + + t.Run("nil", func(t *testing.T) { + testRepeatedUInt64Marshal(t, nil, false) + }) +} + +func testMarshal(t *testing.T, c stablePrimitives, tr test.Primitives, wrongField bool) *test.Primitives { var ( wire []byte err error @@ -367,13 +454,64 @@ func testIntMarshal(t *testing.T, c stablePrimitives, tr test.Primitives, wrongF return result } +func testBytesMarshal(t *testing.T, data []byte, wrongField bool) { + var ( + custom = stablePrimitives{FieldA: data} + transport = test.Primitives{FieldA: data} + ) + + result := testMarshal(t, custom, transport, wrongField) + + if !wrongField { + require.Len(t, result.FieldA, len(data)) + if len(data) > 0 { + require.Equal(t, data, result.FieldA) + } + } else { + require.Len(t, result.FieldA, 0) + } +} + +func testStringMarshal(t *testing.T, s string, wrongField bool) { + var ( + custom = stablePrimitives{FieldB: s} + transport = test.Primitives{FieldB: s} + ) + + result := testMarshal(t, custom, transport, wrongField) + + if !wrongField { + require.Len(t, result.FieldB, len(s)) + if len(s) > 0 { + require.Equal(t, s, result.FieldB) + } + } else { + require.Len(t, result.FieldB, 0) + } +} + +func testBoolMarshal(t *testing.T, b bool, wrongField bool) { + var ( + custom = stablePrimitives{FieldC: b} + transport = test.Primitives{FieldC: b} + ) + + result := testMarshal(t, custom, transport, wrongField) + + if !wrongField { + require.Equal(t, b, result.FieldC) + } else { + require.False(t, false, result.FieldC) + } +} + 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) + result := testMarshal(t, custom, transport, wrongField) if !wrongField { require.Equal(t, n, result.FieldD) @@ -388,7 +526,7 @@ func testUInt32Marshal(t *testing.T, n uint32, wrongField bool) { transport = test.Primitives{FieldE: n} ) - result := testIntMarshal(t, custom, transport, wrongField) + result := testMarshal(t, custom, transport, wrongField) if !wrongField { require.Equal(t, n, result.FieldE) @@ -403,7 +541,7 @@ func testInt64Marshal(t *testing.T, n int64, wrongField bool) { transport = test.Primitives{FieldF: n} ) - result := testIntMarshal(t, custom, transport, wrongField) + result := testMarshal(t, custom, transport, wrongField) if !wrongField { require.Equal(t, n, result.FieldF) @@ -418,7 +556,7 @@ func testUInt64Marshal(t *testing.T, n uint64, wrongField bool) { transport = test.Primitives{FieldG: n} ) - result := testIntMarshal(t, custom, transport, wrongField) + result := testMarshal(t, custom, transport, wrongField) if !wrongField { require.Equal(t, n, result.FieldG) @@ -429,17 +567,28 @@ func testUInt64Marshal(t *testing.T, n uint64, wrongField bool) { func testEnumMarshal(t *testing.T, e SomeEnum, wrongField bool) { var ( - wire []byte - err error - custom = stablePrimitives{FieldH: e} transport = test.Primitives{FieldH: test.Primitives_SomeEnum(e)} ) - wire, err = custom.stableMarshal(nil, wrongField) + result := testMarshal(t, custom, transport, wrongField) + + if !wrongField { + require.EqualValues(t, custom.FieldH, result.FieldH) + } else { + require.EqualValues(t, 0, result.FieldH) + } +} + +func testRepMarshal(t *testing.T, c stableRepPrimitives, tr test.RepPrimitives, wrongField bool) *test.RepPrimitives { + var ( + wire []byte + err error + ) + wire, err = c.stableMarshal(nil, wrongField) require.NoError(t, err) - wireGen, err := transport.Marshal() + wireGen, err := tr.Marshal() require.NoError(t, err) if !wrongField { @@ -449,13 +598,117 @@ func testEnumMarshal(t *testing.T, e SomeEnum, wrongField bool) { require.NotEqual(t, wireGen, wire) } - result := new(test.Primitives) + result := new(test.RepPrimitives) err = result.Unmarshal(wire) require.NoError(t, err) + return result +} + +func testRepeatedBytesMarshal(t *testing.T, data [][]byte, wrongField bool) { + var ( + custom = stableRepPrimitives{FieldA: data} + transport = test.RepPrimitives{FieldA: data} + ) + + result := testRepMarshal(t, custom, transport, wrongField) + if !wrongField { - require.EqualValues(t, custom.FieldH, result.FieldH) + require.Len(t, result.FieldA, len(data)) + if len(data) > 0 { + require.Equal(t, data, result.FieldA) + } } else { - require.EqualValues(t, 0, result.FieldH) + require.Len(t, result.FieldA, 0) + } +} + +func testRepeatedStringMarshal(t *testing.T, s []string, wrongField bool) { + var ( + custom = stableRepPrimitives{FieldB: s} + transport = test.RepPrimitives{FieldB: s} + ) + + result := testRepMarshal(t, custom, transport, wrongField) + + if !wrongField { + require.Len(t, result.FieldB, len(s)) + if len(s) > 0 { + require.Equal(t, s, result.FieldB) + } + } else { + require.Len(t, result.FieldB, 0) + } +} + +func testRepeatedInt32Marshal(t *testing.T, n []int32, wrongField bool) { + var ( + custom = stableRepPrimitives{FieldC: n} + transport = test.RepPrimitives{FieldC: n} + ) + + result := testRepMarshal(t, custom, transport, wrongField) + + if !wrongField { + require.Len(t, result.FieldC, len(n)) + if len(n) > 0 { + require.Equal(t, n, result.FieldC) + } + } else { + require.Len(t, result.FieldC, 0) + } +} + +func testRepeatedUInt32Marshal(t *testing.T, n []uint32, wrongField bool) { + var ( + custom = stableRepPrimitives{FieldD: n} + transport = test.RepPrimitives{FieldD: n} + ) + + result := testRepMarshal(t, custom, transport, wrongField) + + if !wrongField { + require.Len(t, result.FieldD, len(n)) + if len(n) > 0 { + require.Equal(t, n, result.FieldD) + } + } else { + require.Len(t, result.FieldD, 0) + } +} + +func testRepeatedInt64Marshal(t *testing.T, n []int64, wrongField bool) { + var ( + custom = stableRepPrimitives{FieldE: n} + transport = test.RepPrimitives{FieldE: n} + ) + + result := testRepMarshal(t, custom, transport, wrongField) + + if !wrongField { + require.Len(t, result.FieldE, len(n)) + if len(n) > 0 { + require.Equal(t, n, result.FieldE) + } + } else { + require.Len(t, result.FieldE, 0) + } +} + +func testRepeatedUInt64Marshal(t *testing.T, n []uint64, wrongField bool) { + var ( + custom = stableRepPrimitives{FieldF: n} + transport = test.RepPrimitives{FieldF: n} + ) + + result := testRepMarshal(t, custom, transport, wrongField) + + if !wrongField { + require.Len(t, result.FieldF, len(n)) + if len(n) > 0 { + require.Equal(t, n, result.FieldF) + } + } else { + require.Len(t, result.FieldF, 0) } } diff --git a/util/proto/test/test.pb.go b/util/proto/test/test.pb.go index 0c3146791ee92bf42e98864599b952a72e7f47fb..686ef72c5874dd3732dd50e7b915a8ea463fa395 100644 GIT binary patch literal 31073 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 MDo14V