proto: wire encoding support

Add proto.Marshal.

Change-Id: If7254bb4c4cbbee782a2a163762f9fcf98e7ab08
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/167388
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
This commit is contained in:
Damien Neil 2019-03-13 17:06:42 -07:00
parent 4866b95a73
commit 99f24c33b0
7 changed files with 611 additions and 58 deletions

View File

@ -39,8 +39,9 @@ func main() {
chdirRoot() chdirRoot()
writeSource("internal/fileinit/desc_list_gen.go", generateFileinitDescList()) writeSource("internal/fileinit/desc_list_gen.go", generateFileinitDescList())
writeSource("proto/decode_gen.go", generateProtoDecode())
writeSource("internal/prototype/protofile_list_gen.go", generateListTypes()) writeSource("internal/prototype/protofile_list_gen.go", generateListTypes())
writeSource("proto/decode_gen.go", generateProtoDecode())
writeSource("proto/encode_gen.go", generateProtoEncode())
} }
// chdirRoot changes the working directory to the repository root. // chdirRoot changes the working directory to the repository root.
@ -304,6 +305,7 @@ func writeSource(file, src string) {
"sync", "sync",
"", "",
"github.com/golang/protobuf/v2/internal/encoding/wire", "github.com/golang/protobuf/v2/internal/encoding/wire",
"github.com/golang/protobuf/v2/internal/errors",
"github.com/golang/protobuf/v2/internal/pragma", "github.com/golang/protobuf/v2/internal/pragma",
"github.com/golang/protobuf/v2/internal/typefmt", "github.com/golang/protobuf/v2/internal/typefmt",
"github.com/golang/protobuf/v2/reflect/protoreflect", "github.com/golang/protobuf/v2/reflect/protoreflect",

View File

@ -28,9 +28,10 @@ func (w WireType) Packable() bool {
} }
type ProtoKind struct { type ProtoKind struct {
Name string Name string
WireType WireType WireType WireType
ToValue Expr ToValue Expr
FromValue Expr
} }
func (k ProtoKind) Expr() Expr { func (k ProtoKind) Expr() Expr {
@ -39,94 +40,112 @@ func (k ProtoKind) Expr() Expr {
var ProtoKinds = []ProtoKind{ var ProtoKinds = []ProtoKind{
{ {
Name: "Bool", Name: "Bool",
WireType: WireVarint, WireType: WireVarint,
ToValue: "wire.DecodeBool(v)", ToValue: "wire.DecodeBool(v)",
FromValue: "wire.EncodeBool(v.Bool())",
}, },
{ {
Name: "Enum", Name: "Enum",
WireType: WireVarint, WireType: WireVarint,
ToValue: "protoreflect.EnumNumber(v)", ToValue: "protoreflect.EnumNumber(v)",
FromValue: "uint64(v.Enum())",
}, },
{ {
Name: "Int32", Name: "Int32",
WireType: WireVarint, WireType: WireVarint,
ToValue: "int32(v)", ToValue: "int32(v)",
FromValue: "uint64(int32(v.Int()))",
}, },
{ {
Name: "Sint32", Name: "Sint32",
WireType: WireVarint, WireType: WireVarint,
ToValue: "int32(wire.DecodeZigZag(v & math.MaxUint32))", ToValue: "int32(wire.DecodeZigZag(v & math.MaxUint32))",
FromValue: "wire.EncodeZigZag(int64(int32(v.Int())))",
}, },
{ {
Name: "Uint32", Name: "Uint32",
WireType: WireVarint, WireType: WireVarint,
ToValue: "uint32(v)", ToValue: "uint32(v)",
FromValue: "uint64(uint32(v.Uint()))",
}, },
{ {
Name: "Int64", Name: "Int64",
WireType: WireVarint, WireType: WireVarint,
ToValue: "int64(v)", ToValue: "int64(v)",
FromValue: "uint64(v.Int())",
}, },
{ {
Name: "Sint64", Name: "Sint64",
WireType: WireVarint, WireType: WireVarint,
ToValue: "wire.DecodeZigZag(v)", ToValue: "wire.DecodeZigZag(v)",
FromValue: "wire.EncodeZigZag(v.Int())",
}, },
{ {
Name: "Uint64", Name: "Uint64",
WireType: WireVarint, WireType: WireVarint,
ToValue: "v", ToValue: "v",
FromValue: "v.Uint()",
}, },
{ {
Name: "Sfixed32", Name: "Sfixed32",
WireType: WireFixed32, WireType: WireFixed32,
ToValue: "int32(v)", ToValue: "int32(v)",
FromValue: "uint32(v.Int())",
}, },
{ {
Name: "Fixed32", Name: "Fixed32",
WireType: WireFixed32, WireType: WireFixed32,
ToValue: "uint32(v)", ToValue: "uint32(v)",
FromValue: "uint32(v.Uint())",
}, },
{ {
Name: "Float", Name: "Float",
WireType: WireFixed32, WireType: WireFixed32,
ToValue: "math.Float32frombits(uint32(v))", ToValue: "math.Float32frombits(uint32(v))",
FromValue: "math.Float32bits(float32(v.Float()))",
}, },
{ {
Name: "Sfixed64", Name: "Sfixed64",
WireType: WireFixed64, WireType: WireFixed64,
ToValue: "int64(v)", ToValue: "int64(v)",
FromValue: "uint64(v.Int())",
}, },
{ {
Name: "Fixed64", Name: "Fixed64",
WireType: WireFixed64, WireType: WireFixed64,
ToValue: "v", ToValue: "v",
FromValue: "v.Uint()",
}, },
{ {
Name: "Double", Name: "Double",
WireType: WireFixed64, WireType: WireFixed64,
ToValue: "math.Float64frombits(v)", ToValue: "math.Float64frombits(v)",
FromValue: "math.Float64bits(v.Float())",
}, },
{ {
Name: "String", Name: "String",
WireType: WireBytes, WireType: WireBytes,
ToValue: "string(v)", ToValue: "string(v)",
FromValue: "[]byte(v.String())",
}, },
{ {
Name: "Bytes", Name: "Bytes",
WireType: WireBytes, WireType: WireBytes,
ToValue: "append(([]byte)(nil), v...)", ToValue: "append(([]byte)(nil), v...)",
FromValue: "v.Bytes()",
}, },
{ {
Name: "Message", Name: "Message",
WireType: WireBytes, WireType: WireBytes,
ToValue: "v", ToValue: "v",
FromValue: "v",
}, },
{ {
Name: "Group", Name: "Group",
WireType: WireGroup, WireType: WireGroup,
ToValue: "v", ToValue: "v",
FromValue: "v",
}, },
} }
@ -208,3 +227,45 @@ func (o UnmarshalOptions) unmarshalList(b []byte, wtyp wire.Type, num wire.Numbe
} }
} }
`)) `))
func generateProtoEncode() string {
return mustExecute(protoEncodeTemplate, ProtoKinds)
}
var protoEncodeTemplate = template.Must(template.New("").Parse(`
var wireTypes = map[protoreflect.Kind]wire.Type{
{{- range .}}
{{.Expr}}: {{.WireType.Expr}},
{{- end}}
}
func (o MarshalOptions) marshalSingular(b []byte, num wire.Number, kind protoreflect.Kind, v protoreflect.Value) ([]byte, error) {
switch kind {
{{- range .}}
case {{.Expr}}:
{{if (eq .Name "Message") -}}
var pos int
var err error
b, pos = appendSpeculativeLength(b)
b, err = o.marshalMessage(b, v.Message())
if err != nil {
return nil, err
}
b = finishSpeculativeLength(b, pos)
{{- else if (eq .Name "Group") -}}
var err error
b, err = o.marshalMessage(b, v.Message())
if err != nil {
return nil, err
}
b = wire.AppendVarint(b, wire.EncodeTag(num, wire.EndGroupType))
{{- else -}}
b = wire.Append{{.WireType}}(b, {{.FromValue}})
{{- end}}
{{- end}}
default:
return nil, errors.New("invalid kind %v", kind)
}
return b, nil
}
`))

View File

@ -0,0 +1,47 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package mapsort provides sorted access to maps.
package mapsort
import (
"sort"
"github.com/golang/protobuf/v2/reflect/protoreflect"
)
// Range iterates over every map entry in sorted key order,
// calling f for each key and value encountered.
func Range(mapv protoreflect.Map, keyKind protoreflect.Kind, f func(protoreflect.MapKey, protoreflect.Value) bool) {
var keys []protoreflect.MapKey
mapv.Range(func(key protoreflect.MapKey, _ protoreflect.Value) bool {
keys = append(keys, key)
return true
})
sort.Slice(keys, func(i, j int) bool {
switch keyKind {
case protoreflect.BoolKind:
return !keys[i].Bool() && keys[j].Bool()
case protoreflect.Int32Kind,
protoreflect.Sint32Kind,
protoreflect.Sfixed32Kind,
protoreflect.Int64Kind,
protoreflect.Sint64Kind,
protoreflect.Sfixed64Kind:
return keys[i].Int() < keys[j].Int()
case protoreflect.Uint32Kind,
protoreflect.Fixed32Kind,
protoreflect.Uint64Kind,
protoreflect.Fixed64Kind:
return keys[i].Uint() < keys[j].Uint()
default:
return keys[i].String() < keys[j].String()
}
})
for _, key := range keys {
if !f(key, mapv.Get(key)) {
break
}
}
}

View File

@ -0,0 +1,67 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package mapsort_test
import (
"reflect"
"testing"
"github.com/golang/protobuf/v2/internal/mapsort"
"github.com/golang/protobuf/v2/internal/value"
pref "github.com/golang/protobuf/v2/reflect/protoreflect"
)
func TestRange(t *testing.T) {
for _, test := range []struct {
mapv interface{}
kind pref.Kind
}{
{
mapv: &map[bool]int32{
false: 0,
true: 1,
},
kind: pref.BoolKind,
},
{
mapv: &map[int32]int32{
0: 0,
1: 1,
2: 2,
},
kind: pref.Int32Kind,
},
{
mapv: &map[uint64]int32{
0: 0,
1: 1,
2: 2,
},
kind: pref.Uint64Kind,
},
{
mapv: &map[string]int32{
"a": 0,
"b": 1,
"c": 2,
},
kind: pref.StringKind,
},
} {
rv := reflect.TypeOf(test.mapv).Elem()
mapv := value.MapOf(test.mapv, value.NewConverter(rv.Key(), test.kind), value.NewConverter(rv.Elem(), pref.Int32Kind))
var got []pref.MapKey
mapsort.Range(mapv, test.kind, func(key pref.MapKey, _ pref.Value) bool {
got = append(got, key)
return true
})
for i, key := range got {
if int64(i) != mapv.Get(key).Int() {
t.Errorf("out of order range over map: %v", got)
break
}
}
}
}

216
proto/encode.go Normal file
View File

@ -0,0 +1,216 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package proto
import (
"fmt"
"sort"
"github.com/golang/protobuf/v2/internal/encoding/wire"
"github.com/golang/protobuf/v2/internal/mapsort"
"github.com/golang/protobuf/v2/internal/pragma"
"github.com/golang/protobuf/v2/reflect/protoreflect"
)
// MarshalOptions configures the marshaler.
//
// Example usage:
// b, err := MarshalOptions{Deterministic: true}.Marshal(m)
type MarshalOptions struct {
// Deterministic controls whether the same message will always be
// serialized to the same bytes within the same binary.
//
// Setting this option guarantees that repeated serialization of
// the same message will return the same bytes, and that different
// processes of the same binary (which may be executing on different
// machines) will serialize equal messages to the same bytes.
//
// Note that the deterministic serialization is NOT canonical across
// languages. It is not guaranteed to remain stable over time. It is
// unstable across different builds with schema changes due to unknown
// fields. Users who need canonical serialization (e.g., persistent
// storage in a canonical form, fingerprinting, etc.) must define
// their own canonicalization specification and implement their own
// serializer rather than relying on this API.
//
// If deterministic serialization is requested, map entries will be
// sorted by keys in lexographical order. This is an implementation
// detail and subject to change.
Deterministic bool
pragma.NoUnkeyedLiterals
}
// Marshal returns the wire-format encoding of m.
func Marshal(m Message) ([]byte, error) {
return MarshalOptions{}.MarshalAppend(nil, m)
}
// Marshal returns the wire-format encoding of m.
func (o MarshalOptions) Marshal(m Message) ([]byte, error) {
return o.marshalMessage(nil, m.ProtoReflect())
}
// MarshalAppend appends the wire-format encoding of m to b,
// returning the result.
func (o MarshalOptions) MarshalAppend(b []byte, m Message) ([]byte, error) {
return o.marshalMessage(b, m.ProtoReflect())
}
func (o MarshalOptions) marshalMessage(b []byte, m protoreflect.Message) ([]byte, error) {
// There are many choices for what order we visit fields in. The default one here
// is chosen for reasonable efficiency and simplicity given the protoreflect API.
// It is not deterministic, since KnownFields.Range does not return fields in any
// defined order.
//
// When using deterministic serialization, we sort the known fields by field number.
fields := m.Type().Fields()
knownFields := m.KnownFields()
var err error
o.rangeKnown(knownFields, func(num protoreflect.FieldNumber, value protoreflect.Value) bool {
field := fields.ByNumber(num)
if field == nil {
field = knownFields.ExtensionTypes().ByNumber(num)
if field == nil {
panic(fmt.Errorf("no descriptor for field %d in %q", num, m.Type().FullName()))
}
}
b, err = o.marshalField(b, field, value)
return err == nil
})
if err != nil {
return nil, err
}
m.UnknownFields().Range(func(_ protoreflect.FieldNumber, raw protoreflect.RawFields) bool {
b = append(b, raw...)
return true
})
// TODO: required field checks
return b, nil
}
// rangeKnown visits known fields in field number order when deterministic
// serialization is enabled.
func (o MarshalOptions) rangeKnown(knownFields protoreflect.KnownFields, f func(protoreflect.FieldNumber, protoreflect.Value) bool) {
if !o.Deterministic {
knownFields.Range(f)
return
}
nums := make([]protoreflect.FieldNumber, 0, knownFields.Len())
knownFields.Range(func(num protoreflect.FieldNumber, _ protoreflect.Value) bool {
nums = append(nums, num)
return true
})
sort.Slice(nums, func(a, b int) bool {
return nums[a] < nums[b]
})
for _, num := range nums {
if !f(num, knownFields.Get(num)) {
break
}
}
}
func (o MarshalOptions) marshalField(b []byte, field protoreflect.FieldDescriptor, value protoreflect.Value) ([]byte, error) {
num := field.Number()
kind := field.Kind()
switch {
case field.Cardinality() != protoreflect.Repeated:
b = wire.AppendTag(b, num, wireTypes[kind])
return o.marshalSingular(b, num, kind, value)
case field.IsMap():
return o.marshalMap(b, num, kind, field.MessageType(), value.Map())
case field.IsPacked():
return o.marshalPacked(b, num, kind, value.List())
default:
return o.marshalList(b, num, kind, value.List())
}
}
func (o MarshalOptions) marshalMap(b []byte, num wire.Number, kind protoreflect.Kind, mdesc protoreflect.MessageDescriptor, mapv protoreflect.Map) ([]byte, error) {
keyf := mdesc.Fields().ByNumber(1)
valf := mdesc.Fields().ByNumber(2)
var err error
o.rangeMap(mapv, keyf.Kind(), func(key protoreflect.MapKey, value protoreflect.Value) bool {
b = wire.AppendTag(b, num, wire.BytesType)
var pos int
b, pos = appendSpeculativeLength(b)
b, err = o.marshalField(b, keyf, key.Value())
if err != nil {
return false
}
b, err = o.marshalField(b, valf, value)
if err != nil {
return false
}
b = finishSpeculativeLength(b, pos)
return true
})
if err != nil {
return nil, err
}
return b, nil
}
func (o MarshalOptions) rangeMap(mapv protoreflect.Map, kind protoreflect.Kind, f func(protoreflect.MapKey, protoreflect.Value) bool) {
if !o.Deterministic {
mapv.Range(f)
return
}
mapsort.Range(mapv, kind, f)
}
func (o MarshalOptions) marshalPacked(b []byte, num wire.Number, kind protoreflect.Kind, list protoreflect.List) ([]byte, error) {
b = wire.AppendTag(b, num, wire.BytesType)
b, pos := appendSpeculativeLength(b)
for i, llen := 0, list.Len(); i < llen; i++ {
var err error
b, err = o.marshalSingular(b, num, kind, list.Get(i))
if err != nil {
return nil, err
}
}
b = finishSpeculativeLength(b, pos)
return b, nil
}
func (o MarshalOptions) marshalList(b []byte, num wire.Number, kind protoreflect.Kind, list protoreflect.List) ([]byte, error) {
for i, llen := 0, list.Len(); i < llen; i++ {
var err error
b = wire.AppendTag(b, num, wireTypes[kind])
b, err = o.marshalSingular(b, num, kind, list.Get(i))
if err != nil {
return nil, err
}
}
return b, nil
}
// When encoding length-prefixed fields, we speculatively set aside some number of bytes
// for the length, encode the data, and then encode the length (shifting the data if necessary
// to make room).
const speculativeLength = 1
func appendSpeculativeLength(b []byte) ([]byte, int) {
pos := len(b)
b = append(b, "\x00\x00\x00\x00"[:speculativeLength]...)
return b, pos
}
func finishSpeculativeLength(b []byte, pos int) []byte {
mlen := len(b) - pos - speculativeLength
msiz := wire.SizeVarint(uint64(mlen))
if msiz != speculativeLength {
for i := 0; i < msiz-speculativeLength; i++ {
b = append(b, 0)
}
copy(b[pos+msiz:], b[pos+speculativeLength:])
b = b[:pos+msiz+mlen]
}
wire.AppendVarint(b[:pos], uint64(mlen))
return b
}

92
proto/encode_gen.go Normal file
View File

@ -0,0 +1,92 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style.
// license that can be found in the LICENSE file.
// Code generated by generate-types. DO NOT EDIT.
package proto
import (
"math"
"github.com/golang/protobuf/v2/internal/encoding/wire"
"github.com/golang/protobuf/v2/internal/errors"
"github.com/golang/protobuf/v2/reflect/protoreflect"
)
var wireTypes = map[protoreflect.Kind]wire.Type{
protoreflect.BoolKind: wire.VarintType,
protoreflect.EnumKind: wire.VarintType,
protoreflect.Int32Kind: wire.VarintType,
protoreflect.Sint32Kind: wire.VarintType,
protoreflect.Uint32Kind: wire.VarintType,
protoreflect.Int64Kind: wire.VarintType,
protoreflect.Sint64Kind: wire.VarintType,
protoreflect.Uint64Kind: wire.VarintType,
protoreflect.Sfixed32Kind: wire.Fixed32Type,
protoreflect.Fixed32Kind: wire.Fixed32Type,
protoreflect.FloatKind: wire.Fixed32Type,
protoreflect.Sfixed64Kind: wire.Fixed64Type,
protoreflect.Fixed64Kind: wire.Fixed64Type,
protoreflect.DoubleKind: wire.Fixed64Type,
protoreflect.StringKind: wire.BytesType,
protoreflect.BytesKind: wire.BytesType,
protoreflect.MessageKind: wire.BytesType,
protoreflect.GroupKind: wire.StartGroupType,
}
func (o MarshalOptions) marshalSingular(b []byte, num wire.Number, kind protoreflect.Kind, v protoreflect.Value) ([]byte, error) {
switch kind {
case protoreflect.BoolKind:
b = wire.AppendVarint(b, wire.EncodeBool(v.Bool()))
case protoreflect.EnumKind:
b = wire.AppendVarint(b, uint64(v.Enum()))
case protoreflect.Int32Kind:
b = wire.AppendVarint(b, uint64(int32(v.Int())))
case protoreflect.Sint32Kind:
b = wire.AppendVarint(b, wire.EncodeZigZag(int64(int32(v.Int()))))
case protoreflect.Uint32Kind:
b = wire.AppendVarint(b, uint64(uint32(v.Uint())))
case protoreflect.Int64Kind:
b = wire.AppendVarint(b, uint64(v.Int()))
case protoreflect.Sint64Kind:
b = wire.AppendVarint(b, wire.EncodeZigZag(v.Int()))
case protoreflect.Uint64Kind:
b = wire.AppendVarint(b, v.Uint())
case protoreflect.Sfixed32Kind:
b = wire.AppendFixed32(b, uint32(v.Int()))
case protoreflect.Fixed32Kind:
b = wire.AppendFixed32(b, uint32(v.Uint()))
case protoreflect.FloatKind:
b = wire.AppendFixed32(b, math.Float32bits(float32(v.Float())))
case protoreflect.Sfixed64Kind:
b = wire.AppendFixed64(b, uint64(v.Int()))
case protoreflect.Fixed64Kind:
b = wire.AppendFixed64(b, v.Uint())
case protoreflect.DoubleKind:
b = wire.AppendFixed64(b, math.Float64bits(v.Float()))
case protoreflect.StringKind:
b = wire.AppendBytes(b, []byte(v.String()))
case protoreflect.BytesKind:
b = wire.AppendBytes(b, v.Bytes())
case protoreflect.MessageKind:
var pos int
var err error
b, pos = appendSpeculativeLength(b)
b, err = o.marshalMessage(b, v.Message())
if err != nil {
return nil, err
}
b = finishSpeculativeLength(b, pos)
case protoreflect.GroupKind:
var err error
b, err = o.marshalMessage(b, v.Message())
if err != nil {
return nil, err
}
b = wire.AppendVarint(b, wire.EncodeTag(num, wire.EndGroupType))
default:
return nil, errors.New("invalid kind %v", kind)
}
return b, nil
}

68
proto/encode_test.go Normal file
View File

@ -0,0 +1,68 @@
package proto_test
import (
"bytes"
"fmt"
"reflect"
"testing"
protoV1 "github.com/golang/protobuf/proto"
//_ "github.com/golang/protobuf/v2/internal/legacy"
"github.com/golang/protobuf/v2/proto"
"github.com/google/go-cmp/cmp"
)
func TestEncode(t *testing.T) {
for _, test := range testProtos {
for _, want := range test.decodeTo {
t.Run(fmt.Sprintf("%s (%T)", test.desc, want), func(t *testing.T) {
wire, err := proto.Marshal(want)
if err != nil {
t.Fatalf("Marshal error: %v\nMessage:\n%v", err, protoV1.MarshalTextString(want.(protoV1.Message)))
}
got := reflect.New(reflect.TypeOf(want).Elem()).Interface().(proto.Message)
if err := proto.Unmarshal(wire, got); err != nil {
t.Errorf("Unmarshal error: %v\nMessage:\n%v", err, protoV1.MarshalTextString(want.(protoV1.Message)))
return
}
if !protoV1.Equal(got.(protoV1.Message), want.(protoV1.Message)) {
t.Errorf("Unmarshal returned unexpected result; got:\n%v\nwant:\n%v", protoV1.MarshalTextString(got.(protoV1.Message)), protoV1.MarshalTextString(want.(protoV1.Message)))
}
})
}
}
}
func TestEncodeDeterministic(t *testing.T) {
for _, test := range testProtos {
for _, want := range test.decodeTo {
t.Run(fmt.Sprintf("%s (%T)", test.desc, want), func(t *testing.T) {
wire, err := proto.MarshalOptions{Deterministic: true}.Marshal(want)
if err != nil {
t.Fatalf("Marshal error: %v\nMessage:\n%v", err, protoV1.MarshalTextString(want.(protoV1.Message)))
}
wire2, err := proto.MarshalOptions{Deterministic: true}.Marshal(want)
if err != nil {
t.Fatalf("Marshal error: %v\nMessage:\n%v", err, protoV1.MarshalTextString(want.(protoV1.Message)))
}
if !bytes.Equal(wire, wire2) {
t.Fatalf("deterministic marshal returned varying results:\n%v", cmp.Diff(wire, wire2))
}
got := reflect.New(reflect.TypeOf(want).Elem()).Interface().(proto.Message)
if err := proto.Unmarshal(wire, got); err != nil {
t.Errorf("Unmarshal error: %v\nMessage:\n%v", err, protoV1.MarshalTextString(want.(protoV1.Message)))
return
}
if !protoV1.Equal(got.(protoV1.Message), want.(protoV1.Message)) {
t.Errorf("Unmarshal returned unexpected result; got:\n%v\nwant:\n%v", protoV1.MarshalTextString(got.(protoV1.Message)), protoV1.MarshalTextString(want.(protoV1.Message)))
}
})
}
}
}