github.com/bakjos/protoreflect@v1.9.2/desc/internal/proto3_optional.go (about) 1 package internal 2 3 import ( 4 "reflect" 5 "strings" 6 7 "github.com/bakjos/protoreflect/internal/codec" 8 "github.com/golang/protobuf/proto" 9 dpb "github.com/golang/protobuf/protoc-gen-go/descriptor" 10 11 "github.com/bakjos/protoreflect/internal" 12 ) 13 14 // NB: We use reflection or unknown fields in case we are linked against an older 15 // version of the proto runtime which does not know about the proto3_optional field. 16 // We don't require linking with newer version (which would greatly simplify this) 17 // because that means pulling in v1.4+ of the protobuf runtime, which has some 18 // compatibility issues. (We'll be nice to users and not require they upgrade to 19 // that latest runtime to upgrade to newer protoreflect.) 20 21 func GetProto3Optional(fd *dpb.FieldDescriptorProto) bool { 22 type newerFieldDesc interface { 23 GetProto3Optional() bool 24 } 25 var pm proto.Message = fd 26 if fd, ok := pm.(newerFieldDesc); ok { 27 return fd.GetProto3Optional() 28 } 29 30 // Field does not exist, so we have to examine unknown fields 31 // (we just silently bail if we have problems parsing them) 32 unk := internal.GetUnrecognized(pm) 33 buf := codec.NewBuffer(unk) 34 for { 35 tag, wt, err := buf.DecodeTagAndWireType() 36 if err != nil { 37 return false 38 } 39 if tag == Field_proto3OptionalTag && wt == proto.WireVarint { 40 v, _ := buf.DecodeVarint() 41 return v != 0 42 } 43 if err := buf.SkipField(wt); err != nil { 44 return false 45 } 46 } 47 } 48 49 func SetProto3Optional(fd *dpb.FieldDescriptorProto) { 50 rv := reflect.ValueOf(fd).Elem() 51 fld := rv.FieldByName("Proto3Optional") 52 if fld.IsValid() { 53 fld.Set(reflect.ValueOf(proto.Bool(true))) 54 return 55 } 56 57 // Field does not exist, so we have to store as unknown field. 58 var buf codec.Buffer 59 if err := buf.EncodeTagAndWireType(Field_proto3OptionalTag, proto.WireVarint); err != nil { 60 // TODO: panic? log? 61 return 62 } 63 if err := buf.EncodeVarint(1); err != nil { 64 // TODO: panic? log? 65 return 66 } 67 internal.SetUnrecognized(fd, buf.Bytes()) 68 } 69 70 // ProcessProto3OptionalFields adds synthetic oneofs to the given message descriptor 71 // for each proto3 optional field. It also updates the fields to have the correct 72 // oneof index reference. 73 func ProcessProto3OptionalFields(msgd *dpb.DescriptorProto) { 74 var allNames map[string]struct{} 75 for _, fd := range msgd.Field { 76 if GetProto3Optional(fd) { 77 // lazy init the set of all names 78 if allNames == nil { 79 allNames = map[string]struct{}{} 80 for _, fd := range msgd.Field { 81 allNames[fd.GetName()] = struct{}{} 82 } 83 for _, fd := range msgd.Extension { 84 allNames[fd.GetName()] = struct{}{} 85 } 86 for _, ed := range msgd.EnumType { 87 allNames[ed.GetName()] = struct{}{} 88 for _, evd := range ed.Value { 89 allNames[evd.GetName()] = struct{}{} 90 } 91 } 92 for _, fd := range msgd.NestedType { 93 allNames[fd.GetName()] = struct{}{} 94 } 95 for _, n := range msgd.ReservedName { 96 allNames[n] = struct{}{} 97 } 98 } 99 100 // Compute a name for the synthetic oneof. This uses the same 101 // algorithm as used in protoc: 102 // https://github.com/protocolbuffers/protobuf/blob/74ad62759e0a9b5a21094f3fb9bb4ebfaa0d1ab8/src/google/protobuf/compiler/parser.cc#L785-L803 103 ooName := fd.GetName() 104 if !strings.HasPrefix(ooName, "_") { 105 ooName = "_" + ooName 106 } 107 for { 108 _, ok := allNames[ooName] 109 if !ok { 110 // found a unique name 111 allNames[ooName] = struct{}{} 112 break 113 } 114 ooName = "X" + ooName 115 } 116 117 fd.OneofIndex = proto.Int32(int32(len(msgd.OneofDecl))) 118 msgd.OneofDecl = append(msgd.OneofDecl, &dpb.OneofDescriptorProto{Name: proto.String(ooName)}) 119 } 120 } 121 }