diff --git a/util/proto/marshal.go b/util/proto/marshal.go index a602b783..21e80133 100644 --- a/util/proto/marshal.go +++ b/util/proto/marshal.go @@ -384,3 +384,34 @@ func Float64Size(fNum int, v float64) int { return VarUIntSize(uint64(prefix)) + 8 } + +// Fixed32Marshal encodes uint32 value to Protocol Buffers fixed32 field with specified number, +// and writes it to specified buffer. Returns number of bytes written. +// +// Panics if the buffer is undersized. +func Fixed32Marshal(field int, buf []byte, v uint32) int { + if v == 0 { + return 0 + } + + prefix := field<<3 | 5 + + // buf length check can prevent panic at PutUvarint, but it will make + // marshaller a bit slower. + i := binary.PutUvarint(buf, uint64(prefix)) + binary.LittleEndian.PutUint32(buf[i:], v) + + return i + 4 +} + +// Fixed32Size returns number of bytes required to encode uint32 value to Protocol Buffers fixed32 field +// with specified number. +func Fixed32Size(fNum int, v uint32) int { + if v == 0 { + return 0 + } + + prefix := fNum<<3 | 5 + + return VarUIntSize(uint64(prefix)) + 4 +} diff --git a/util/proto/marshal_test.go b/util/proto/marshal_test.go index ba78c00e..a3f6882f 100644 --- a/util/proto/marshal_test.go +++ b/util/proto/marshal_test.go @@ -24,6 +24,7 @@ type stablePrimitives struct { FieldH SomeEnum FieldI uint64 // fixed64 FieldJ float64 + FieldK uint32 // fixed32 } type stableRepPrimitives struct { @@ -144,6 +145,15 @@ func (s *stablePrimitives) stableMarshal(buf []byte, wrongField bool) ([]byte, e } i += offset + fieldNum = 207 + if wrongField { + fieldNum++ + } + + offset = proto.Fixed32Marshal(fieldNum, buf, s.FieldK) + + i += offset + fieldNum = 300 if wrongField { fieldNum++ @@ -167,6 +177,7 @@ func (s *stablePrimitives) stableSize() int { proto.UInt64Size(204, s.FieldG) + proto.Fixed64Size(205, s.FieldI) + proto.Float64Size(206, s.FieldJ) + + proto.Fixed32Size(207, s.FieldK) + proto.EnumSize(300, int32(s.FieldH)) } @@ -478,6 +489,17 @@ func TestFloat64Marshal(t *testing.T) { }) } +func TestFixed32Marshal(t *testing.T) { + t.Run("zero", func(t *testing.T) { + testFixed32Marshal(t, 0, false) + }) + + t.Run("non zero", func(t *testing.T) { + testFixed32Marshal(t, math.MaxUint32, false) + testFixed32Marshal(t, math.MaxUint32, true) + }) +} + func testMarshal(t *testing.T, c stablePrimitives, tr test.Primitives, wrongField bool) *test.Primitives { var ( wire []byte @@ -791,3 +813,18 @@ func testFixed64Marshal(t *testing.T, n uint64, wrongField bool) { require.EqualValues(t, 0, result.FieldI) } } + +func testFixed32Marshal(t *testing.T, n uint32, wrongField bool) { + var ( + custom = stablePrimitives{FieldK: n} + transport = test.Primitives{FieldK: n} + ) + + result := testMarshal(t, custom, transport, wrongField) + + if !wrongField { + require.Equal(t, n, result.FieldK) + } else { + require.EqualValues(t, 0, result.FieldK) + } +} diff --git a/util/proto/test/test.pb.go b/util/proto/test/test.pb.go index 7ce722ac..2a433a2b 100644 Binary files a/util/proto/test/test.pb.go and b/util/proto/test/test.pb.go differ