protobuf-go/proto/extension.go
Nicolas Hillegeer 39bbf13930 proto: CSE ProtoReflect() and TypeDescriptor() in proto.HasExtension
No need to call them multiple times, no matter how cheap they are. Mild
improvements, this CL is 01-cse-hasextension:

    $ perflock -socket=@perflock -shared -cores 12 go test -tags=protolegacy -test.bench=BenchmarkExtension -test.benchmem -test.run=^# -test.count 10 internal/benchmarks/micro/micro_test.go

    goarch: amd64
    cpu: AMD Ryzen Threadripper PRO 3995WX 64-Cores
                          │ 00-cse-messageinfo │        01-cse-hasextension         │
                          │       sec/op       │   sec/op     vs base               │
    Extension/Has/None-12         103.30n ± 3%   96.73n ± 1%  -6.36% (p=0.000 n=10)
    Extension/Has/Set-12           113.0n ± 3%   107.1n ± 1%  -5.22% (p=0.000 n=10)
    Extension/Get/None-12          182.7n ± 2%   176.3n ± 2%  -3.48% (p=0.000 n=10)
    Extension/Get/Set-12           140.1n ± 2%   138.0n ± 1%  -1.46% (p=0.024 n=10)
    Extension/Set-12               218.6n ± 2%   219.5n ± 1%       ~ (p=0.172 n=10)
    geomean                        145.6n        140.8n       -3.25%

Change-Id: Ide1a0c0fe4e562ed24f88dc829249fca0f052d48
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/576315
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Nicolas Hillegeer <aktau@google.com>
Reviewed-by: Michael Stapelberg <stapelberg@google.com>
2024-04-08 13:07:34 +00:00

96 lines
2.9 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 proto
import (
"google.golang.org/protobuf/reflect/protoreflect"
)
// HasExtension reports whether an extension field is populated.
// It returns false if m is invalid or if xt does not extend m.
func HasExtension(m Message, xt protoreflect.ExtensionType) bool {
// Treat nil message interface or descriptor as an empty message; no populated
// fields.
if m == nil || xt == nil {
return false
}
// As a special-case, we reports invalid or mismatching descriptors
// as always not being populated (since they aren't).
mr := m.ProtoReflect()
xd := xt.TypeDescriptor()
if mr.Descriptor() != xd.ContainingMessage() {
return false
}
return mr.Has(xd)
}
// ClearExtension clears an extension field such that subsequent
// [HasExtension] calls return false.
// It panics if m is invalid or if xt does not extend m.
func ClearExtension(m Message, xt protoreflect.ExtensionType) {
m.ProtoReflect().Clear(xt.TypeDescriptor())
}
// GetExtension retrieves the value for an extension field.
// If the field is unpopulated, it returns the default value for
// scalars and an immutable, empty value for lists or messages.
// It panics if xt does not extend m.
func GetExtension(m Message, xt protoreflect.ExtensionType) interface{} {
// Treat nil message interface as an empty message; return the default.
if m == nil {
return xt.InterfaceOf(xt.Zero())
}
return xt.InterfaceOf(m.ProtoReflect().Get(xt.TypeDescriptor()))
}
// SetExtension stores the value of an extension field.
// It panics if m is invalid, xt does not extend m, or if type of v
// is invalid for the specified extension field.
func SetExtension(m Message, xt protoreflect.ExtensionType, v interface{}) {
xd := xt.TypeDescriptor()
pv := xt.ValueOf(v)
// Specially treat an invalid list, map, or message as clear.
isValid := true
switch {
case xd.IsList():
isValid = pv.List().IsValid()
case xd.IsMap():
isValid = pv.Map().IsValid()
case xd.Message() != nil:
isValid = pv.Message().IsValid()
}
if !isValid {
m.ProtoReflect().Clear(xd)
return
}
m.ProtoReflect().Set(xd, pv)
}
// RangeExtensions iterates over every populated extension field in m in an
// undefined order, calling f for each extension type and value encountered.
// It returns immediately if f returns false.
// While iterating, mutating operations may only be performed
// on the current extension field.
func RangeExtensions(m Message, f func(protoreflect.ExtensionType, interface{}) bool) {
// Treat nil message interface as an empty message; nothing to range over.
if m == nil {
return
}
m.ProtoReflect().Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
if fd.IsExtension() {
xt := fd.(protoreflect.ExtensionTypeDescriptor).Type()
vi := xt.InterfaceOf(v)
return f(xt, vi)
}
return true
})
}