mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-02-21 09:40:03 +00:00
In the upcoming 3.12.x release of protoc, the proto3 language will be amended to support true presence for scalars. This CL adds support to both the generator and runtime to support these semantics. Newly added public API: protogen.Plugin.SupportedFeatures protoreflect.FieldDescriptor.HasPresence protoreflect.FieldDescriptor.HasOptionalKeyword protoreflect.OneofDescriptor.IsSynthetic Change-Id: I7c86bf66d0ae56642109beb5f2132184593747ad Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/230698 Reviewed-by: Damien Neil <dneil@google.com>
171 lines
5.3 KiB
Go
171 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 (
|
|
"fmt"
|
|
"reflect"
|
|
"sort"
|
|
|
|
"google.golang.org/protobuf/encoding/protowire"
|
|
"google.golang.org/protobuf/internal/encoding/messageset"
|
|
"google.golang.org/protobuf/internal/fieldsort"
|
|
pref "google.golang.org/protobuf/reflect/protoreflect"
|
|
piface "google.golang.org/protobuf/runtime/protoiface"
|
|
)
|
|
|
|
// coderMessageInfo contains per-message information used by the fast-path functions.
|
|
// This is a different type from MessageInfo to keep MessageInfo as general-purpose as
|
|
// possible.
|
|
type coderMessageInfo struct {
|
|
methods piface.Methods
|
|
|
|
orderedCoderFields []*coderFieldInfo
|
|
denseCoderFields []*coderFieldInfo
|
|
coderFields map[protowire.Number]*coderFieldInfo
|
|
sizecacheOffset offset
|
|
unknownOffset offset
|
|
extensionOffset offset
|
|
needsInitCheck bool
|
|
isMessageSet bool
|
|
numRequiredFields uint8
|
|
|
|
// Include space for a number of coderFieldInfos to improve cache locality.
|
|
// The number of entries is chosen through a combination of guesswork and
|
|
// empirical testing.
|
|
coderFieldBuf [32]coderFieldInfo
|
|
}
|
|
|
|
type coderFieldInfo struct {
|
|
funcs pointerCoderFuncs // fast-path per-field functions
|
|
mi *MessageInfo // field's message
|
|
ft reflect.Type
|
|
validation validationInfo // information used by message validation
|
|
num pref.FieldNumber // field number
|
|
offset offset // struct field offset
|
|
wiretag uint64 // field tag (number + wire type)
|
|
tagsize int // size of the varint-encoded tag
|
|
isPointer bool // true if IsNil may be called on the struct field
|
|
isRequired bool // true if field is required
|
|
}
|
|
|
|
func (mi *MessageInfo) makeCoderMethods(t reflect.Type, si structInfo) {
|
|
mi.sizecacheOffset = si.sizecacheOffset
|
|
mi.unknownOffset = si.unknownOffset
|
|
mi.extensionOffset = si.extensionOffset
|
|
|
|
mi.coderFields = make(map[protowire.Number]*coderFieldInfo)
|
|
fields := mi.Desc.Fields()
|
|
preallocFields := mi.coderFieldBuf[:]
|
|
for i := 0; i < fields.Len(); i++ {
|
|
fd := fields.Get(i)
|
|
|
|
fs := si.fieldsByNumber[fd.Number()]
|
|
isOneof := fd.ContainingOneof() != nil && !fd.ContainingOneof().IsSynthetic()
|
|
if isOneof {
|
|
fs = si.oneofsByName[fd.ContainingOneof().Name()]
|
|
}
|
|
ft := fs.Type
|
|
var wiretag uint64
|
|
if !fd.IsPacked() {
|
|
wiretag = protowire.EncodeTag(fd.Number(), wireTypes[fd.Kind()])
|
|
} else {
|
|
wiretag = protowire.EncodeTag(fd.Number(), protowire.BytesType)
|
|
}
|
|
var fieldOffset offset
|
|
var funcs pointerCoderFuncs
|
|
var childMessage *MessageInfo
|
|
switch {
|
|
case isOneof:
|
|
fieldOffset = offsetOf(fs, mi.Exporter)
|
|
case fd.IsWeak():
|
|
fieldOffset = si.weakOffset
|
|
funcs = makeWeakMessageFieldCoder(fd)
|
|
default:
|
|
fieldOffset = offsetOf(fs, mi.Exporter)
|
|
childMessage, funcs = fieldCoder(fd, ft)
|
|
}
|
|
var cf *coderFieldInfo
|
|
if len(preallocFields) > 0 {
|
|
cf = &preallocFields[0]
|
|
preallocFields = preallocFields[1:]
|
|
} else {
|
|
cf = new(coderFieldInfo)
|
|
}
|
|
*cf = coderFieldInfo{
|
|
num: fd.Number(),
|
|
offset: fieldOffset,
|
|
wiretag: wiretag,
|
|
ft: ft,
|
|
tagsize: protowire.SizeVarint(wiretag),
|
|
funcs: funcs,
|
|
mi: childMessage,
|
|
validation: newFieldValidationInfo(mi, si, fd, ft),
|
|
isPointer: fd.Cardinality() == pref.Repeated || fd.HasPresence(),
|
|
isRequired: fd.Cardinality() == pref.Required,
|
|
}
|
|
mi.orderedCoderFields = append(mi.orderedCoderFields, cf)
|
|
mi.coderFields[cf.num] = cf
|
|
}
|
|
for i, oneofs := 0, mi.Desc.Oneofs(); i < oneofs.Len(); i++ {
|
|
if od := oneofs.Get(i); !od.IsSynthetic() {
|
|
mi.initOneofFieldCoders(od, si)
|
|
}
|
|
}
|
|
if messageset.IsMessageSet(mi.Desc) {
|
|
if !mi.extensionOffset.IsValid() {
|
|
panic(fmt.Sprintf("%v: MessageSet with no extensions field", mi.Desc.FullName()))
|
|
}
|
|
if !mi.unknownOffset.IsValid() {
|
|
panic(fmt.Sprintf("%v: MessageSet with no unknown field", mi.Desc.FullName()))
|
|
}
|
|
mi.isMessageSet = true
|
|
}
|
|
sort.Slice(mi.orderedCoderFields, func(i, j int) bool {
|
|
return mi.orderedCoderFields[i].num < mi.orderedCoderFields[j].num
|
|
})
|
|
|
|
var maxDense pref.FieldNumber
|
|
for _, cf := range mi.orderedCoderFields {
|
|
if cf.num >= 16 && cf.num >= 2*maxDense {
|
|
break
|
|
}
|
|
maxDense = cf.num
|
|
}
|
|
mi.denseCoderFields = make([]*coderFieldInfo, maxDense+1)
|
|
for _, cf := range mi.orderedCoderFields {
|
|
if int(cf.num) >= len(mi.denseCoderFields) {
|
|
break
|
|
}
|
|
mi.denseCoderFields[cf.num] = cf
|
|
}
|
|
|
|
// To preserve compatibility with historic wire output, marshal oneofs last.
|
|
if mi.Desc.Oneofs().Len() > 0 {
|
|
sort.Slice(mi.orderedCoderFields, func(i, j int) bool {
|
|
fi := fields.ByNumber(mi.orderedCoderFields[i].num)
|
|
fj := fields.ByNumber(mi.orderedCoderFields[j].num)
|
|
return fieldsort.Less(fi, fj)
|
|
})
|
|
}
|
|
|
|
mi.needsInitCheck = needsInitCheck(mi.Desc)
|
|
if mi.methods.Marshal == nil && mi.methods.Size == nil {
|
|
mi.methods.Flags |= piface.SupportMarshalDeterministic
|
|
mi.methods.Marshal = mi.marshal
|
|
mi.methods.Size = mi.size
|
|
}
|
|
if mi.methods.Unmarshal == nil {
|
|
mi.methods.Flags |= piface.SupportUnmarshalDiscardUnknown
|
|
mi.methods.Unmarshal = mi.unmarshal
|
|
}
|
|
if mi.methods.CheckInitialized == nil {
|
|
mi.methods.CheckInitialized = mi.checkInitialized
|
|
}
|
|
if mi.methods.Merge == nil {
|
|
mi.methods.Merge = mi.merge
|
|
}
|
|
}
|