blob: 69d5a96f4e446325e557a0d1aae08037a7f7e578 [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 piface "google.golang.org/protobuf/runtime/protoiface"
14)
15
16type extensionFieldInfo struct {
17 wiretag uint64
18 tagsize int
19 funcs ifaceCoderFuncs
20}
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
Joe Tsai89d49632019-06-04 16:20:00 -070032 wiretag := wire.EncodeTag(xt.Number(), wireTypes[xt.Kind()])
Damien Neilc37adef2019-04-01 13:49:56 -070033 e = &extensionFieldInfo{
34 wiretag: wiretag,
35 tagsize: wire.SizeVarint(wiretag),
Joe Tsai89d49632019-06-04 16:20:00 -070036 funcs: encoderFuncsForValue(xt, xt.GoType()),
Damien Neilc37adef2019-04-01 13:49:56 -070037 }
38
39 mi.extensionFieldInfosMu.Lock()
40 if mi.extensionFieldInfos == nil {
Joe Tsai89d49632019-06-04 16:20:00 -070041 mi.extensionFieldInfos = make(map[pref.ExtensionType]*extensionFieldInfo)
Damien Neilc37adef2019-04-01 13:49:56 -070042 }
Joe Tsai89d49632019-06-04 16:20:00 -070043 mi.extensionFieldInfos[xt] = e
Damien Neilc37adef2019-04-01 13:49:56 -070044 mi.extensionFieldInfosMu.Unlock()
45 return e
46}
Joe Tsai89d49632019-06-04 16:20:00 -070047
48type ExtensionField struct {
49 // Desc is the descriptor information for the extension field.
50 // It must be populated if value is populated.
51 Desc *piface.ExtensionDescV1 // TODO: unexport and switch to protoreflect.ExtensionType
52
53 // value is either the value of GetValue,
54 // or a *lazyExtensionValue that then returns the value of GetValue.
55 value interface{} // TODO: switch to protoreflect.Value
56}
57
58func (f ExtensionField) HasType() bool {
59 return f.Desc != nil
60}
61func (f ExtensionField) GetType() pref.ExtensionType {
62 return legacyExtensionTypeFromDesc(f.Desc)
63}
64func (f *ExtensionField) SetType(t pref.ExtensionType) {
65 f.Desc = legacyExtensionDescFromType(t)
66}
67
68// HasValue reports whether a value is set for the extension field.
69// This may be called concurrently.
70func (f ExtensionField) HasValue() bool {
71 return f.value != nil
72}
73
74// GetValue returns the concrete value for the extension field.
75// Let the type of Desc.ExtensionType be the "API type" and
76// the type of GetValue be the "storage type".
77// The API type and storage type are the same except:
78// * for scalars (except []byte), where the API type uses *T,
79// while the storage type uses T.
80// * for repeated fields, where the API type uses []T,
81// while the storage type uses *[]T.
82//
83// The reason for the divergence is so that the storage type more naturally
84// matches what is expected of when retrieving the values through the
85// protobuf reflection APIs.
86//
87// GetValue is only populated if Desc is also populated.
88// This may be called concurrently.
89//
90// TODO: switch interface{} to protoreflect.Value
91func (f ExtensionField) GetValue() interface{} {
92 if f, ok := f.value.(*lazyExtensionValue); ok {
93 return f.GetValue()
94 }
95 return f.value
96}
97
98// SetEagerValue sets the current value of the extension.
99// This must not be called concurrently.
100func (f *ExtensionField) SetEagerValue(v interface{}) {
101 f.value = v
102}
103
104// SetLazyValue sets a value that is to be lazily evaluated upon first use.
105// The returned value must not be nil.
106// This must not be called concurrently.
107func (f *ExtensionField) SetLazyValue(v func() interface{}) {
108 f.value = &lazyExtensionValue{value: v}
109}
110
111type lazyExtensionValue struct {
112 once uint32 // atomically set if value is valid
113 mu sync.Mutex // protects value
114 value interface{} // either the value itself or a func() interface{}
115}
116
117func (v *lazyExtensionValue) GetValue() interface{} {
118 if atomic.LoadUint32(&v.once) == 0 {
119 v.mu.Lock()
120 if f, ok := v.value.(func() interface{}); ok {
121 v.value = f()
122 }
123 atomic.StoreUint32(&v.once, 1)
124 v.mu.Unlock()
125 }
126 return v.value
127}