mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-04-17 20:42:51 +00:00
Clearly specify that Get on an unpopulated field: * returns the default value for scalars * returns a mutable (but empty) List for repeated fields * returns a mutable (but empty) Map for map fields * returns an invalid value for message fields The difference in semantics between List+Maps and Messages is because protobuf semantics provide no distinction between an unpopulated and empty list or map. On the other hand, there is a semantic difference between an unpopulated message and an empty message. Default values for scalars is trivial to implement with FieldDescriptor.Default. A mutable, but empty List and Map is easy to implement for known fields since known fields are generated as a slice or map field in a struct. Since struct fields are addressable, the implementation can just return a reference to the slice or map. Repeated, extension fields are a little more tricky since extension fields are implemented under the hood as a map[FieldNumber]Extension. Rather than allocating an empty list in KnownFields.Get upon first retrieval (which presents a race), delegate the work to ExtensionFieldTypes.Register, which must occur before any Get operation. Register is not a concurrent-safe operation, so that is an excellent time to initilize empty lists. The implementation of extensions will need to be careful that Clear on a repeated field simply truncates it zero instead of deleting the object. For unpopulated messages, we return an invalid value, instead of the prior behavior of returning a typed nil-pointer to the Go type for the message. The approach is problematic because it assumes that 1) all messages are always implemented on a pointer reciever 2) a typed nil-pointer is an appropriate "read-only, but empty" message These assumptions are not true of all message types (e.g., dynamic messages). Change-Id: Ie96e6744c890308d9de738b6cf01d3b19e7e7c6a Reviewed-on: https://go-review.googlesource.com/c/150319 Reviewed-by: Damien Neil <dneil@google.com>
87 lines
2.1 KiB
Go
87 lines
2.1 KiB
Go
// Copyright 2018 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 value
|
|
|
|
import (
|
|
"reflect"
|
|
|
|
pref "github.com/golang/protobuf/v2/reflect/protoreflect"
|
|
)
|
|
|
|
func MapOf(p interface{}, kc, kv Converter) pref.Map {
|
|
// TODO: Validate that p is a *map[K]V?
|
|
rv := reflect.ValueOf(p).Elem()
|
|
return mapReflect{rv, kc, kv}
|
|
}
|
|
|
|
type mapReflect struct {
|
|
v reflect.Value // addressable map[K]V
|
|
keyConv Converter
|
|
valConv Converter
|
|
}
|
|
|
|
func (ms mapReflect) Len() int {
|
|
return ms.v.Len()
|
|
}
|
|
func (ms mapReflect) Has(k pref.MapKey) bool {
|
|
rk := ms.keyConv.GoValueOf(k.Value())
|
|
rv := ms.v.MapIndex(rk)
|
|
return rv.IsValid()
|
|
}
|
|
func (ms mapReflect) Get(k pref.MapKey) pref.Value {
|
|
rk := ms.keyConv.GoValueOf(k.Value())
|
|
rv := ms.v.MapIndex(rk)
|
|
if !rv.IsValid() {
|
|
return pref.Value{}
|
|
}
|
|
return ms.valConv.PBValueOf(rv)
|
|
}
|
|
func (ms mapReflect) Set(k pref.MapKey, v pref.Value) {
|
|
if ms.v.IsNil() {
|
|
ms.v.Set(reflect.MakeMap(ms.v.Type()))
|
|
}
|
|
rk := ms.keyConv.GoValueOf(k.Value())
|
|
rv := ms.valConv.GoValueOf(v)
|
|
ms.v.SetMapIndex(rk, rv)
|
|
}
|
|
func (ms mapReflect) Clear(k pref.MapKey) {
|
|
rk := ms.keyConv.GoValueOf(k.Value())
|
|
ms.v.SetMapIndex(rk, reflect.Value{})
|
|
}
|
|
func (ms mapReflect) Mutable(k pref.MapKey) pref.Mutable {
|
|
// Mutable is only valid for messages and panics for other kinds.
|
|
if ms.v.IsNil() {
|
|
ms.v.Set(reflect.MakeMap(ms.v.Type()))
|
|
}
|
|
rk := ms.keyConv.GoValueOf(k.Value())
|
|
rv := ms.v.MapIndex(rk)
|
|
if !rv.IsValid() {
|
|
pv := pref.ValueOf(ms.valConv.MessageType.New().ProtoReflect())
|
|
rv = ms.valConv.GoValueOf(pv)
|
|
ms.v.SetMapIndex(rk, rv)
|
|
}
|
|
return ms.valConv.PBValueOf(rv).Message()
|
|
}
|
|
func (ms mapReflect) Range(f func(pref.MapKey, pref.Value) bool) {
|
|
for _, k := range ms.v.MapKeys() {
|
|
if v := ms.v.MapIndex(k); v.IsValid() {
|
|
pk := ms.keyConv.PBValueOf(k).MapKey()
|
|
pv := ms.valConv.PBValueOf(v)
|
|
if !f(pk, pv) {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
func (ms mapReflect) Unwrap() interface{} {
|
|
return ms.v.Addr().Interface()
|
|
}
|
|
func (ms mapReflect) ProtoMutable() {}
|
|
|
|
var (
|
|
_ pref.Map = mapReflect{}
|
|
_ Unwrapper = mapReflect{}
|
|
)
|