blob: ab1d4ec58c526d534d2f3871535ace96ae76f365 [file] [log] [blame]
Damien Neil4ae30bb2019-06-20 10:12:23 -07001// Copyright 2019 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package impl
6
7import (
Damien Neil302cb322019-06-19 15:22:13 -07008 "fmt"
Damien Neil4ae30bb2019-06-20 10:12:23 -07009 "reflect"
10 "sort"
Joe Tsai0484b1a2019-08-13 15:36:08 -070011 "sync"
Damien Neil4ae30bb2019-06-20 10:12:23 -070012
Damien Neil302cb322019-06-19 15:22:13 -070013 "google.golang.org/protobuf/internal/encoding/messageset"
Damien Neil4ae30bb2019-06-20 10:12:23 -070014 "google.golang.org/protobuf/internal/encoding/wire"
15 pref "google.golang.org/protobuf/reflect/protoreflect"
16 piface "google.golang.org/protobuf/runtime/protoiface"
17)
18
19// coderMessageInfo contains per-message information used by the fast-path functions.
20// This is a different type from MessageInfo to keep MessageInfo as general-purpose as
21// possible.
22type coderMessageInfo struct {
Joe Tsai0484b1a2019-08-13 15:36:08 -070023 methods piface.Methods
24
Damien Neil4ae30bb2019-06-20 10:12:23 -070025 orderedCoderFields []*coderFieldInfo
Damien Neile91877d2019-06-27 10:54:42 -070026 denseCoderFields []*coderFieldInfo
27 coderFields map[wire.Number]*coderFieldInfo
Damien Neil4ae30bb2019-06-20 10:12:23 -070028 sizecacheOffset offset
Damien Neil4ae30bb2019-06-20 10:12:23 -070029 unknownOffset offset
Joe Tsaic0e4bb22019-07-06 13:05:11 -070030 extensionOffset offset
Damien Neil4ae30bb2019-06-20 10:12:23 -070031 needsInitCheck bool
Damien Neilce3384c2019-11-06 13:18:28 -080032 isMessageSet bool
Joe Tsai0484b1a2019-08-13 15:36:08 -070033
34 extensionFieldInfosMu sync.RWMutex
35 extensionFieldInfos map[pref.ExtensionType]*extensionFieldInfo
Damien Neil4ae30bb2019-06-20 10:12:23 -070036}
37
38type coderFieldInfo struct {
39 funcs pointerCoderFuncs // fast-path per-field functions
40 num pref.FieldNumber // field number
41 offset offset // struct field offset
42 wiretag uint64 // field tag (number + wire type)
43 tagsize int // size of the varint-encoded tag
44 isPointer bool // true if IsNil may be called on the struct field
45 isRequired bool // true if field is required
46}
47
Joe Tsai0484b1a2019-08-13 15:36:08 -070048func (mi *MessageInfo) makeCoderMethods(t reflect.Type, si structInfo) {
Joe Tsai93fd9682019-07-06 13:02:14 -070049 mi.sizecacheOffset = si.sizecacheOffset
50 mi.unknownOffset = si.unknownOffset
51 mi.extensionOffset = si.extensionOffset
Damien Neil4ae30bb2019-06-20 10:12:23 -070052
Damien Neile91877d2019-06-27 10:54:42 -070053 mi.coderFields = make(map[wire.Number]*coderFieldInfo)
Damien Neil16163b42019-08-06 15:43:25 -070054 fields := mi.Desc.Fields()
Damien Neil92f76182019-08-02 16:58:08 -070055 for i := 0; i < fields.Len(); i++ {
56 fd := fields.Get(i)
Damien Neil4ae30bb2019-06-20 10:12:23 -070057
58 fs := si.fieldsByNumber[fd.Number()]
Damien Neile91877d2019-06-27 10:54:42 -070059 if fd.ContainingOneof() != nil {
60 fs = si.oneofsByName[fd.ContainingOneof().Name()]
61 }
Damien Neil4ae30bb2019-06-20 10:12:23 -070062 ft := fs.Type
63 var wiretag uint64
64 if !fd.IsPacked() {
65 wiretag = wire.EncodeTag(fd.Number(), wireTypes[fd.Kind()])
66 } else {
67 wiretag = wire.EncodeTag(fd.Number(), wire.BytesType)
68 }
Joe Tsai6e095992019-08-10 13:56:36 -070069 var fieldOffset offset
Damien Neile91877d2019-06-27 10:54:42 -070070 var funcs pointerCoderFuncs
Joe Tsai6e095992019-08-10 13:56:36 -070071 switch {
72 case fd.ContainingOneof() != nil:
73 fieldOffset = offsetOf(fs, mi.Exporter)
74 funcs = makeOneofFieldCoder(fd, si)
75 case fd.IsWeak():
76 fieldOffset = si.weakOffset
77 funcs = makeWeakMessageFieldCoder(fd)
78 default:
79 fieldOffset = offsetOf(fs, mi.Exporter)
Damien Neile91877d2019-06-27 10:54:42 -070080 funcs = fieldCoder(fd, ft)
81 }
82 cf := &coderFieldInfo{
Damien Neil4ae30bb2019-06-20 10:12:23 -070083 num: fd.Number(),
Joe Tsai6e095992019-08-10 13:56:36 -070084 offset: fieldOffset,
Damien Neil4ae30bb2019-06-20 10:12:23 -070085 wiretag: wiretag,
86 tagsize: wire.SizeVarint(wiretag),
Damien Neile91877d2019-06-27 10:54:42 -070087 funcs: funcs,
Damien Neil4ae30bb2019-06-20 10:12:23 -070088 isPointer: (fd.Cardinality() == pref.Repeated ||
89 fd.Kind() == pref.MessageKind ||
90 fd.Kind() == pref.GroupKind ||
91 fd.Syntax() != pref.Proto3),
92 isRequired: fd.Cardinality() == pref.Required,
Damien Neile91877d2019-06-27 10:54:42 -070093 }
94 mi.orderedCoderFields = append(mi.orderedCoderFields, cf)
95 mi.coderFields[cf.num] = cf
Damien Neil4ae30bb2019-06-20 10:12:23 -070096 }
Damien Neil16163b42019-08-06 15:43:25 -070097 if messageset.IsMessageSet(mi.Desc) {
Damien Neil302cb322019-06-19 15:22:13 -070098 if !mi.extensionOffset.IsValid() {
Damien Neil16163b42019-08-06 15:43:25 -070099 panic(fmt.Sprintf("%v: MessageSet with no extensions field", mi.Desc.FullName()))
Damien Neil302cb322019-06-19 15:22:13 -0700100 }
Damien Neilce3384c2019-11-06 13:18:28 -0800101 if !mi.unknownOffset.IsValid() {
102 panic(fmt.Sprintf("%v: MessageSet with no unknown field", mi.Desc.FullName()))
Damien Neil302cb322019-06-19 15:22:13 -0700103 }
Damien Neilce3384c2019-11-06 13:18:28 -0800104 mi.isMessageSet = true
Damien Neil302cb322019-06-19 15:22:13 -0700105 }
Damien Neil4ae30bb2019-06-20 10:12:23 -0700106 sort.Slice(mi.orderedCoderFields, func(i, j int) bool {
107 return mi.orderedCoderFields[i].num < mi.orderedCoderFields[j].num
108 })
109
Damien Neile91877d2019-06-27 10:54:42 -0700110 var maxDense pref.FieldNumber
111 for _, cf := range mi.orderedCoderFields {
112 if cf.num >= 16 && cf.num >= 2*maxDense {
113 break
114 }
115 maxDense = cf.num
116 }
117 mi.denseCoderFields = make([]*coderFieldInfo, maxDense+1)
118 for _, cf := range mi.orderedCoderFields {
119 if int(cf.num) > len(mi.denseCoderFields) {
120 break
121 }
122 mi.denseCoderFields[cf.num] = cf
123 }
124
Damien Neil16163b42019-08-06 15:43:25 -0700125 mi.needsInitCheck = needsInitCheck(mi.Desc)
Damien Neil37ef6912019-09-25 16:51:15 -0700126 if mi.methods.MarshalAppend == nil && mi.methods.Size == nil {
127 mi.methods.Flags |= piface.SupportMarshalDeterministic
128 mi.methods.MarshalAppend = mi.marshalAppend
129 mi.methods.Size = mi.size
130 }
131 if mi.methods.Unmarshal == nil {
132 mi.methods.Flags |= piface.SupportUnmarshalDiscardUnknown
133 mi.methods.Unmarshal = mi.unmarshal
134 }
135 if mi.methods.IsInitialized == nil {
136 mi.methods.IsInitialized = mi.isInitialized
Damien Neil4ae30bb2019-06-20 10:12:23 -0700137 }
138}