mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-01-30 12:32:36 +00:00
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:
parent
4866b95a73
commit
99f24c33b0
@ -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",
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
`))
|
||||||
|
47
internal/mapsort/mapsort.go
Normal file
47
internal/mapsort/mapsort.go
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
67
internal/mapsort/mapsort_test.go
Normal file
67
internal/mapsort/mapsort_test.go
Normal 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
216
proto/encode.go
Normal 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
92
proto/encode_gen.go
Normal 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
68
proto/encode_test.go
Normal 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)))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user