internal/impl: fix size for zero-length packed extensions

The size calculation for packed repeated extension fields was
considering a zero-length list as encoding to a zero-length
wire.BytesType field, rather than being omitted entirely.

Change-Id: I7d4424a21ca8afd4fa81391caede49cadb4e2505
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/212297
Reviewed-by: Joe Tsai <joetsai@google.com>
diff --git a/internal/cmd/generate-types/impl.go b/internal/cmd/generate-types/impl.go
index 700ae27..bca586c 100644
--- a/internal/cmd/generate-types/impl.go
+++ b/internal/cmd/generate-types/impl.go
@@ -556,11 +556,15 @@
 // size{{.Name}}PackedSliceValue returns the size of wire encoding a []{{.GoType}} value as a packed repeated {{.Name}}.
 func size{{.Name}}PackedSliceValue(listv protoreflect.Value, tagsize int, _ marshalOptions) (size int) {
 	list := listv.List()
+	llen := list.Len()
+	if llen == 0 {
+		return 0
+	}
 	{{if .WireType.ConstSize -}}
-	n := list.Len() * {{template "SizeValue" .}}
+	n := llen * {{template "SizeValue" .}}
 	{{- else -}}
 	n := 0
-	for i, llen := 0, list.Len(); i < llen; i++ {
+	for i, llen := 0, llen; i < llen; i++ {
 		v := list.Get(i)
 		n += {{template "SizeValue" .}}
 	}
diff --git a/internal/impl/codec_gen.go b/internal/impl/codec_gen.go
index bc7e414..060d5bd 100644
--- a/internal/impl/codec_gen.go
+++ b/internal/impl/codec_gen.go
@@ -296,8 +296,12 @@
 // sizeBoolPackedSliceValue returns the size of wire encoding a []bool value as a packed repeated Bool.
 func sizeBoolPackedSliceValue(listv protoreflect.Value, tagsize int, _ marshalOptions) (size int) {
 	list := listv.List()
+	llen := list.Len()
+	if llen == 0 {
+		return 0
+	}
 	n := 0
-	for i, llen := 0, list.Len(); i < llen; i++ {
+	for i, llen := 0, llen; i < llen; i++ {
 		v := list.Get(i)
 		n += wire.SizeVarint(wire.EncodeBool(v.Bool()))
 	}
@@ -420,8 +424,12 @@
 // sizeEnumPackedSliceValue returns the size of wire encoding a [] value as a packed repeated Enum.
 func sizeEnumPackedSliceValue(listv protoreflect.Value, tagsize int, _ marshalOptions) (size int) {
 	list := listv.List()
+	llen := list.Len()
+	if llen == 0 {
+		return 0
+	}
 	n := 0
-	for i, llen := 0, list.Len(); i < llen; i++ {
+	for i, llen := 0, llen; i < llen; i++ {
 		v := list.Get(i)
 		n += wire.SizeVarint(uint64(v.Enum()))
 	}
@@ -737,8 +745,12 @@
 // sizeInt32PackedSliceValue returns the size of wire encoding a []int32 value as a packed repeated Int32.
 func sizeInt32PackedSliceValue(listv protoreflect.Value, tagsize int, _ marshalOptions) (size int) {
 	list := listv.List()
+	llen := list.Len()
+	if llen == 0 {
+		return 0
+	}
 	n := 0
-	for i, llen := 0, list.Len(); i < llen; i++ {
+	for i, llen := 0, llen; i < llen; i++ {
 		v := list.Get(i)
 		n += wire.SizeVarint(uint64(int32(v.Int())))
 	}
@@ -1054,8 +1066,12 @@
 // sizeSint32PackedSliceValue returns the size of wire encoding a []int32 value as a packed repeated Sint32.
 func sizeSint32PackedSliceValue(listv protoreflect.Value, tagsize int, _ marshalOptions) (size int) {
 	list := listv.List()
+	llen := list.Len()
+	if llen == 0 {
+		return 0
+	}
 	n := 0
-	for i, llen := 0, list.Len(); i < llen; i++ {
+	for i, llen := 0, llen; i < llen; i++ {
 		v := list.Get(i)
 		n += wire.SizeVarint(wire.EncodeZigZag(int64(int32(v.Int()))))
 	}
@@ -1371,8 +1387,12 @@
 // sizeUint32PackedSliceValue returns the size of wire encoding a []uint32 value as a packed repeated Uint32.
 func sizeUint32PackedSliceValue(listv protoreflect.Value, tagsize int, _ marshalOptions) (size int) {
 	list := listv.List()
+	llen := list.Len()
+	if llen == 0 {
+		return 0
+	}
 	n := 0
-	for i, llen := 0, list.Len(); i < llen; i++ {
+	for i, llen := 0, llen; i < llen; i++ {
 		v := list.Get(i)
 		n += wire.SizeVarint(uint64(uint32(v.Uint())))
 	}
@@ -1688,8 +1708,12 @@
 // sizeInt64PackedSliceValue returns the size of wire encoding a []int64 value as a packed repeated Int64.
 func sizeInt64PackedSliceValue(listv protoreflect.Value, tagsize int, _ marshalOptions) (size int) {
 	list := listv.List()
+	llen := list.Len()
+	if llen == 0 {
+		return 0
+	}
 	n := 0
-	for i, llen := 0, list.Len(); i < llen; i++ {
+	for i, llen := 0, llen; i < llen; i++ {
 		v := list.Get(i)
 		n += wire.SizeVarint(uint64(v.Int()))
 	}
@@ -2005,8 +2029,12 @@
 // sizeSint64PackedSliceValue returns the size of wire encoding a []int64 value as a packed repeated Sint64.
 func sizeSint64PackedSliceValue(listv protoreflect.Value, tagsize int, _ marshalOptions) (size int) {
 	list := listv.List()
+	llen := list.Len()
+	if llen == 0 {
+		return 0
+	}
 	n := 0
-	for i, llen := 0, list.Len(); i < llen; i++ {
+	for i, llen := 0, llen; i < llen; i++ {
 		v := list.Get(i)
 		n += wire.SizeVarint(wire.EncodeZigZag(v.Int()))
 	}
@@ -2322,8 +2350,12 @@
 // sizeUint64PackedSliceValue returns the size of wire encoding a []uint64 value as a packed repeated Uint64.
 func sizeUint64PackedSliceValue(listv protoreflect.Value, tagsize int, _ marshalOptions) (size int) {
 	list := listv.List()
+	llen := list.Len()
+	if llen == 0 {
+		return 0
+	}
 	n := 0
-	for i, llen := 0, list.Len(); i < llen; i++ {
+	for i, llen := 0, llen; i < llen; i++ {
 		v := list.Get(i)
 		n += wire.SizeVarint(v.Uint())
 	}
@@ -2627,7 +2659,11 @@
 // sizeSfixed32PackedSliceValue returns the size of wire encoding a []int32 value as a packed repeated Sfixed32.
 func sizeSfixed32PackedSliceValue(listv protoreflect.Value, tagsize int, _ marshalOptions) (size int) {
 	list := listv.List()
-	n := list.Len() * wire.SizeFixed32()
+	llen := list.Len()
+	if llen == 0 {
+		return 0
+	}
+	n := llen * wire.SizeFixed32()
 	return tagsize + wire.SizeBytes(n)
 }
 
@@ -2924,7 +2960,11 @@
 // sizeFixed32PackedSliceValue returns the size of wire encoding a []uint32 value as a packed repeated Fixed32.
 func sizeFixed32PackedSliceValue(listv protoreflect.Value, tagsize int, _ marshalOptions) (size int) {
 	list := listv.List()
-	n := list.Len() * wire.SizeFixed32()
+	llen := list.Len()
+	if llen == 0 {
+		return 0
+	}
+	n := llen * wire.SizeFixed32()
 	return tagsize + wire.SizeBytes(n)
 }
 
@@ -3221,7 +3261,11 @@
 // sizeFloatPackedSliceValue returns the size of wire encoding a []float32 value as a packed repeated Float.
 func sizeFloatPackedSliceValue(listv protoreflect.Value, tagsize int, _ marshalOptions) (size int) {
 	list := listv.List()
-	n := list.Len() * wire.SizeFixed32()
+	llen := list.Len()
+	if llen == 0 {
+		return 0
+	}
+	n := llen * wire.SizeFixed32()
 	return tagsize + wire.SizeBytes(n)
 }
 
@@ -3518,7 +3562,11 @@
 // sizeSfixed64PackedSliceValue returns the size of wire encoding a []int64 value as a packed repeated Sfixed64.
 func sizeSfixed64PackedSliceValue(listv protoreflect.Value, tagsize int, _ marshalOptions) (size int) {
 	list := listv.List()
-	n := list.Len() * wire.SizeFixed64()
+	llen := list.Len()
+	if llen == 0 {
+		return 0
+	}
+	n := llen * wire.SizeFixed64()
 	return tagsize + wire.SizeBytes(n)
 }
 
@@ -3815,7 +3863,11 @@
 // sizeFixed64PackedSliceValue returns the size of wire encoding a []uint64 value as a packed repeated Fixed64.
 func sizeFixed64PackedSliceValue(listv protoreflect.Value, tagsize int, _ marshalOptions) (size int) {
 	list := listv.List()
-	n := list.Len() * wire.SizeFixed64()
+	llen := list.Len()
+	if llen == 0 {
+		return 0
+	}
+	n := llen * wire.SizeFixed64()
 	return tagsize + wire.SizeBytes(n)
 }
 
@@ -4112,7 +4164,11 @@
 // sizeDoublePackedSliceValue returns the size of wire encoding a []float64 value as a packed repeated Double.
 func sizeDoublePackedSliceValue(listv protoreflect.Value, tagsize int, _ marshalOptions) (size int) {
 	list := listv.List()
-	n := list.Len() * wire.SizeFixed64()
+	llen := list.Len()
+	if llen == 0 {
+		return 0
+	}
+	n := llen * wire.SizeFixed64()
 	return tagsize + wire.SizeBytes(n)
 }
 
diff --git a/proto/testmessages_test.go b/proto/testmessages_test.go
index 2e722f8..48c0c34 100644
--- a/proto/testmessages_test.go
+++ b/proto/testmessages_test.go
@@ -493,8 +493,23 @@
 		decodeTo: []proto.Message{
 			&testpb.TestAllTypes{},
 			&test3pb.TestAllTypes{},
-			&testpb.TestAllExtensions{},
-		},
+			build(
+				&testpb.TestAllExtensions{},
+				extend(testpb.E_RepeatedInt32Extension, []int32{}),
+				extend(testpb.E_RepeatedInt64Extension, []int64{}),
+				extend(testpb.E_RepeatedUint32Extension, []uint32{}),
+				extend(testpb.E_RepeatedUint64Extension, []uint64{}),
+				extend(testpb.E_RepeatedSint32Extension, []int32{}),
+				extend(testpb.E_RepeatedSint64Extension, []int64{}),
+				extend(testpb.E_RepeatedFixed32Extension, []uint32{}),
+				extend(testpb.E_RepeatedFixed64Extension, []uint64{}),
+				extend(testpb.E_RepeatedSfixed32Extension, []int32{}),
+				extend(testpb.E_RepeatedSfixed64Extension, []int64{}),
+				extend(testpb.E_RepeatedFloatExtension, []float32{}),
+				extend(testpb.E_RepeatedDoubleExtension, []float64{}),
+				extend(testpb.E_RepeatedBoolExtension, []bool{}),
+				extend(testpb.E_RepeatedNestedEnumExtension, []testpb.TestAllTypes_NestedEnum{}),
+			)},
 		wire: pack.Message{
 			pack.Tag{31, pack.BytesType}, pack.LengthPrefix{},
 			pack.Tag{32, pack.BytesType}, pack.LengthPrefix{},
@@ -602,8 +617,23 @@
 		desc: "packed repeated types (zero length)",
 		decodeTo: []proto.Message{
 			&testpb.TestPackedTypes{},
-			&testpb.TestPackedExtensions{},
-		},
+			build(
+				&testpb.TestPackedExtensions{},
+				extend(testpb.E_PackedInt32Extension, []int32{}),
+				extend(testpb.E_PackedInt64Extension, []int64{}),
+				extend(testpb.E_PackedUint32Extension, []uint32{}),
+				extend(testpb.E_PackedUint64Extension, []uint64{}),
+				extend(testpb.E_PackedSint32Extension, []int32{}),
+				extend(testpb.E_PackedSint64Extension, []int64{}),
+				extend(testpb.E_PackedFixed32Extension, []uint32{}),
+				extend(testpb.E_PackedFixed64Extension, []uint64{}),
+				extend(testpb.E_PackedSfixed32Extension, []int32{}),
+				extend(testpb.E_PackedSfixed64Extension, []int64{}),
+				extend(testpb.E_PackedFloatExtension, []float32{}),
+				extend(testpb.E_PackedDoubleExtension, []float64{}),
+				extend(testpb.E_PackedBoolExtension, []bool{}),
+				extend(testpb.E_PackedEnumExtension, []testpb.ForeignEnum{}),
+			)},
 		wire: pack.Message{
 			pack.Tag{90, pack.BytesType}, pack.LengthPrefix{},
 			pack.Tag{91, pack.BytesType}, pack.LengthPrefix{},