mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-01-06 00:55:51 +00:00
524c60670a
Change the representation of option flags in protoiface from bools to a bitfield. This brings the representation of options in protoiface in sync with that in internal/impl. This change has several benefits: 1. We will probably find that we need to add more option flags over time. Converting to the more efficient representation of these flags as high in the call stack as possible minimizes the performance implication of the struct growing. 2. On a similar note, this avoids the need to convert from the compact representation to the larger one when passing from internal/impl to proto, since the {Marshal,Unmarshal}State methods take the compact form. 3. This removes unused options from protoiface. Instead of documenting that AllowPartial is always set, we can just not include an AllowPartial flag in the protoiface options. 4. Conversely, this provides a way to add option flags to protoiface that we don't want to expose in the proto package. name old time/op new time/op delta EmptyMessage/Wire/Marshal-12 11.1ns ± 7% 10.1ns ± 1% -9.35% (p=0.000 n=8+8) EmptyMessage/Wire/Unmarshal-12 7.07ns ± 0% 6.74ns ± 1% -4.58% (p=0.000 n=8+8) EmptyMessage/Wire/Validate-12 4.30ns ± 1% 3.80ns ± 8% -11.45% (p=0.000 n=7+8) RepeatedInt32/Wire/Marshal-12 1.17µs ± 1% 1.21µs ± 7% +4.09% (p=0.000 n=8+8) RepeatedInt32/Wire/Unmarshal-12 938ns ± 0% 942ns ± 3% ~ (p=0.178 n=7+8) RepeatedInt32/Wire/Validate-12 521ns ± 4% 543ns ± 7% ~ (p=0.157 n=7+8) Required/Wire/Marshal-12 97.2ns ± 1% 95.3ns ± 1% -1.98% (p=0.001 n=7+7) Required/Wire/Unmarshal-12 41.0ns ± 9% 38.6ns ± 3% -5.73% (p=0.048 n=8+8) Required/Wire/Validate-12 25.4ns ±11% 21.4ns ± 3% -15.62% (p=0.000 n=8+7) Change-Id: I3ac1b00ab36cfdf61316ec087a5dd20d9248e4f6 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/216760 Reviewed-by: Joe Tsai <joetsai@google.com>
202 lines
5.3 KiB
Go
202 lines
5.3 KiB
Go
// 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 impl
|
|
|
|
import (
|
|
"math/bits"
|
|
|
|
"google.golang.org/protobuf/internal/encoding/wire"
|
|
"google.golang.org/protobuf/internal/errors"
|
|
"google.golang.org/protobuf/internal/flags"
|
|
"google.golang.org/protobuf/proto"
|
|
pref "google.golang.org/protobuf/reflect/protoreflect"
|
|
preg "google.golang.org/protobuf/reflect/protoregistry"
|
|
piface "google.golang.org/protobuf/runtime/protoiface"
|
|
)
|
|
|
|
type unmarshalOptions piface.UnmarshalOptions
|
|
|
|
func (o unmarshalOptions) Options() proto.UnmarshalOptions {
|
|
return proto.UnmarshalOptions{
|
|
Merge: true,
|
|
AllowPartial: true,
|
|
DiscardUnknown: o.DiscardUnknown(),
|
|
Resolver: o.Resolver,
|
|
}
|
|
}
|
|
|
|
func (o unmarshalOptions) DiscardUnknown() bool { return o.Flags&piface.UnmarshalDiscardUnknown != 0 }
|
|
|
|
type unmarshalOutput struct {
|
|
n int // number of bytes consumed
|
|
initialized bool
|
|
}
|
|
|
|
// unmarshal is protoreflect.Methods.Unmarshal.
|
|
func (mi *MessageInfo) unmarshal(m pref.Message, in piface.UnmarshalInput, opts piface.UnmarshalOptions) (piface.UnmarshalOutput, error) {
|
|
var p pointer
|
|
if ms, ok := m.(*messageState); ok {
|
|
p = ms.pointer()
|
|
} else {
|
|
p = m.(*messageReflectWrapper).pointer()
|
|
}
|
|
out, err := mi.unmarshalPointer(in.Buf, p, 0, unmarshalOptions(opts))
|
|
return piface.UnmarshalOutput{
|
|
Initialized: out.initialized,
|
|
}, err
|
|
}
|
|
|
|
// errUnknown is returned during unmarshaling to indicate a parse error that
|
|
// should result in a field being placed in the unknown fields section (for example,
|
|
// when the wire type doesn't match) as opposed to the entire unmarshal operation
|
|
// failing (for example, when a field extends past the available input).
|
|
//
|
|
// This is a sentinel error which should never be visible to the user.
|
|
var errUnknown = errors.New("unknown")
|
|
|
|
func (mi *MessageInfo) unmarshalPointer(b []byte, p pointer, groupTag wire.Number, opts unmarshalOptions) (out unmarshalOutput, err error) {
|
|
mi.init()
|
|
if flags.ProtoLegacy && mi.isMessageSet {
|
|
return unmarshalMessageSet(mi, b, p, opts)
|
|
}
|
|
initialized := true
|
|
var requiredMask uint64
|
|
var exts *map[int32]ExtensionField
|
|
start := len(b)
|
|
for len(b) > 0 {
|
|
// Parse the tag (field number and wire type).
|
|
var tag uint64
|
|
if b[0] < 0x80 {
|
|
tag = uint64(b[0])
|
|
b = b[1:]
|
|
} else if len(b) >= 2 && b[1] < 128 {
|
|
tag = uint64(b[0]&0x7f) + uint64(b[1])<<7
|
|
b = b[2:]
|
|
} else {
|
|
var n int
|
|
tag, n = wire.ConsumeVarint(b)
|
|
if n < 0 {
|
|
return out, wire.ParseError(n)
|
|
}
|
|
b = b[n:]
|
|
}
|
|
num := wire.Number(tag >> 3)
|
|
wtyp := wire.Type(tag & 7)
|
|
|
|
if num < wire.MinValidNumber || num > wire.MaxValidNumber {
|
|
return out, errors.New("invalid field number")
|
|
}
|
|
|
|
if wtyp == wire.EndGroupType {
|
|
if num != groupTag {
|
|
return out, errors.New("mismatching end group marker")
|
|
}
|
|
groupTag = 0
|
|
break
|
|
}
|
|
|
|
var f *coderFieldInfo
|
|
if int(num) < len(mi.denseCoderFields) {
|
|
f = mi.denseCoderFields[num]
|
|
} else {
|
|
f = mi.coderFields[num]
|
|
}
|
|
var n int
|
|
err := errUnknown
|
|
switch {
|
|
case f != nil:
|
|
if f.funcs.unmarshal == nil {
|
|
break
|
|
}
|
|
var o unmarshalOutput
|
|
o, err = f.funcs.unmarshal(b, p.Apply(f.offset), wtyp, opts)
|
|
n = o.n
|
|
if err != nil {
|
|
break
|
|
}
|
|
requiredMask |= f.validation.requiredBit
|
|
if f.funcs.isInit != nil && !o.initialized {
|
|
initialized = false
|
|
}
|
|
default:
|
|
// Possible extension.
|
|
if exts == nil && mi.extensionOffset.IsValid() {
|
|
exts = p.Apply(mi.extensionOffset).Extensions()
|
|
if *exts == nil {
|
|
*exts = make(map[int32]ExtensionField)
|
|
}
|
|
}
|
|
if exts == nil {
|
|
break
|
|
}
|
|
var o unmarshalOutput
|
|
o, err = mi.unmarshalExtension(b, num, wtyp, *exts, opts)
|
|
n = o.n
|
|
if !o.initialized {
|
|
initialized = false
|
|
}
|
|
}
|
|
if err != nil {
|
|
if err != errUnknown {
|
|
return out, err
|
|
}
|
|
n = wire.ConsumeFieldValue(num, wtyp, b)
|
|
if n < 0 {
|
|
return out, wire.ParseError(n)
|
|
}
|
|
if !opts.DiscardUnknown() && mi.unknownOffset.IsValid() {
|
|
u := p.Apply(mi.unknownOffset).Bytes()
|
|
*u = wire.AppendTag(*u, num, wtyp)
|
|
*u = append(*u, b[:n]...)
|
|
}
|
|
}
|
|
b = b[n:]
|
|
}
|
|
if groupTag != 0 {
|
|
return out, errors.New("missing end group marker")
|
|
}
|
|
if mi.numRequiredFields > 0 && bits.OnesCount64(requiredMask) != int(mi.numRequiredFields) {
|
|
initialized = false
|
|
}
|
|
if initialized {
|
|
out.initialized = true
|
|
}
|
|
out.n = start - len(b)
|
|
return out, nil
|
|
}
|
|
|
|
func (mi *MessageInfo) unmarshalExtension(b []byte, num wire.Number, wtyp wire.Type, exts map[int32]ExtensionField, opts unmarshalOptions) (out unmarshalOutput, err error) {
|
|
x := exts[int32(num)]
|
|
xt := x.Type()
|
|
if xt == nil {
|
|
var err error
|
|
xt, err = opts.Resolver.FindExtensionByNumber(mi.Desc.FullName(), num)
|
|
if err != nil {
|
|
if err == preg.NotFound {
|
|
return out, errUnknown
|
|
}
|
|
return out, err
|
|
}
|
|
}
|
|
xi := getExtensionFieldInfo(xt)
|
|
if xi.funcs.unmarshal == nil {
|
|
return out, errUnknown
|
|
}
|
|
ival := x.Value()
|
|
if !ival.IsValid() && xi.unmarshalNeedsValue {
|
|
// Create a new message, list, or map value to fill in.
|
|
// For enums, create a prototype value to let the unmarshal func know the
|
|
// concrete type.
|
|
ival = xt.New()
|
|
}
|
|
v, out, err := xi.funcs.unmarshal(b, ival, num, wtyp, opts)
|
|
if err != nil {
|
|
return out, err
|
|
}
|
|
x.Set(xt, v)
|
|
exts[int32(num)] = x
|
|
return out, nil
|
|
}
|