blob: 577f1df2053a2ec4b451b0ad350b4a9899ec50bb [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 {
16 wiretag uint64
17 tagsize int
18 funcs ifaceCoderFuncs
19}
20
Joe Tsai89d49632019-06-04 16:20:00 -070021func (mi *MessageInfo) extensionFieldInfo(xt pref.ExtensionType) *extensionFieldInfo {
Damien Neilc37adef2019-04-01 13:49:56 -070022 // As of this time (Go 1.12, linux/amd64), an RWMutex benchmarks as faster
23 // than a sync.Map.
24 mi.extensionFieldInfosMu.RLock()
Joe Tsai89d49632019-06-04 16:20:00 -070025 e, ok := mi.extensionFieldInfos[xt]
Damien Neilc37adef2019-04-01 13:49:56 -070026 mi.extensionFieldInfosMu.RUnlock()
27 if ok {
28 return e
29 }
30
Joe Tsai89d49632019-06-04 16:20:00 -070031 wiretag := wire.EncodeTag(xt.Number(), wireTypes[xt.Kind()])
Damien Neilc37adef2019-04-01 13:49:56 -070032 e = &extensionFieldInfo{
33 wiretag: wiretag,
34 tagsize: wire.SizeVarint(wiretag),
Joe Tsai89d49632019-06-04 16:20:00 -070035 funcs: encoderFuncsForValue(xt, xt.GoType()),
Damien Neilc37adef2019-04-01 13:49:56 -070036 }
37
38 mi.extensionFieldInfosMu.Lock()
39 if mi.extensionFieldInfos == nil {
Joe Tsai89d49632019-06-04 16:20:00 -070040 mi.extensionFieldInfos = make(map[pref.ExtensionType]*extensionFieldInfo)
Damien Neilc37adef2019-04-01 13:49:56 -070041 }
Joe Tsai89d49632019-06-04 16:20:00 -070042 mi.extensionFieldInfos[xt] = e
Damien Neilc37adef2019-04-01 13:49:56 -070043 mi.extensionFieldInfosMu.Unlock()
44 return e
45}
Joe Tsai89d49632019-06-04 16:20:00 -070046
47type ExtensionField struct {
Joe Tsai05e11b82019-06-04 17:05:04 -070048 typ pref.ExtensionType
Joe Tsai89d49632019-06-04 16:20:00 -070049
50 // value is either the value of GetValue,
51 // or a *lazyExtensionValue that then returns the value of GetValue.
52 value interface{} // TODO: switch to protoreflect.Value
53}
54
55func (f ExtensionField) HasType() bool {
Joe Tsai05e11b82019-06-04 17:05:04 -070056 return f.typ != nil
Joe Tsai89d49632019-06-04 16:20:00 -070057}
58func (f ExtensionField) GetType() pref.ExtensionType {
Joe Tsai05e11b82019-06-04 17:05:04 -070059 return f.typ
Joe Tsai89d49632019-06-04 16:20:00 -070060}
61func (f *ExtensionField) SetType(t pref.ExtensionType) {
Joe Tsai05e11b82019-06-04 17:05:04 -070062 f.typ = t
Joe Tsai89d49632019-06-04 16:20:00 -070063}
64
65// HasValue reports whether a value is set for the extension field.
66// This may be called concurrently.
67func (f ExtensionField) HasValue() bool {
68 return f.value != nil
69}
70
71// GetValue returns the concrete value for the extension field.
72// Let the type of Desc.ExtensionType be the "API type" and
73// the type of GetValue be the "storage type".
74// The API type and storage type are the same except:
75// * for scalars (except []byte), where the API type uses *T,
76// while the storage type uses T.
77// * for repeated fields, where the API type uses []T,
78// while the storage type uses *[]T.
79//
80// The reason for the divergence is so that the storage type more naturally
81// matches what is expected of when retrieving the values through the
82// protobuf reflection APIs.
83//
84// GetValue is only populated if Desc is also populated.
85// This may be called concurrently.
86//
87// TODO: switch interface{} to protoreflect.Value
88func (f ExtensionField) GetValue() interface{} {
89 if f, ok := f.value.(*lazyExtensionValue); ok {
90 return f.GetValue()
91 }
92 return f.value
93}
94
95// SetEagerValue sets the current value of the extension.
96// This must not be called concurrently.
97func (f *ExtensionField) SetEagerValue(v interface{}) {
98 f.value = v
99}
100
101// SetLazyValue sets a value that is to be lazily evaluated upon first use.
102// The returned value must not be nil.
103// This must not be called concurrently.
104func (f *ExtensionField) SetLazyValue(v func() interface{}) {
105 f.value = &lazyExtensionValue{value: v}
106}
107
108type lazyExtensionValue struct {
109 once uint32 // atomically set if value is valid
110 mu sync.Mutex // protects value
111 value interface{} // either the value itself or a func() interface{}
112}
113
114func (v *lazyExtensionValue) GetValue() interface{} {
115 if atomic.LoadUint32(&v.once) == 0 {
116 v.mu.Lock()
117 if f, ok := v.value.(func() interface{}); ok {
118 v.value = f()
119 }
120 atomic.StoreUint32(&v.once, 1)
121 v.mu.Unlock()
122 }
123 return v.value
124}