| // 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 |
| } |
| } |