protobuf-go/internal/impl/codec_extension.go
Damien Neil 4b3a82f6b1 internal/impl: clean up Value codecs
Remove the Go type from consideration when creating Value codecs, as it
is unnecessary. Value codecs convert between wire form and Values,
while Converters convert between Values and the Go type.

Change-Id: Iaa4bc7db81ad0a29dabd42c2229e6f33a0c91c67
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/193457
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-09-05 16:31:05 +00:00

162 lines
4.1 KiB
Go

// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package impl
import (
"sync"
"sync/atomic"
"google.golang.org/protobuf/internal/encoding/wire"
pref "google.golang.org/protobuf/reflect/protoreflect"
)
type extensionFieldInfo struct {
wiretag uint64
tagsize int
unmarshalNeedsValue bool
funcs valueCoderFuncs
}
func (mi *MessageInfo) extensionFieldInfo(xt pref.ExtensionType) *extensionFieldInfo {
// As of this time (Go 1.12, linux/amd64), an RWMutex benchmarks as faster
// than a sync.Map.
mi.extensionFieldInfosMu.RLock()
e, ok := mi.extensionFieldInfos[xt]
mi.extensionFieldInfosMu.RUnlock()
if ok {
return e
}
xd := xt.TypeDescriptor()
var wiretag uint64
if !xd.IsPacked() {
wiretag = wire.EncodeTag(xd.Number(), wireTypes[xd.Kind()])
} else {
wiretag = wire.EncodeTag(xd.Number(), wire.BytesType)
}
e = &extensionFieldInfo{
wiretag: wiretag,
tagsize: wire.SizeVarint(wiretag),
funcs: encoderFuncsForValue(xd),
}
// Does the unmarshal function need a value passed to it?
// This is true for composite types, where we pass in a message, list, or map to fill in,
// and for enums, where we pass in a prototype value to specify the concrete enum type.
switch xd.Kind() {
case pref.MessageKind, pref.GroupKind, pref.EnumKind:
e.unmarshalNeedsValue = true
default:
if xd.Cardinality() == pref.Repeated {
e.unmarshalNeedsValue = true
}
}
mi.extensionFieldInfosMu.Lock()
if mi.extensionFieldInfos == nil {
mi.extensionFieldInfos = make(map[pref.ExtensionType]*extensionFieldInfo)
}
mi.extensionFieldInfos[xt] = e
mi.extensionFieldInfosMu.Unlock()
return e
}
type ExtensionField struct {
typ pref.ExtensionType
// value is either the value of GetValue,
// or a *lazyExtensionValue that then returns the value of GetValue.
value pref.Value
lazy *lazyExtensionValue
}
// Set sets the type and value of the extension field.
// This must not be called concurrently.
func (f *ExtensionField) Set(t pref.ExtensionType, v pref.Value) {
f.typ = t
f.value = v
}
// SetLazy sets the type and a value that is to be lazily evaluated upon first use.
// This must not be called concurrently.
func (f *ExtensionField) SetLazy(t pref.ExtensionType, fn func() pref.Value) {
f.typ = t
f.lazy = &lazyExtensionValue{value: fn}
}
// Value returns the value of the extension field.
// This may be called concurrently.
func (f *ExtensionField) Value() pref.Value {
if f.lazy != nil {
return f.lazy.GetValue()
}
return f.value
}
// Type returns the type of the extension field.
// This may be called concurrently.
func (f ExtensionField) Type() pref.ExtensionType {
return f.typ
}
// IsSet returns whether the extension field is set.
// This may be called concurrently.
func (f ExtensionField) IsSet() bool {
return f.typ != nil
}
// Deprecated: Do not use.
func (f ExtensionField) HasType() bool {
return f.typ != nil
}
// Deprecated: Do not use.
func (f ExtensionField) GetType() pref.ExtensionType {
return f.typ
}
// Deprecated: Do not use.
func (f *ExtensionField) SetType(t pref.ExtensionType) {
f.typ = t
}
// Deprecated: Do not use.
func (f ExtensionField) HasValue() bool {
return f.value.IsValid() || f.lazy != nil
}
// Deprecated: Do not use.
func (f ExtensionField) GetValue() interface{} {
return f.typ.InterfaceOf(f.Value())
}
// Deprecated: Do not use.
func (f *ExtensionField) SetEagerValue(ival interface{}) {
f.value = f.typ.ValueOf(ival)
}
// Deprecated: Do not use.
func (f *ExtensionField) SetLazyValue(fn func() interface{}) {
f.lazy = &lazyExtensionValue{value: func() interface{} {
return f.typ.ValueOf(fn())
}}
}
type lazyExtensionValue struct {
once uint32 // atomically set if value is valid
mu sync.Mutex // protects value
value interface{} // either the value itself or a func() interface{}
}
func (v *lazyExtensionValue) GetValue() pref.Value {
if atomic.LoadUint32(&v.once) == 0 {
v.mu.Lock()
if f, ok := v.value.(func() pref.Value); ok {
v.value = f()
}
atomic.StoreUint32(&v.once, 1)
v.mu.Unlock()
}
return v.value.(pref.Value)
}