mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-01-26 09:35:33 +00:00
b0c26f1868
This adds a experimental function to the internal/impl package which validates a wire-format message against a message type. The validator reports whether the message can be successfully unmarshaled, and whether the result is initialized (all required fields are set). In some cases, the validator returns ambiguous results when full validation would be expensive. The validator is unused outside of tests. In the future, it may be used to permit lazy unmarshaling of some data. It is being added now for testing; in particular, the wire fuzzer now checks the validator output for consistency with the unmarshaler. The validator adds a small amount of unused per-MessageType state. If this becomes a concern, we could conditionalize it with a build tag. Change-Id: I4216ef81d6a9ed975302eed189b02d08608858b4 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/212302 Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
169 lines
4.4 KiB
Go
169 lines
4.4 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
|
|
validation validationInfo
|
|
}
|
|
|
|
var legacyExtensionFieldInfoCache sync.Map // map[protoreflect.ExtensionType]*extensionFieldInfo
|
|
|
|
func getExtensionFieldInfo(xt pref.ExtensionType) *extensionFieldInfo {
|
|
if xi, ok := xt.(*ExtensionInfo); ok {
|
|
xi.lazyInit()
|
|
return xi.info
|
|
}
|
|
return legacyLoadExtensionFieldInfo(xt)
|
|
}
|
|
|
|
// legacyLoadExtensionFieldInfo dynamically loads a *ExtensionInfo for xt.
|
|
func legacyLoadExtensionFieldInfo(xt pref.ExtensionType) *extensionFieldInfo {
|
|
if xi, ok := legacyExtensionFieldInfoCache.Load(xt); ok {
|
|
return xi.(*extensionFieldInfo)
|
|
}
|
|
e := makeExtensionFieldInfo(xt.TypeDescriptor())
|
|
if e, ok := legacyMessageTypeCache.LoadOrStore(xt, e); ok {
|
|
return e.(*extensionFieldInfo)
|
|
}
|
|
return e
|
|
}
|
|
|
|
func makeExtensionFieldInfo(xd pref.ExtensionDescriptor) *extensionFieldInfo {
|
|
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
|
|
}
|
|
}
|
|
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() pref.Value {
|
|
return f.typ.ValueOf(fn())
|
|
}}
|
|
}
|
|
|
|
type lazyExtensionValue struct {
|
|
once uint32 // atomically set if value is valid
|
|
mu sync.Mutex // protects value
|
|
value interface{} // either a pref.Value itself or a func() pref.ValueOf
|
|
}
|
|
|
|
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)
|
|
}
|