mirror of https://github.com/docker/cli.git
218 lines
7.0 KiB
Go
218 lines
7.0 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/order"
|
||
|
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
|
||
|
unknownPtrKind bool
|
||
|
extensionOffset offset
|
||
|
needsInitCheck bool
|
||
|
isMessageSet bool
|
||
|
numRequiredFields uint8
|
||
|
}
|
||
|
|
||
|
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 = invalidOffset
|
||
|
mi.unknownOffset = invalidOffset
|
||
|
mi.extensionOffset = invalidOffset
|
||
|
|
||
|
if si.sizecacheOffset.IsValid() && si.sizecacheType == sizecacheType {
|
||
|
mi.sizecacheOffset = si.sizecacheOffset
|
||
|
}
|
||
|
if si.unknownOffset.IsValid() && (si.unknownType == unknownFieldsAType || si.unknownType == unknownFieldsBType) {
|
||
|
mi.unknownOffset = si.unknownOffset
|
||
|
mi.unknownPtrKind = si.unknownType.Kind() == reflect.Ptr
|
||
|
}
|
||
|
if si.extensionOffset.IsValid() && si.extensionType == extensionFieldsType {
|
||
|
mi.extensionOffset = si.extensionOffset
|
||
|
}
|
||
|
|
||
|
mi.coderFields = make(map[protowire.Number]*coderFieldInfo)
|
||
|
fields := mi.Desc.Fields()
|
||
|
preallocFields := make([]coderFieldInfo, fields.Len())
|
||
|
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 ft == nil:
|
||
|
// This never occurs for generated message types.
|
||
|
// It implies that a hand-crafted type has missing Go fields
|
||
|
// for specific protobuf message fields.
|
||
|
funcs = pointerCoderFuncs{
|
||
|
size: func(p pointer, f *coderFieldInfo, opts marshalOptions) int {
|
||
|
return 0
|
||
|
},
|
||
|
marshal: func(b []byte, p pointer, f *coderFieldInfo, opts marshalOptions) ([]byte, error) {
|
||
|
return nil, nil
|
||
|
},
|
||
|
unmarshal: func(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (unmarshalOutput, error) {
|
||
|
panic("missing Go struct field for " + string(fd.FullName()))
|
||
|
},
|
||
|
isInit: func(p pointer, f *coderFieldInfo) error {
|
||
|
panic("missing Go struct field for " + string(fd.FullName()))
|
||
|
},
|
||
|
merge: func(dst, src pointer, f *coderFieldInfo, opts mergeOptions) {
|
||
|
panic("missing Go struct field for " + string(fd.FullName()))
|
||
|
},
|
||
|
}
|
||
|
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)
|
||
|
}
|
||
|
cf := &preallocFields[i]
|
||
|
*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 order.LegacyFieldOrder(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
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// getUnknownBytes returns a *[]byte for the unknown fields.
|
||
|
// It is the caller's responsibility to check whether the pointer is nil.
|
||
|
// This function is specially designed to be inlineable.
|
||
|
func (mi *MessageInfo) getUnknownBytes(p pointer) *[]byte {
|
||
|
if mi.unknownPtrKind {
|
||
|
return *p.Apply(mi.unknownOffset).BytesPtr()
|
||
|
} else {
|
||
|
return p.Apply(mi.unknownOffset).Bytes()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// mutableUnknownBytes returns a *[]byte for the unknown fields.
|
||
|
// The returned pointer is guaranteed to not be nil.
|
||
|
func (mi *MessageInfo) mutableUnknownBytes(p pointer) *[]byte {
|
||
|
if mi.unknownPtrKind {
|
||
|
bp := p.Apply(mi.unknownOffset).BytesPtr()
|
||
|
if *bp == nil {
|
||
|
*bp = new([]byte)
|
||
|
}
|
||
|
return *bp
|
||
|
} else {
|
||
|
return p.Apply(mi.unknownOffset).Bytes()
|
||
|
}
|
||
|
}
|