blob: 10a9fa2599646753380e317435b68719fbe4ba58 [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
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 }
Damien Neile91877d2019-06-27 10:54:42 -070038 // Does the unmarshal function need a value passed to it?
39 // This is true for composite types, where we pass in a message, list, or map to fill in,
40 // and for enums, where we pass in a prototype value to specify the concrete enum type.
41 switch xt.Kind() {
42 case pref.MessageKind, pref.GroupKind, pref.EnumKind:
43 e.unmarshalNeedsValue = true
44 default:
45 if xt.Cardinality() == pref.Repeated {
46 e.unmarshalNeedsValue = true
47 }
48 }
Damien Neilc37adef2019-04-01 13:49:56 -070049 mi.extensionFieldInfosMu.Lock()
50 if mi.extensionFieldInfos == nil {
Joe Tsai89d49632019-06-04 16:20:00 -070051 mi.extensionFieldInfos = make(map[pref.ExtensionType]*extensionFieldInfo)
Damien Neilc37adef2019-04-01 13:49:56 -070052 }
Joe Tsai89d49632019-06-04 16:20:00 -070053 mi.extensionFieldInfos[xt] = e
Damien Neilc37adef2019-04-01 13:49:56 -070054 mi.extensionFieldInfosMu.Unlock()
55 return e
56}
Joe Tsai89d49632019-06-04 16:20:00 -070057
58type ExtensionField struct {
Joe Tsai05e11b82019-06-04 17:05:04 -070059 typ pref.ExtensionType
Joe Tsai89d49632019-06-04 16:20:00 -070060
61 // value is either the value of GetValue,
62 // or a *lazyExtensionValue that then returns the value of GetValue.
63 value interface{} // TODO: switch to protoreflect.Value
64}
65
66func (f ExtensionField) HasType() bool {
Joe Tsai05e11b82019-06-04 17:05:04 -070067 return f.typ != nil
Joe Tsai89d49632019-06-04 16:20:00 -070068}
69func (f ExtensionField) GetType() pref.ExtensionType {
Joe Tsai05e11b82019-06-04 17:05:04 -070070 return f.typ
Joe Tsai89d49632019-06-04 16:20:00 -070071}
72func (f *ExtensionField) SetType(t pref.ExtensionType) {
Joe Tsai05e11b82019-06-04 17:05:04 -070073 f.typ = t
Joe Tsai89d49632019-06-04 16:20:00 -070074}
75
76// HasValue reports whether a value is set for the extension field.
77// This may be called concurrently.
78func (f ExtensionField) HasValue() bool {
79 return f.value != nil
80}
81
82// GetValue returns the concrete value for the extension field.
83// Let the type of Desc.ExtensionType be the "API type" and
84// the type of GetValue be the "storage type".
85// The API type and storage type are the same except:
86// * for scalars (except []byte), where the API type uses *T,
87// while the storage type uses T.
88// * for repeated fields, where the API type uses []T,
89// while the storage type uses *[]T.
90//
91// The reason for the divergence is so that the storage type more naturally
92// matches what is expected of when retrieving the values through the
93// protobuf reflection APIs.
94//
95// GetValue is only populated if Desc is also populated.
96// This may be called concurrently.
97//
98// TODO: switch interface{} to protoreflect.Value
99func (f ExtensionField) GetValue() interface{} {
100 if f, ok := f.value.(*lazyExtensionValue); ok {
101 return f.GetValue()
102 }
103 return f.value
104}
105
106// SetEagerValue sets the current value of the extension.
107// This must not be called concurrently.
108func (f *ExtensionField) SetEagerValue(v interface{}) {
109 f.value = v
110}
111
112// SetLazyValue sets a value that is to be lazily evaluated upon first use.
113// The returned value must not be nil.
114// This must not be called concurrently.
115func (f *ExtensionField) SetLazyValue(v func() interface{}) {
116 f.value = &lazyExtensionValue{value: v}
117}
118
119type lazyExtensionValue struct {
120 once uint32 // atomically set if value is valid
121 mu sync.Mutex // protects value
122 value interface{} // either the value itself or a func() interface{}
123}
124
125func (v *lazyExtensionValue) GetValue() interface{} {
126 if atomic.LoadUint32(&v.once) == 0 {
127 v.mu.Lock()
128 if f, ok := v.value.(func() interface{}); ok {
129 v.value = f()
130 }
131 atomic.StoreUint32(&v.once, 1)
132 v.mu.Unlock()
133 }
134 return v.value
135}