blob: 3a23bb0de114d53b487bc8375a8870f21c9d3874 [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 Neil7492a092019-07-10 15:23:29 -070032 var wiretag uint64
33 if !xt.IsPacked() {
34 wiretag = wire.EncodeTag(xt.Number(), wireTypes[xt.Kind()])
35 } else {
36 wiretag = wire.EncodeTag(xt.Number(), wire.BytesType)
37 }
Damien Neilc37adef2019-04-01 13:49:56 -070038 e = &extensionFieldInfo{
39 wiretag: wiretag,
40 tagsize: wire.SizeVarint(wiretag),
Joe Tsai89d49632019-06-04 16:20:00 -070041 funcs: encoderFuncsForValue(xt, xt.GoType()),
Damien Neilc37adef2019-04-01 13:49:56 -070042 }
Damien Neile91877d2019-06-27 10:54:42 -070043 // Does the unmarshal function need a value passed to it?
44 // This is true for composite types, where we pass in a message, list, or map to fill in,
45 // and for enums, where we pass in a prototype value to specify the concrete enum type.
46 switch xt.Kind() {
47 case pref.MessageKind, pref.GroupKind, pref.EnumKind:
48 e.unmarshalNeedsValue = true
49 default:
50 if xt.Cardinality() == pref.Repeated {
51 e.unmarshalNeedsValue = true
52 }
53 }
Damien Neilc37adef2019-04-01 13:49:56 -070054 mi.extensionFieldInfosMu.Lock()
55 if mi.extensionFieldInfos == nil {
Joe Tsai89d49632019-06-04 16:20:00 -070056 mi.extensionFieldInfos = make(map[pref.ExtensionType]*extensionFieldInfo)
Damien Neilc37adef2019-04-01 13:49:56 -070057 }
Joe Tsai89d49632019-06-04 16:20:00 -070058 mi.extensionFieldInfos[xt] = e
Damien Neilc37adef2019-04-01 13:49:56 -070059 mi.extensionFieldInfosMu.Unlock()
60 return e
61}
Joe Tsai89d49632019-06-04 16:20:00 -070062
63type ExtensionField struct {
Joe Tsai05e11b82019-06-04 17:05:04 -070064 typ pref.ExtensionType
Joe Tsai89d49632019-06-04 16:20:00 -070065
66 // value is either the value of GetValue,
67 // or a *lazyExtensionValue that then returns the value of GetValue.
68 value interface{} // TODO: switch to protoreflect.Value
69}
70
71func (f ExtensionField) HasType() bool {
Joe Tsai05e11b82019-06-04 17:05:04 -070072 return f.typ != nil
Joe Tsai89d49632019-06-04 16:20:00 -070073}
74func (f ExtensionField) GetType() pref.ExtensionType {
Joe Tsai05e11b82019-06-04 17:05:04 -070075 return f.typ
Joe Tsai89d49632019-06-04 16:20:00 -070076}
77func (f *ExtensionField) SetType(t pref.ExtensionType) {
Joe Tsai05e11b82019-06-04 17:05:04 -070078 f.typ = t
Joe Tsai89d49632019-06-04 16:20:00 -070079}
80
81// HasValue reports whether a value is set for the extension field.
82// This may be called concurrently.
83func (f ExtensionField) HasValue() bool {
84 return f.value != nil
85}
86
87// GetValue returns the concrete value for the extension field.
88// Let the type of Desc.ExtensionType be the "API type" and
89// the type of GetValue be the "storage type".
90// The API type and storage type are the same except:
91// * for scalars (except []byte), where the API type uses *T,
92// while the storage type uses T.
93// * for repeated fields, where the API type uses []T,
94// while the storage type uses *[]T.
95//
96// The reason for the divergence is so that the storage type more naturally
97// matches what is expected of when retrieving the values through the
98// protobuf reflection APIs.
99//
100// GetValue is only populated if Desc is also populated.
101// This may be called concurrently.
102//
103// TODO: switch interface{} to protoreflect.Value
104func (f ExtensionField) GetValue() interface{} {
105 if f, ok := f.value.(*lazyExtensionValue); ok {
106 return f.GetValue()
107 }
108 return f.value
109}
110
111// SetEagerValue sets the current value of the extension.
112// This must not be called concurrently.
113func (f *ExtensionField) SetEagerValue(v interface{}) {
114 f.value = v
115}
116
117// SetLazyValue sets a value that is to be lazily evaluated upon first use.
118// The returned value must not be nil.
119// This must not be called concurrently.
120func (f *ExtensionField) SetLazyValue(v func() interface{}) {
121 f.value = &lazyExtensionValue{value: v}
122}
123
124type lazyExtensionValue struct {
125 once uint32 // atomically set if value is valid
126 mu sync.Mutex // protects value
127 value interface{} // either the value itself or a func() interface{}
128}
129
130func (v *lazyExtensionValue) GetValue() interface{} {
131 if atomic.LoadUint32(&v.once) == 0 {
132 v.mu.Lock()
133 if f, ok := v.value.(func() interface{}); ok {
134 v.value = f()
135 }
136 atomic.StoreUint32(&v.once, 1)
137 v.mu.Unlock()
138 }
139 return v.value
140}