proto: consistently use non-nil, zero-length []bytes for empty bytes strings

The fast-path decoder decodes zero-length repeated bytes values as
non-nil, zero-length []bytes. Do the same in the reflection decoder.

This isn't really a correctness issue, since there's no ambiguity about what a
nil entry in a [][]byte means. Still a good idea for consistency, and
retains v1 behavior.

Change-Id: Icd2cb726d14ff1f2b9f142e65756777a359971f3
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/210257
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
This commit is contained in:
Damien Neil 2019-12-05 14:54:35 -08:00
parent 9f1165c3bf
commit 5366f825ad
4 changed files with 27 additions and 5 deletions

View File

@ -233,7 +233,7 @@ var ProtoKinds = []ProtoKind{
{
Name: "Bytes",
WireType: WireBytes,
ToValue: "protoreflect.ValueOfBytes(append(([]byte)(nil), v...))",
ToValue: "protoreflect.ValueOfBytes(append(emptyBuf[:], v...))",
FromValue: "v.Bytes()",
GoType: GoBytes,
ToGoType: "append(emptyBuf[:], v...)",
@ -344,6 +344,9 @@ func (o UnmarshalOptions) unmarshalList(b []byte, wtyp wire.Type, list protorefl
return 0, errUnknown
}
}
// We append to an empty array rather than a nil []byte to get non-nil zero-length byte slices.
var emptyBuf [0]byte
`))
func generateProtoEncode() string {

View File

@ -4711,7 +4711,7 @@ func consumeBytesValue(b []byte, _ protoreflect.Value, _ wire.Number, wtyp wire.
if n < 0 {
return protoreflect.Value{}, 0, wire.ParseError(n)
}
return protoreflect.ValueOfBytes(append(([]byte)(nil), v...)), n, nil
return protoreflect.ValueOfBytes(append(emptyBuf[:], v...)), n, nil
}
var coderBytesValue = valueCoderFuncs{
@ -4751,7 +4751,7 @@ func consumeBytesSliceValue(b []byte, listv protoreflect.Value, _ wire.Number, w
if n < 0 {
return protoreflect.Value{}, 0, wire.ParseError(n)
}
list.Append(protoreflect.ValueOfBytes(append(([]byte)(nil), v...)))
list.Append(protoreflect.ValueOfBytes(append(emptyBuf[:], v...)))
return listv, n, nil
}

View File

@ -167,7 +167,7 @@ func (o UnmarshalOptions) unmarshalScalar(b []byte, wtyp wire.Type, fd protorefl
if n < 0 {
return val, 0, wire.ParseError(n)
}
return protoreflect.ValueOfBytes(append(([]byte)(nil), v...)), n, nil
return protoreflect.ValueOfBytes(append(emptyBuf[:], v...)), n, nil
case protoreflect.MessageKind:
if wtyp != wire.BytesType {
return val, 0, errUnknown
@ -564,7 +564,7 @@ func (o UnmarshalOptions) unmarshalList(b []byte, wtyp wire.Type, list protorefl
if n < 0 {
return 0, wire.ParseError(n)
}
list.Append(protoreflect.ValueOfBytes(append(([]byte)(nil), v...)))
list.Append(protoreflect.ValueOfBytes(append(emptyBuf[:], v...)))
return n, nil
case protoreflect.MessageKind:
if wtyp != wire.BytesType {
@ -598,3 +598,6 @@ func (o UnmarshalOptions) unmarshalList(b []byte, wtyp wire.Type, list protorefl
return 0, errUnknown
}
}
// We append to an empty array rather than a nil []byte to get non-nil zero-length byte slices.
var emptyBuf [0]byte

View File

@ -1773,6 +1773,22 @@ var invalidFieldNumberTestProtos = []struct {
},
}
func TestDecodeEmptyBytes(t *testing.T) {
// There's really nothing wrong with a nil entry in a [][]byte,
// but we take care to produce non-nil []bytes for zero-length
// byte strings, so test for it.
m := &testpb.TestAllTypes{}
b := pack.Message{
pack.Tag{45, pack.BytesType}, pack.Bytes(nil),
}.Marshal()
if err := proto.Unmarshal(b, m); err != nil {
t.Fatal(err)
}
if m.RepeatedBytes[0] == nil {
t.Errorf("unmarshaling repeated bytes field containing zero-length value: Got nil bytes, want non-nil")
}
}
func build(m proto.Message, opts ...buildOpt) proto.Message {
for _, opt := range opts {
opt(m)