internal/impl: initial commit
This provides an implementation of the has, get, set, clear methods for each
field in a message. The approach taken here is similar to the table-driven
implementation in the current v1 proto package.
The pointer_reflect.go and pointer_unsafe.go files are a simplified version of
the same files in the v1 implementation. They provide a pointer abstraction
that enables a high-efficiency approach in a non-purego environment.
The unsafe fast-path is not implemented in this commit.
This commit only implements the accessor methods for scalars using pure
Go reflection.
Change-Id: Icdf707e9d4e3385e55434f93b30a341a7680ae11
Reviewed-on: https://go-review.googlesource.com/135136
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/internal/impl/message.go b/internal/impl/message.go
new file mode 100644
index 0000000..2f10f1d
--- /dev/null
+++ b/internal/impl/message.go
@@ -0,0 +1,91 @@
+// 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 impl
+
+import (
+ "reflect"
+ "strconv"
+ "strings"
+
+ pref "google.golang.org/proto/reflect/protoreflect"
+)
+
+type MessageInfo struct {
+ // TODO: Split fields into dense and sparse maps similar to the current
+ // table-driven implementation in v1?
+ fields map[pref.FieldNumber]*fieldInfo
+}
+
+// generateFieldFuncs generates per-field functions for all common operations
+// to be performed on each field. It takes in a reflect.Type representing the
+// Go struct, and a protoreflect.MessageDescriptor to match with the fields
+// in the struct.
+//
+// This code assumes that the struct is well-formed and panics if there are
+// any discrepancies.
+func (mi *MessageInfo) generateFieldFuncs(t reflect.Type, md pref.MessageDescriptor) {
+ // Generate a mapping of field numbers and names to Go struct field or type.
+ fields := map[pref.FieldNumber]reflect.StructField{}
+ oneofs := map[pref.Name]reflect.StructField{}
+ oneofFields := map[pref.FieldNumber]reflect.Type{}
+ special := map[string]reflect.StructField{}
+fieldLoop:
+ for i := 0; i < t.NumField(); i++ {
+ f := t.Field(i)
+ for _, s := range strings.Split(f.Tag.Get("protobuf"), ",") {
+ if len(s) > 0 && strings.Trim(s, "0123456789") == "" {
+ n, _ := strconv.ParseUint(s, 10, 64)
+ fields[pref.FieldNumber(n)] = f
+ continue fieldLoop
+ }
+ }
+ if s := f.Tag.Get("protobuf_oneof"); len(s) > 0 {
+ oneofs[pref.Name(s)] = f
+ continue fieldLoop
+ }
+ switch f.Name {
+ case "XXX_weak", "XXX_unrecognized", "XXX_sizecache", "XXX_extensions", "XXX_InternalExtensions":
+ special[f.Name] = f
+ continue fieldLoop
+ }
+ }
+ if fn, ok := t.MethodByName("XXX_OneofFuncs"); ok {
+ vs := fn.Func.Call([]reflect.Value{reflect.New(fn.Type.In(0)).Elem()})[3]
+ oneofLoop:
+ for _, v := range vs.Interface().([]interface{}) {
+ tf := reflect.TypeOf(v).Elem()
+ f := tf.Field(0)
+ for _, s := range strings.Split(f.Tag.Get("protobuf"), ",") {
+ if len(s) > 0 && strings.Trim(s, "0123456789") == "" {
+ n, _ := strconv.ParseUint(s, 10, 64)
+ oneofFields[pref.FieldNumber(n)] = tf
+ continue oneofLoop
+ }
+ }
+ }
+ }
+
+ mi.fields = map[pref.FieldNumber]*fieldInfo{}
+ for i := 0; i < md.Fields().Len(); i++ {
+ fd := md.Fields().Get(i)
+ fs := fields[fd.Number()]
+ var fi fieldInfo
+ switch {
+ case fd.IsWeak():
+ fi = fieldInfoForWeak(fd, special["XXX_weak"])
+ case fd.OneofType() != nil:
+ fi = fieldInfoForOneof(fd, oneofs[fd.OneofType().Name()], oneofFields[fd.Number()])
+ case fd.IsMap():
+ fi = fieldInfoForMap(fd, fs)
+ case fd.Cardinality() == pref.Repeated:
+ fi = fieldInfoForVector(fd, fs)
+ case fd.Kind() != pref.MessageKind && fd.Kind() != pref.GroupKind:
+ fi = fieldInfoForScalar(fd, fs)
+ default:
+ fi = fieldInfoForMessage(fd, fs)
+ }
+ mi.fields[fd.Number()] = &fi
+ }
+}