blob: 8a999ef776083e35caee2dabdf9b6188cf077347 [file] [log] [blame]
Damien Neilc37adef2019-04-01 13:49:56 -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 (
Joe Tsai89d49632019-06-04 16:20:00 -07008 "sync"
9 "sync/atomic"
10
Damien Neilc37adef2019-04-01 13:49:56 -070011 "google.golang.org/protobuf/internal/encoding/wire"
Joe Tsai89d49632019-06-04 16:20:00 -070012 pref "google.golang.org/protobuf/reflect/protoreflect"
Damien Neilc37adef2019-04-01 13:49:56 -070013)
14
15type extensionFieldInfo struct {
Damien Neile91877d2019-06-27 10:54:42 -070016 wiretag uint64
17 tagsize int
18 unmarshalNeedsValue bool
19 funcs ifaceCoderFuncs
Damien Neilc37adef2019-04-01 13:49:56 -070020}
21
Joe Tsai89d49632019-06-04 16:20:00 -070022func (mi *MessageInfo) extensionFieldInfo(xt pref.ExtensionType) *extensionFieldInfo {
Damien Neilc37adef2019-04-01 13:49:56 -070023 // As of this time (Go 1.12, linux/amd64), an RWMutex benchmarks as faster
24 // than a sync.Map.
25 mi.extensionFieldInfosMu.RLock()
Joe Tsai89d49632019-06-04 16:20:00 -070026 e, ok := mi.extensionFieldInfos[xt]
Damien Neilc37adef2019-04-01 13:49:56 -070027 mi.extensionFieldInfosMu.RUnlock()
28 if ok {
29 return e
30 }
31
Damien Neil79bfdbe2019-08-28 11:08:22 -070032 xd := xt.TypeDescriptor()
Damien Neil7492a092019-07-10 15:23:29 -070033 var wiretag uint64
Damien Neil92f76182019-08-02 16:58:08 -070034 if !xd.IsPacked() {
35 wiretag = wire.EncodeTag(xd.Number(), wireTypes[xd.Kind()])
Damien Neil7492a092019-07-10 15:23:29 -070036 } else {
Damien Neil92f76182019-08-02 16:58:08 -070037 wiretag = wire.EncodeTag(xd.Number(), wire.BytesType)
Damien Neil7492a092019-07-10 15:23:29 -070038 }
Damien Neilc37adef2019-04-01 13:49:56 -070039 e = &extensionFieldInfo{
40 wiretag: wiretag,
41 tagsize: wire.SizeVarint(wiretag),
Damien Neil92f76182019-08-02 16:58:08 -070042 funcs: encoderFuncsForValue(xd, xt.GoType()),
Damien Neilc37adef2019-04-01 13:49:56 -070043 }
Damien Neile91877d2019-06-27 10:54:42 -070044 // Does the unmarshal function need a value passed to it?
45 // This is true for composite types, where we pass in a message, list, or map to fill in,
46 // and for enums, where we pass in a prototype value to specify the concrete enum type.
Damien Neil92f76182019-08-02 16:58:08 -070047 switch xd.Kind() {
Damien Neile91877d2019-06-27 10:54:42 -070048 case pref.MessageKind, pref.GroupKind, pref.EnumKind:
49 e.unmarshalNeedsValue = true
50 default:
Damien Neil92f76182019-08-02 16:58:08 -070051 if xd.Cardinality() == pref.Repeated {
Damien Neile91877d2019-06-27 10:54:42 -070052 e.unmarshalNeedsValue = true
53 }
54 }
Damien Neilc37adef2019-04-01 13:49:56 -070055 mi.extensionFieldInfosMu.Lock()
56 if mi.extensionFieldInfos == nil {
Joe Tsai89d49632019-06-04 16:20:00 -070057 mi.extensionFieldInfos = make(map[pref.ExtensionType]*extensionFieldInfo)
Damien Neilc37adef2019-04-01 13:49:56 -070058 }
Joe Tsai89d49632019-06-04 16:20:00 -070059 mi.extensionFieldInfos[xt] = e
Damien Neilc37adef2019-04-01 13:49:56 -070060 mi.extensionFieldInfosMu.Unlock()
61 return e
62}
Joe Tsai89d49632019-06-04 16:20:00 -070063
64type ExtensionField struct {
Joe Tsai05e11b82019-06-04 17:05:04 -070065 typ pref.ExtensionType
Joe Tsai89d49632019-06-04 16:20:00 -070066
67 // value is either the value of GetValue,
68 // or a *lazyExtensionValue that then returns the value of GetValue.
69 value interface{} // TODO: switch to protoreflect.Value
70}
71
72func (f ExtensionField) HasType() bool {
Joe Tsai05e11b82019-06-04 17:05:04 -070073 return f.typ != nil
Joe Tsai89d49632019-06-04 16:20:00 -070074}
75func (f ExtensionField) GetType() pref.ExtensionType {
Joe Tsai05e11b82019-06-04 17:05:04 -070076 return f.typ
Joe Tsai89d49632019-06-04 16:20:00 -070077}
78func (f *ExtensionField) SetType(t pref.ExtensionType) {
Joe Tsai05e11b82019-06-04 17:05:04 -070079 f.typ = t
Joe Tsai89d49632019-06-04 16:20:00 -070080}
81
82// HasValue reports whether a value is set for the extension field.
83// This may be called concurrently.
84func (f ExtensionField) HasValue() bool {
85 return f.value != nil
86}
87
88// GetValue returns the concrete value for the extension field.
89// Let the type of Desc.ExtensionType be the "API type" and
90// the type of GetValue be the "storage type".
91// The API type and storage type are the same except:
92// * for scalars (except []byte), where the API type uses *T,
93// while the storage type uses T.
94// * for repeated fields, where the API type uses []T,
95// while the storage type uses *[]T.
96//
97// The reason for the divergence is so that the storage type more naturally
98// matches what is expected of when retrieving the values through the
99// protobuf reflection APIs.
100//
101// GetValue is only populated if Desc is also populated.
102// This may be called concurrently.
103//
104// TODO: switch interface{} to protoreflect.Value
105func (f ExtensionField) GetValue() interface{} {
106 if f, ok := f.value.(*lazyExtensionValue); ok {
107 return f.GetValue()
108 }
109 return f.value
110}
111
112// SetEagerValue sets the current value of the extension.
113// This must not be called concurrently.
114func (f *ExtensionField) SetEagerValue(v interface{}) {
115 f.value = v
116}
117
118// SetLazyValue sets a value that is to be lazily evaluated upon first use.
119// The returned value must not be nil.
120// This must not be called concurrently.
121func (f *ExtensionField) SetLazyValue(v func() interface{}) {
122 f.value = &lazyExtensionValue{value: v}
123}
124
125type lazyExtensionValue struct {
126 once uint32 // atomically set if value is valid
127 mu sync.Mutex // protects value
128 value interface{} // either the value itself or a func() interface{}
129}
130
131func (v *lazyExtensionValue) GetValue() interface{} {
132 if atomic.LoadUint32(&v.once) == 0 {
133 v.mu.Lock()
134 if f, ok := v.value.(func() interface{}); ok {
135 v.value = f()
136 }
137 atomic.StoreUint32(&v.once, 1)
138 v.mu.Unlock()
139 }
140 return v.value
141}