xref: /aosp_15_r20/external/golang-protobuf/internal/impl/codec_extension.go (revision 1c12ee1efe575feb122dbf939ff15148a3b3e8f2)
1*1c12ee1eSDan Willemsen// Copyright 2019 The Go Authors. All rights reserved.
2*1c12ee1eSDan Willemsen// Use of this source code is governed by a BSD-style
3*1c12ee1eSDan Willemsen// license that can be found in the LICENSE file.
4*1c12ee1eSDan Willemsen
5*1c12ee1eSDan Willemsenpackage impl
6*1c12ee1eSDan Willemsen
7*1c12ee1eSDan Willemsenimport (
8*1c12ee1eSDan Willemsen	"sync"
9*1c12ee1eSDan Willemsen	"sync/atomic"
10*1c12ee1eSDan Willemsen
11*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/encoding/protowire"
12*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/internal/errors"
13*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/reflect/protoreflect"
14*1c12ee1eSDan Willemsen)
15*1c12ee1eSDan Willemsen
16*1c12ee1eSDan Willemsentype extensionFieldInfo struct {
17*1c12ee1eSDan Willemsen	wiretag             uint64
18*1c12ee1eSDan Willemsen	tagsize             int
19*1c12ee1eSDan Willemsen	unmarshalNeedsValue bool
20*1c12ee1eSDan Willemsen	funcs               valueCoderFuncs
21*1c12ee1eSDan Willemsen	validation          validationInfo
22*1c12ee1eSDan Willemsen}
23*1c12ee1eSDan Willemsen
24*1c12ee1eSDan Willemsenvar legacyExtensionFieldInfoCache sync.Map // map[protoreflect.ExtensionType]*extensionFieldInfo
25*1c12ee1eSDan Willemsen
26*1c12ee1eSDan Willemsenfunc getExtensionFieldInfo(xt protoreflect.ExtensionType) *extensionFieldInfo {
27*1c12ee1eSDan Willemsen	if xi, ok := xt.(*ExtensionInfo); ok {
28*1c12ee1eSDan Willemsen		xi.lazyInit()
29*1c12ee1eSDan Willemsen		return xi.info
30*1c12ee1eSDan Willemsen	}
31*1c12ee1eSDan Willemsen	return legacyLoadExtensionFieldInfo(xt)
32*1c12ee1eSDan Willemsen}
33*1c12ee1eSDan Willemsen
34*1c12ee1eSDan Willemsen// legacyLoadExtensionFieldInfo dynamically loads a *ExtensionInfo for xt.
35*1c12ee1eSDan Willemsenfunc legacyLoadExtensionFieldInfo(xt protoreflect.ExtensionType) *extensionFieldInfo {
36*1c12ee1eSDan Willemsen	if xi, ok := legacyExtensionFieldInfoCache.Load(xt); ok {
37*1c12ee1eSDan Willemsen		return xi.(*extensionFieldInfo)
38*1c12ee1eSDan Willemsen	}
39*1c12ee1eSDan Willemsen	e := makeExtensionFieldInfo(xt.TypeDescriptor())
40*1c12ee1eSDan Willemsen	if e, ok := legacyMessageTypeCache.LoadOrStore(xt, e); ok {
41*1c12ee1eSDan Willemsen		return e.(*extensionFieldInfo)
42*1c12ee1eSDan Willemsen	}
43*1c12ee1eSDan Willemsen	return e
44*1c12ee1eSDan Willemsen}
45*1c12ee1eSDan Willemsen
46*1c12ee1eSDan Willemsenfunc makeExtensionFieldInfo(xd protoreflect.ExtensionDescriptor) *extensionFieldInfo {
47*1c12ee1eSDan Willemsen	var wiretag uint64
48*1c12ee1eSDan Willemsen	if !xd.IsPacked() {
49*1c12ee1eSDan Willemsen		wiretag = protowire.EncodeTag(xd.Number(), wireTypes[xd.Kind()])
50*1c12ee1eSDan Willemsen	} else {
51*1c12ee1eSDan Willemsen		wiretag = protowire.EncodeTag(xd.Number(), protowire.BytesType)
52*1c12ee1eSDan Willemsen	}
53*1c12ee1eSDan Willemsen	e := &extensionFieldInfo{
54*1c12ee1eSDan Willemsen		wiretag: wiretag,
55*1c12ee1eSDan Willemsen		tagsize: protowire.SizeVarint(wiretag),
56*1c12ee1eSDan Willemsen		funcs:   encoderFuncsForValue(xd),
57*1c12ee1eSDan Willemsen	}
58*1c12ee1eSDan Willemsen	// Does the unmarshal function need a value passed to it?
59*1c12ee1eSDan Willemsen	// This is true for composite types, where we pass in a message, list, or map to fill in,
60*1c12ee1eSDan Willemsen	// and for enums, where we pass in a prototype value to specify the concrete enum type.
61*1c12ee1eSDan Willemsen	switch xd.Kind() {
62*1c12ee1eSDan Willemsen	case protoreflect.MessageKind, protoreflect.GroupKind, protoreflect.EnumKind:
63*1c12ee1eSDan Willemsen		e.unmarshalNeedsValue = true
64*1c12ee1eSDan Willemsen	default:
65*1c12ee1eSDan Willemsen		if xd.Cardinality() == protoreflect.Repeated {
66*1c12ee1eSDan Willemsen			e.unmarshalNeedsValue = true
67*1c12ee1eSDan Willemsen		}
68*1c12ee1eSDan Willemsen	}
69*1c12ee1eSDan Willemsen	return e
70*1c12ee1eSDan Willemsen}
71*1c12ee1eSDan Willemsen
72*1c12ee1eSDan Willemsentype lazyExtensionValue struct {
73*1c12ee1eSDan Willemsen	atomicOnce uint32 // atomically set if value is valid
74*1c12ee1eSDan Willemsen	mu         sync.Mutex
75*1c12ee1eSDan Willemsen	xi         *extensionFieldInfo
76*1c12ee1eSDan Willemsen	value      protoreflect.Value
77*1c12ee1eSDan Willemsen	b          []byte
78*1c12ee1eSDan Willemsen	fn         func() protoreflect.Value
79*1c12ee1eSDan Willemsen}
80*1c12ee1eSDan Willemsen
81*1c12ee1eSDan Willemsentype ExtensionField struct {
82*1c12ee1eSDan Willemsen	typ protoreflect.ExtensionType
83*1c12ee1eSDan Willemsen
84*1c12ee1eSDan Willemsen	// value is either the value of GetValue,
85*1c12ee1eSDan Willemsen	// or a *lazyExtensionValue that then returns the value of GetValue.
86*1c12ee1eSDan Willemsen	value protoreflect.Value
87*1c12ee1eSDan Willemsen	lazy  *lazyExtensionValue
88*1c12ee1eSDan Willemsen}
89*1c12ee1eSDan Willemsen
90*1c12ee1eSDan Willemsenfunc (f *ExtensionField) appendLazyBytes(xt protoreflect.ExtensionType, xi *extensionFieldInfo, num protowire.Number, wtyp protowire.Type, b []byte) {
91*1c12ee1eSDan Willemsen	if f.lazy == nil {
92*1c12ee1eSDan Willemsen		f.lazy = &lazyExtensionValue{xi: xi}
93*1c12ee1eSDan Willemsen	}
94*1c12ee1eSDan Willemsen	f.typ = xt
95*1c12ee1eSDan Willemsen	f.lazy.xi = xi
96*1c12ee1eSDan Willemsen	f.lazy.b = protowire.AppendTag(f.lazy.b, num, wtyp)
97*1c12ee1eSDan Willemsen	f.lazy.b = append(f.lazy.b, b...)
98*1c12ee1eSDan Willemsen}
99*1c12ee1eSDan Willemsen
100*1c12ee1eSDan Willemsenfunc (f *ExtensionField) canLazy(xt protoreflect.ExtensionType) bool {
101*1c12ee1eSDan Willemsen	if f.typ == nil {
102*1c12ee1eSDan Willemsen		return true
103*1c12ee1eSDan Willemsen	}
104*1c12ee1eSDan Willemsen	if f.typ == xt && f.lazy != nil && atomic.LoadUint32(&f.lazy.atomicOnce) == 0 {
105*1c12ee1eSDan Willemsen		return true
106*1c12ee1eSDan Willemsen	}
107*1c12ee1eSDan Willemsen	return false
108*1c12ee1eSDan Willemsen}
109*1c12ee1eSDan Willemsen
110*1c12ee1eSDan Willemsenfunc (f *ExtensionField) lazyInit() {
111*1c12ee1eSDan Willemsen	f.lazy.mu.Lock()
112*1c12ee1eSDan Willemsen	defer f.lazy.mu.Unlock()
113*1c12ee1eSDan Willemsen	if atomic.LoadUint32(&f.lazy.atomicOnce) == 1 {
114*1c12ee1eSDan Willemsen		return
115*1c12ee1eSDan Willemsen	}
116*1c12ee1eSDan Willemsen	if f.lazy.xi != nil {
117*1c12ee1eSDan Willemsen		b := f.lazy.b
118*1c12ee1eSDan Willemsen		val := f.typ.New()
119*1c12ee1eSDan Willemsen		for len(b) > 0 {
120*1c12ee1eSDan Willemsen			var tag uint64
121*1c12ee1eSDan Willemsen			if b[0] < 0x80 {
122*1c12ee1eSDan Willemsen				tag = uint64(b[0])
123*1c12ee1eSDan Willemsen				b = b[1:]
124*1c12ee1eSDan Willemsen			} else if len(b) >= 2 && b[1] < 128 {
125*1c12ee1eSDan Willemsen				tag = uint64(b[0]&0x7f) + uint64(b[1])<<7
126*1c12ee1eSDan Willemsen				b = b[2:]
127*1c12ee1eSDan Willemsen			} else {
128*1c12ee1eSDan Willemsen				var n int
129*1c12ee1eSDan Willemsen				tag, n = protowire.ConsumeVarint(b)
130*1c12ee1eSDan Willemsen				if n < 0 {
131*1c12ee1eSDan Willemsen					panic(errors.New("bad tag in lazy extension decoding"))
132*1c12ee1eSDan Willemsen				}
133*1c12ee1eSDan Willemsen				b = b[n:]
134*1c12ee1eSDan Willemsen			}
135*1c12ee1eSDan Willemsen			num := protowire.Number(tag >> 3)
136*1c12ee1eSDan Willemsen			wtyp := protowire.Type(tag & 7)
137*1c12ee1eSDan Willemsen			var out unmarshalOutput
138*1c12ee1eSDan Willemsen			var err error
139*1c12ee1eSDan Willemsen			val, out, err = f.lazy.xi.funcs.unmarshal(b, val, num, wtyp, lazyUnmarshalOptions)
140*1c12ee1eSDan Willemsen			if err != nil {
141*1c12ee1eSDan Willemsen				panic(errors.New("decode failure in lazy extension decoding: %v", err))
142*1c12ee1eSDan Willemsen			}
143*1c12ee1eSDan Willemsen			b = b[out.n:]
144*1c12ee1eSDan Willemsen		}
145*1c12ee1eSDan Willemsen		f.lazy.value = val
146*1c12ee1eSDan Willemsen	} else {
147*1c12ee1eSDan Willemsen		f.lazy.value = f.lazy.fn()
148*1c12ee1eSDan Willemsen	}
149*1c12ee1eSDan Willemsen	f.lazy.xi = nil
150*1c12ee1eSDan Willemsen	f.lazy.fn = nil
151*1c12ee1eSDan Willemsen	f.lazy.b = nil
152*1c12ee1eSDan Willemsen	atomic.StoreUint32(&f.lazy.atomicOnce, 1)
153*1c12ee1eSDan Willemsen}
154*1c12ee1eSDan Willemsen
155*1c12ee1eSDan Willemsen// Set sets the type and value of the extension field.
156*1c12ee1eSDan Willemsen// This must not be called concurrently.
157*1c12ee1eSDan Willemsenfunc (f *ExtensionField) Set(t protoreflect.ExtensionType, v protoreflect.Value) {
158*1c12ee1eSDan Willemsen	f.typ = t
159*1c12ee1eSDan Willemsen	f.value = v
160*1c12ee1eSDan Willemsen	f.lazy = nil
161*1c12ee1eSDan Willemsen}
162*1c12ee1eSDan Willemsen
163*1c12ee1eSDan Willemsen// SetLazy sets the type and a value that is to be lazily evaluated upon first use.
164*1c12ee1eSDan Willemsen// This must not be called concurrently.
165*1c12ee1eSDan Willemsenfunc (f *ExtensionField) SetLazy(t protoreflect.ExtensionType, fn func() protoreflect.Value) {
166*1c12ee1eSDan Willemsen	f.typ = t
167*1c12ee1eSDan Willemsen	f.lazy = &lazyExtensionValue{fn: fn}
168*1c12ee1eSDan Willemsen}
169*1c12ee1eSDan Willemsen
170*1c12ee1eSDan Willemsen// Value returns the value of the extension field.
171*1c12ee1eSDan Willemsen// This may be called concurrently.
172*1c12ee1eSDan Willemsenfunc (f *ExtensionField) Value() protoreflect.Value {
173*1c12ee1eSDan Willemsen	if f.lazy != nil {
174*1c12ee1eSDan Willemsen		if atomic.LoadUint32(&f.lazy.atomicOnce) == 0 {
175*1c12ee1eSDan Willemsen			f.lazyInit()
176*1c12ee1eSDan Willemsen		}
177*1c12ee1eSDan Willemsen		return f.lazy.value
178*1c12ee1eSDan Willemsen	}
179*1c12ee1eSDan Willemsen	return f.value
180*1c12ee1eSDan Willemsen}
181*1c12ee1eSDan Willemsen
182*1c12ee1eSDan Willemsen// Type returns the type of the extension field.
183*1c12ee1eSDan Willemsen// This may be called concurrently.
184*1c12ee1eSDan Willemsenfunc (f ExtensionField) Type() protoreflect.ExtensionType {
185*1c12ee1eSDan Willemsen	return f.typ
186*1c12ee1eSDan Willemsen}
187*1c12ee1eSDan Willemsen
188*1c12ee1eSDan Willemsen// IsSet returns whether the extension field is set.
189*1c12ee1eSDan Willemsen// This may be called concurrently.
190*1c12ee1eSDan Willemsenfunc (f ExtensionField) IsSet() bool {
191*1c12ee1eSDan Willemsen	return f.typ != nil
192*1c12ee1eSDan Willemsen}
193*1c12ee1eSDan Willemsen
194*1c12ee1eSDan Willemsen// IsLazy reports whether a field is lazily encoded.
195*1c12ee1eSDan Willemsen// It is exported for testing.
196*1c12ee1eSDan Willemsenfunc IsLazy(m protoreflect.Message, fd protoreflect.FieldDescriptor) bool {
197*1c12ee1eSDan Willemsen	var mi *MessageInfo
198*1c12ee1eSDan Willemsen	var p pointer
199*1c12ee1eSDan Willemsen	switch m := m.(type) {
200*1c12ee1eSDan Willemsen	case *messageState:
201*1c12ee1eSDan Willemsen		mi = m.messageInfo()
202*1c12ee1eSDan Willemsen		p = m.pointer()
203*1c12ee1eSDan Willemsen	case *messageReflectWrapper:
204*1c12ee1eSDan Willemsen		mi = m.messageInfo()
205*1c12ee1eSDan Willemsen		p = m.pointer()
206*1c12ee1eSDan Willemsen	default:
207*1c12ee1eSDan Willemsen		return false
208*1c12ee1eSDan Willemsen	}
209*1c12ee1eSDan Willemsen	xd, ok := fd.(protoreflect.ExtensionTypeDescriptor)
210*1c12ee1eSDan Willemsen	if !ok {
211*1c12ee1eSDan Willemsen		return false
212*1c12ee1eSDan Willemsen	}
213*1c12ee1eSDan Willemsen	xt := xd.Type()
214*1c12ee1eSDan Willemsen	ext := mi.extensionMap(p)
215*1c12ee1eSDan Willemsen	if ext == nil {
216*1c12ee1eSDan Willemsen		return false
217*1c12ee1eSDan Willemsen	}
218*1c12ee1eSDan Willemsen	f, ok := (*ext)[int32(fd.Number())]
219*1c12ee1eSDan Willemsen	if !ok {
220*1c12ee1eSDan Willemsen		return false
221*1c12ee1eSDan Willemsen	}
222*1c12ee1eSDan Willemsen	return f.typ == xt && f.lazy != nil && atomic.LoadUint32(&f.lazy.atomicOnce) == 0
223*1c12ee1eSDan Willemsen}
224