|  | // 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 protoregistry provides data structures to register and lookup | 
|  | // protobuf descriptor types. | 
|  | // | 
|  | // The Files registry contains file descriptors and provides the ability | 
|  | // to iterate over the files or lookup a specific descriptor within the files. | 
|  | // Files only contains protobuf descriptors and has no understanding of Go | 
|  | // type information that may be associated with each descriptor. | 
|  | // | 
|  | // The Types registry contains descriptor types for which there is a known | 
|  | // Go type associated with that descriptor. It provides the ability to iterate | 
|  | // over the registered types or lookup a type by name. | 
|  | package protoregistry | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "log" | 
|  | "strings" | 
|  | "sync" | 
|  |  | 
|  | "google.golang.org/protobuf/internal/errors" | 
|  | "google.golang.org/protobuf/reflect/protoreflect" | 
|  | ) | 
|  |  | 
|  | // ignoreConflict reports whether to ignore a registration conflict | 
|  | // given the descriptor being registered and the error. | 
|  | // It is a variable so that the behavior is easily overridden in another file. | 
|  | var ignoreConflict = func(d protoreflect.Descriptor, err error) bool { | 
|  | log.Printf(""+ | 
|  | "WARNING: %v\n"+ | 
|  | "A future release will panic on registration conflicts.\n"+ | 
|  | // TODO(blocks): Add a URL pointing to documentation on how to resolve conflicts. | 
|  | "\n", err) | 
|  | return true | 
|  | } | 
|  |  | 
|  | var globalMutex sync.RWMutex | 
|  |  | 
|  | // GlobalFiles is a global registry of file descriptors. | 
|  | var GlobalFiles *Files = new(Files) | 
|  |  | 
|  | // GlobalTypes is the registry used by default for type lookups | 
|  | // unless a local registry is provided by the user. | 
|  | var GlobalTypes *Types = new(Types) | 
|  |  | 
|  | // NotFound is a sentinel error value to indicate that the type was not found. | 
|  | // | 
|  | // Since registry lookup can happen in the critical performance path, resolvers | 
|  | // must return this exact error value, not an error wrapping it. | 
|  | var NotFound = errors.New("not found") | 
|  |  | 
|  | // Files is a registry for looking up or iterating over files and the | 
|  | // descriptors contained within them. | 
|  | // The Find and Range methods are safe for concurrent use. | 
|  | type Files struct { | 
|  | // The map of descsByName contains: | 
|  | //	EnumDescriptor | 
|  | //	EnumValueDescriptor | 
|  | //	MessageDescriptor | 
|  | //	ExtensionDescriptor | 
|  | //	ServiceDescriptor | 
|  | //	*packageDescriptor | 
|  | // | 
|  | // Note that files are stored as a slice, since a package may contain | 
|  | // multiple files. Only top-level declarations are registered. | 
|  | // Note that enum values are in the top-level since that are in the same | 
|  | // scope as the parent enum. | 
|  | descsByName map[protoreflect.FullName]interface{} | 
|  | filesByPath map[string]protoreflect.FileDescriptor | 
|  | } | 
|  |  | 
|  | type packageDescriptor struct { | 
|  | files []protoreflect.FileDescriptor | 
|  | } | 
|  |  | 
|  | // RegisterFile registers the provided file descriptor. | 
|  | // | 
|  | // If any descriptor within the file conflicts with the descriptor of any | 
|  | // previously registered file (e.g., two enums with the same full name), | 
|  | // then the file is not registered and an error is returned. | 
|  | // | 
|  | // It is permitted for multiple files to have the same file path. | 
|  | func (r *Files) RegisterFile(file protoreflect.FileDescriptor) error { | 
|  | if r == GlobalFiles { | 
|  | globalMutex.Lock() | 
|  | defer globalMutex.Unlock() | 
|  | } | 
|  | if r.descsByName == nil { | 
|  | r.descsByName = map[protoreflect.FullName]interface{}{ | 
|  | "": &packageDescriptor{}, | 
|  | } | 
|  | r.filesByPath = make(map[string]protoreflect.FileDescriptor) | 
|  | } | 
|  | path := file.Path() | 
|  | if prev := r.filesByPath[path]; prev != nil { | 
|  | err := errors.New("file %q is already registered", file.Path()) | 
|  | err = amendErrorWithCaller(err, prev, file) | 
|  | if r == GlobalFiles && ignoreConflict(file, err) { | 
|  | err = nil | 
|  | } | 
|  | return err | 
|  | } | 
|  |  | 
|  | for name := file.Package(); name != ""; name = name.Parent() { | 
|  | switch prev := r.descsByName[name]; prev.(type) { | 
|  | case nil, *packageDescriptor: | 
|  | default: | 
|  | err := errors.New("file %q has a package name conflict over %v", file.Path(), name) | 
|  | err = amendErrorWithCaller(err, prev, file) | 
|  | if r == GlobalFiles && ignoreConflict(file, err) { | 
|  | err = nil | 
|  | } | 
|  | return err | 
|  | } | 
|  | } | 
|  | var err error | 
|  | var hasConflict bool | 
|  | rangeTopLevelDescriptors(file, func(d protoreflect.Descriptor) { | 
|  | if prev := r.descsByName[d.FullName()]; prev != nil { | 
|  | hasConflict = true | 
|  | err = errors.New("file %q has a name conflict over %v", file.Path(), d.FullName()) | 
|  | err = amendErrorWithCaller(err, prev, file) | 
|  | if r == GlobalFiles && ignoreConflict(d, err) { | 
|  | err = nil | 
|  | } | 
|  | } | 
|  | }) | 
|  | if hasConflict { | 
|  | return err | 
|  | } | 
|  |  | 
|  | for name := file.Package(); name != ""; name = name.Parent() { | 
|  | if r.descsByName[name] == nil { | 
|  | r.descsByName[name] = &packageDescriptor{} | 
|  | } | 
|  | } | 
|  | p := r.descsByName[file.Package()].(*packageDescriptor) | 
|  | p.files = append(p.files, file) | 
|  | rangeTopLevelDescriptors(file, func(d protoreflect.Descriptor) { | 
|  | r.descsByName[d.FullName()] = d | 
|  | }) | 
|  | r.filesByPath[path] = file | 
|  | return nil | 
|  | } | 
|  |  | 
|  | // FindDescriptorByName looks up a descriptor by the full name. | 
|  | // | 
|  | // This returns (nil, NotFound) if not found. | 
|  | func (r *Files) FindDescriptorByName(name protoreflect.FullName) (protoreflect.Descriptor, error) { | 
|  | if r == nil { | 
|  | return nil, NotFound | 
|  | } | 
|  | if r == GlobalFiles { | 
|  | globalMutex.RLock() | 
|  | defer globalMutex.RUnlock() | 
|  | } | 
|  | prefix := name | 
|  | suffix := nameSuffix("") | 
|  | for prefix != "" { | 
|  | if d, ok := r.descsByName[prefix]; ok { | 
|  | switch d := d.(type) { | 
|  | case protoreflect.EnumDescriptor: | 
|  | if d.FullName() == name { | 
|  | return d, nil | 
|  | } | 
|  | case protoreflect.EnumValueDescriptor: | 
|  | if d.FullName() == name { | 
|  | return d, nil | 
|  | } | 
|  | case protoreflect.MessageDescriptor: | 
|  | if d.FullName() == name { | 
|  | return d, nil | 
|  | } | 
|  | if d := findDescriptorInMessage(d, suffix); d != nil && d.FullName() == name { | 
|  | return d, nil | 
|  | } | 
|  | case protoreflect.ExtensionDescriptor: | 
|  | if d.FullName() == name { | 
|  | return d, nil | 
|  | } | 
|  | case protoreflect.ServiceDescriptor: | 
|  | if d.FullName() == name { | 
|  | return d, nil | 
|  | } | 
|  | if d := d.Methods().ByName(suffix.Pop()); d != nil && d.FullName() == name { | 
|  | return d, nil | 
|  | } | 
|  | } | 
|  | return nil, NotFound | 
|  | } | 
|  | prefix = prefix.Parent() | 
|  | suffix = nameSuffix(name[len(prefix)+len("."):]) | 
|  | } | 
|  | return nil, NotFound | 
|  | } | 
|  |  | 
|  | func findDescriptorInMessage(md protoreflect.MessageDescriptor, suffix nameSuffix) protoreflect.Descriptor { | 
|  | name := suffix.Pop() | 
|  | if suffix == "" { | 
|  | if ed := md.Enums().ByName(name); ed != nil { | 
|  | return ed | 
|  | } | 
|  | for i := md.Enums().Len() - 1; i >= 0; i-- { | 
|  | if vd := md.Enums().Get(i).Values().ByName(name); vd != nil { | 
|  | return vd | 
|  | } | 
|  | } | 
|  | if xd := md.Extensions().ByName(name); xd != nil { | 
|  | return xd | 
|  | } | 
|  | if fd := md.Fields().ByName(name); fd != nil { | 
|  | return fd | 
|  | } | 
|  | if od := md.Oneofs().ByName(name); od != nil { | 
|  | return od | 
|  | } | 
|  | } | 
|  | if md := md.Messages().ByName(name); md != nil { | 
|  | if suffix == "" { | 
|  | return md | 
|  | } | 
|  | return findDescriptorInMessage(md, suffix) | 
|  | } | 
|  | return nil | 
|  | } | 
|  |  | 
|  | type nameSuffix string | 
|  |  | 
|  | func (s *nameSuffix) Pop() (name protoreflect.Name) { | 
|  | if i := strings.IndexByte(string(*s), '.'); i >= 0 { | 
|  | name, *s = protoreflect.Name((*s)[:i]), (*s)[i+1:] | 
|  | } else { | 
|  | name, *s = protoreflect.Name((*s)), "" | 
|  | } | 
|  | return name | 
|  | } | 
|  |  | 
|  | // FindFileByPath looks up a file by the path. | 
|  | // | 
|  | // This returns (nil, NotFound) if not found. | 
|  | func (r *Files) FindFileByPath(path string) (protoreflect.FileDescriptor, error) { | 
|  | if r == nil { | 
|  | return nil, NotFound | 
|  | } | 
|  | if r == GlobalFiles { | 
|  | globalMutex.RLock() | 
|  | defer globalMutex.RUnlock() | 
|  | } | 
|  | if fd, ok := r.filesByPath[path]; ok { | 
|  | return fd, nil | 
|  | } | 
|  | return nil, NotFound | 
|  | } | 
|  |  | 
|  | // NumFiles reports the number of registered files. | 
|  | func (r *Files) NumFiles() int { | 
|  | if r == nil { | 
|  | return 0 | 
|  | } | 
|  | if r == GlobalFiles { | 
|  | globalMutex.RLock() | 
|  | defer globalMutex.RUnlock() | 
|  | } | 
|  | return len(r.filesByPath) | 
|  | } | 
|  |  | 
|  | // RangeFiles iterates over all registered files while f returns true. | 
|  | // The iteration order is undefined. | 
|  | func (r *Files) RangeFiles(f func(protoreflect.FileDescriptor) bool) { | 
|  | if r == nil { | 
|  | return | 
|  | } | 
|  | if r == GlobalFiles { | 
|  | globalMutex.RLock() | 
|  | defer globalMutex.RUnlock() | 
|  | } | 
|  | for _, file := range r.filesByPath { | 
|  | if !f(file) { | 
|  | return | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // NumFilesByPackage reports the number of registered files in a proto package. | 
|  | func (r *Files) NumFilesByPackage(name protoreflect.FullName) int { | 
|  | if r == nil { | 
|  | return 0 | 
|  | } | 
|  | if r == GlobalFiles { | 
|  | globalMutex.RLock() | 
|  | defer globalMutex.RUnlock() | 
|  | } | 
|  | p, ok := r.descsByName[name].(*packageDescriptor) | 
|  | if !ok { | 
|  | return 0 | 
|  | } | 
|  | return len(p.files) | 
|  | } | 
|  |  | 
|  | // RangeFilesByPackage iterates over all registered files in a given proto package | 
|  | // while f returns true. The iteration order is undefined. | 
|  | func (r *Files) RangeFilesByPackage(name protoreflect.FullName, f func(protoreflect.FileDescriptor) bool) { | 
|  | if r == nil { | 
|  | return | 
|  | } | 
|  | if r == GlobalFiles { | 
|  | globalMutex.RLock() | 
|  | defer globalMutex.RUnlock() | 
|  | } | 
|  | p, ok := r.descsByName[name].(*packageDescriptor) | 
|  | if !ok { | 
|  | return | 
|  | } | 
|  | for _, file := range p.files { | 
|  | if !f(file) { | 
|  | return | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // rangeTopLevelDescriptors iterates over all top-level descriptors in a file | 
|  | // which will be directly entered into the registry. | 
|  | func rangeTopLevelDescriptors(fd protoreflect.FileDescriptor, f func(protoreflect.Descriptor)) { | 
|  | eds := fd.Enums() | 
|  | for i := eds.Len() - 1; i >= 0; i-- { | 
|  | f(eds.Get(i)) | 
|  | vds := eds.Get(i).Values() | 
|  | for i := vds.Len() - 1; i >= 0; i-- { | 
|  | f(vds.Get(i)) | 
|  | } | 
|  | } | 
|  | mds := fd.Messages() | 
|  | for i := mds.Len() - 1; i >= 0; i-- { | 
|  | f(mds.Get(i)) | 
|  | } | 
|  | xds := fd.Extensions() | 
|  | for i := xds.Len() - 1; i >= 0; i-- { | 
|  | f(xds.Get(i)) | 
|  | } | 
|  | sds := fd.Services() | 
|  | for i := sds.Len() - 1; i >= 0; i-- { | 
|  | f(sds.Get(i)) | 
|  | } | 
|  | } | 
|  |  | 
|  | // MessageTypeResolver is an interface for looking up messages. | 
|  | // | 
|  | // A compliant implementation must deterministically return the same type | 
|  | // if no error is encountered. | 
|  | // | 
|  | // The Types type implements this interface. | 
|  | type MessageTypeResolver interface { | 
|  | // FindMessageByName looks up a message by its full name. | 
|  | // E.g., "google.protobuf.Any" | 
|  | // | 
|  | // This return (nil, NotFound) if not found. | 
|  | FindMessageByName(message protoreflect.FullName) (protoreflect.MessageType, error) | 
|  |  | 
|  | // FindMessageByURL looks up a message by a URL identifier. | 
|  | // See documentation on google.protobuf.Any.type_url for the URL format. | 
|  | // | 
|  | // This returns (nil, NotFound) if not found. | 
|  | FindMessageByURL(url string) (protoreflect.MessageType, error) | 
|  | } | 
|  |  | 
|  | // ExtensionTypeResolver is an interface for looking up extensions. | 
|  | // | 
|  | // A compliant implementation must deterministically return the same type | 
|  | // if no error is encountered. | 
|  | // | 
|  | // The Types type implements this interface. | 
|  | type ExtensionTypeResolver interface { | 
|  | // FindExtensionByName looks up a extension field by the field's full name. | 
|  | // Note that this is the full name of the field as determined by | 
|  | // where the extension is declared and is unrelated to the full name of the | 
|  | // message being extended. | 
|  | // | 
|  | // This returns (nil, NotFound) if not found. | 
|  | FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) | 
|  |  | 
|  | // FindExtensionByNumber looks up a extension field by the field number | 
|  | // within some parent message, identified by full name. | 
|  | // | 
|  | // This returns (nil, NotFound) if not found. | 
|  | FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) | 
|  | } | 
|  |  | 
|  | var ( | 
|  | _ MessageTypeResolver   = (*Types)(nil) | 
|  | _ ExtensionTypeResolver = (*Types)(nil) | 
|  | ) | 
|  |  | 
|  | // Types is a registry for looking up or iterating over descriptor types. | 
|  | // The Find and Range methods are safe for concurrent use. | 
|  | type Types struct { | 
|  | typesByName         typesByName | 
|  | extensionsByMessage extensionsByMessage | 
|  |  | 
|  | numEnums      int | 
|  | numMessages   int | 
|  | numExtensions int | 
|  | } | 
|  |  | 
|  | type ( | 
|  | typesByName         map[protoreflect.FullName]interface{} | 
|  | extensionsByMessage map[protoreflect.FullName]extensionsByNumber | 
|  | extensionsByNumber  map[protoreflect.FieldNumber]protoreflect.ExtensionType | 
|  | ) | 
|  |  | 
|  | // RegisterMessage registers the provided message type. | 
|  | // | 
|  | // If a naming conflict occurs, the type is not registered and an error is returned. | 
|  | func (r *Types) RegisterMessage(mt protoreflect.MessageType) error { | 
|  | if r == GlobalTypes { | 
|  | globalMutex.Lock() | 
|  | defer globalMutex.Unlock() | 
|  | } | 
|  |  | 
|  | if err := r.register("message", mt.Descriptor(), mt); err != nil { | 
|  | return err | 
|  | } | 
|  | r.numMessages++ | 
|  | return nil | 
|  | } | 
|  |  | 
|  | // RegisterEnum registers the provided enum type. | 
|  | // | 
|  | // If a naming conflict occurs, the type is not registered and an error is returned. | 
|  | func (r *Types) RegisterEnum(et protoreflect.EnumType) error { | 
|  | if r == GlobalTypes { | 
|  | globalMutex.Lock() | 
|  | defer globalMutex.Unlock() | 
|  | } | 
|  |  | 
|  | if err := r.register("enum", et.Descriptor(), et); err != nil { | 
|  | return err | 
|  | } | 
|  | r.numEnums++ | 
|  | return nil | 
|  | } | 
|  |  | 
|  | // RegisterExtension registers the provided extension type. | 
|  | // | 
|  | // If a naming conflict occurs, the type is not registered and an error is returned. | 
|  | func (r *Types) RegisterExtension(xt protoreflect.ExtensionType) error { | 
|  | if r == GlobalTypes { | 
|  | globalMutex.Lock() | 
|  | defer globalMutex.Unlock() | 
|  | } | 
|  |  | 
|  | xd := xt.TypeDescriptor() | 
|  | field := xd.Number() | 
|  | message := xd.ContainingMessage().FullName() | 
|  | if prev := r.extensionsByMessage[message][field]; prev != nil { | 
|  | err := errors.New("extension number %d is already registered on message %v", field, message) | 
|  | err = amendErrorWithCaller(err, prev, xt) | 
|  | if !(r == GlobalTypes && ignoreConflict(xd, err)) { | 
|  | return err | 
|  | } | 
|  | } | 
|  |  | 
|  | if err := r.register("extension", xt.TypeDescriptor(), xt); err != nil { | 
|  | return err | 
|  | } | 
|  | if r.extensionsByMessage == nil { | 
|  | r.extensionsByMessage = make(extensionsByMessage) | 
|  | } | 
|  | if r.extensionsByMessage[message] == nil { | 
|  | r.extensionsByMessage[message] = make(extensionsByNumber) | 
|  | } | 
|  | r.extensionsByMessage[message][field] = xt | 
|  | r.numExtensions++ | 
|  | return nil | 
|  | } | 
|  |  | 
|  | func (r *Types) register(kind string, desc protoreflect.Descriptor, typ interface{}) error { | 
|  | name := desc.FullName() | 
|  | prev := r.typesByName[name] | 
|  | if prev != nil { | 
|  | err := errors.New("%v %v is already registered", kind, name) | 
|  | err = amendErrorWithCaller(err, prev, typ) | 
|  | if !(r == GlobalTypes && ignoreConflict(desc, err)) { | 
|  | return err | 
|  | } | 
|  | } | 
|  | if r.typesByName == nil { | 
|  | r.typesByName = make(typesByName) | 
|  | } | 
|  | r.typesByName[name] = typ | 
|  | return nil | 
|  | } | 
|  |  | 
|  | // FindEnumByName looks up an enum by its full name. | 
|  | // E.g., "google.protobuf.Field.Kind". | 
|  | // | 
|  | // This returns (nil, NotFound) if not found. | 
|  | func (r *Types) FindEnumByName(enum protoreflect.FullName) (protoreflect.EnumType, error) { | 
|  | if r == nil { | 
|  | return nil, NotFound | 
|  | } | 
|  | if r == GlobalTypes { | 
|  | globalMutex.RLock() | 
|  | defer globalMutex.RUnlock() | 
|  | } | 
|  | if v := r.typesByName[enum]; v != nil { | 
|  | if et, _ := v.(protoreflect.EnumType); et != nil { | 
|  | return et, nil | 
|  | } | 
|  | return nil, errors.New("found wrong type: got %v, want enum", typeName(v)) | 
|  | } | 
|  | return nil, NotFound | 
|  | } | 
|  |  | 
|  | // FindMessageByName looks up a message by its full name. | 
|  | // E.g., "google.protobuf.Any" | 
|  | // | 
|  | // This return (nil, NotFound) if not found. | 
|  | func (r *Types) FindMessageByName(message protoreflect.FullName) (protoreflect.MessageType, error) { | 
|  | // The full name by itself is a valid URL. | 
|  | return r.FindMessageByURL(string(message)) | 
|  | } | 
|  |  | 
|  | // FindMessageByURL looks up a message by a URL identifier. | 
|  | // See documentation on google.protobuf.Any.type_url for the URL format. | 
|  | // | 
|  | // This returns (nil, NotFound) if not found. | 
|  | func (r *Types) FindMessageByURL(url string) (protoreflect.MessageType, error) { | 
|  | if r == nil { | 
|  | return nil, NotFound | 
|  | } | 
|  | if r == GlobalTypes { | 
|  | globalMutex.RLock() | 
|  | defer globalMutex.RUnlock() | 
|  | } | 
|  | message := protoreflect.FullName(url) | 
|  | if i := strings.LastIndexByte(url, '/'); i >= 0 { | 
|  | message = message[i+len("/"):] | 
|  | } | 
|  |  | 
|  | if v := r.typesByName[message]; v != nil { | 
|  | if mt, _ := v.(protoreflect.MessageType); mt != nil { | 
|  | return mt, nil | 
|  | } | 
|  | return nil, errors.New("found wrong type: got %v, want message", typeName(v)) | 
|  | } | 
|  | return nil, NotFound | 
|  | } | 
|  |  | 
|  | // FindExtensionByName looks up a extension field by the field's full name. | 
|  | // Note that this is the full name of the field as determined by | 
|  | // where the extension is declared and is unrelated to the full name of the | 
|  | // message being extended. | 
|  | // | 
|  | // This returns (nil, NotFound) if not found. | 
|  | func (r *Types) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) { | 
|  | if r == nil { | 
|  | return nil, NotFound | 
|  | } | 
|  | if r == GlobalTypes { | 
|  | globalMutex.RLock() | 
|  | defer globalMutex.RUnlock() | 
|  | } | 
|  | if v := r.typesByName[field]; v != nil { | 
|  | if xt, _ := v.(protoreflect.ExtensionType); xt != nil { | 
|  | return xt, nil | 
|  | } | 
|  | return nil, errors.New("found wrong type: got %v, want extension", typeName(v)) | 
|  | } | 
|  | return nil, NotFound | 
|  | } | 
|  |  | 
|  | // FindExtensionByNumber looks up a extension field by the field number | 
|  | // within some parent message, identified by full name. | 
|  | // | 
|  | // This returns (nil, NotFound) if not found. | 
|  | func (r *Types) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) { | 
|  | if r == nil { | 
|  | return nil, NotFound | 
|  | } | 
|  | if r == GlobalTypes { | 
|  | globalMutex.RLock() | 
|  | defer globalMutex.RUnlock() | 
|  | } | 
|  | if xt, ok := r.extensionsByMessage[message][field]; ok { | 
|  | return xt, nil | 
|  | } | 
|  | return nil, NotFound | 
|  | } | 
|  |  | 
|  | // NumEnums reports the number of registered enums. | 
|  | func (r *Types) NumEnums() int { | 
|  | if r == nil { | 
|  | return 0 | 
|  | } | 
|  | if r == GlobalTypes { | 
|  | globalMutex.RLock() | 
|  | defer globalMutex.RUnlock() | 
|  | } | 
|  | return r.numEnums | 
|  | } | 
|  |  | 
|  | // RangeEnums iterates over all registered enums while f returns true. | 
|  | // Iteration order is undefined. | 
|  | func (r *Types) RangeEnums(f func(protoreflect.EnumType) bool) { | 
|  | if r == nil { | 
|  | return | 
|  | } | 
|  | if r == GlobalTypes { | 
|  | globalMutex.RLock() | 
|  | defer globalMutex.RUnlock() | 
|  | } | 
|  | for _, typ := range r.typesByName { | 
|  | if et, ok := typ.(protoreflect.EnumType); ok { | 
|  | if !f(et) { | 
|  | return | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // NumMessages reports the number of registered messages. | 
|  | func (r *Types) NumMessages() int { | 
|  | if r == nil { | 
|  | return 0 | 
|  | } | 
|  | if r == GlobalTypes { | 
|  | globalMutex.RLock() | 
|  | defer globalMutex.RUnlock() | 
|  | } | 
|  | return r.numMessages | 
|  | } | 
|  |  | 
|  | // RangeMessages iterates over all registered messages while f returns true. | 
|  | // Iteration order is undefined. | 
|  | func (r *Types) RangeMessages(f func(protoreflect.MessageType) bool) { | 
|  | if r == nil { | 
|  | return | 
|  | } | 
|  | if r == GlobalTypes { | 
|  | globalMutex.RLock() | 
|  | defer globalMutex.RUnlock() | 
|  | } | 
|  | for _, typ := range r.typesByName { | 
|  | if mt, ok := typ.(protoreflect.MessageType); ok { | 
|  | if !f(mt) { | 
|  | return | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // NumExtensions reports the number of registered extensions. | 
|  | func (r *Types) NumExtensions() int { | 
|  | if r == nil { | 
|  | return 0 | 
|  | } | 
|  | if r == GlobalTypes { | 
|  | globalMutex.RLock() | 
|  | defer globalMutex.RUnlock() | 
|  | } | 
|  | return r.numExtensions | 
|  | } | 
|  |  | 
|  | // RangeExtensions iterates over all registered extensions while f returns true. | 
|  | // Iteration order is undefined. | 
|  | func (r *Types) RangeExtensions(f func(protoreflect.ExtensionType) bool) { | 
|  | if r == nil { | 
|  | return | 
|  | } | 
|  | if r == GlobalTypes { | 
|  | globalMutex.RLock() | 
|  | defer globalMutex.RUnlock() | 
|  | } | 
|  | for _, typ := range r.typesByName { | 
|  | if xt, ok := typ.(protoreflect.ExtensionType); ok { | 
|  | if !f(xt) { | 
|  | return | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // NumExtensionsByMessage reports the number of registered extensions for | 
|  | // a given message type. | 
|  | func (r *Types) NumExtensionsByMessage(message protoreflect.FullName) int { | 
|  | if r == nil { | 
|  | return 0 | 
|  | } | 
|  | if r == GlobalTypes { | 
|  | globalMutex.RLock() | 
|  | defer globalMutex.RUnlock() | 
|  | } | 
|  | return len(r.extensionsByMessage[message]) | 
|  | } | 
|  |  | 
|  | // RangeExtensionsByMessage iterates over all registered extensions filtered | 
|  | // by a given message type while f returns true. Iteration order is undefined. | 
|  | func (r *Types) RangeExtensionsByMessage(message protoreflect.FullName, f func(protoreflect.ExtensionType) bool) { | 
|  | if r == nil { | 
|  | return | 
|  | } | 
|  | if r == GlobalTypes { | 
|  | globalMutex.RLock() | 
|  | defer globalMutex.RUnlock() | 
|  | } | 
|  | for _, xt := range r.extensionsByMessage[message] { | 
|  | if !f(xt) { | 
|  | return | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func typeName(t interface{}) string { | 
|  | switch t.(type) { | 
|  | case protoreflect.EnumType: | 
|  | return "enum" | 
|  | case protoreflect.MessageType: | 
|  | return "message" | 
|  | case protoreflect.ExtensionType: | 
|  | return "extension" | 
|  | default: | 
|  | return fmt.Sprintf("%T", t) | 
|  | } | 
|  | } | 
|  |  | 
|  | func amendErrorWithCaller(err error, prev, curr interface{}) error { | 
|  | prevPkg := goPackage(prev) | 
|  | currPkg := goPackage(curr) | 
|  | if prevPkg == "" || currPkg == "" || prevPkg == currPkg { | 
|  | return err | 
|  | } | 
|  | return errors.New("%s\n\tpreviously from: %q\n\tcurrently from:  %q", err, prevPkg, currPkg) | 
|  | } | 
|  |  | 
|  | func goPackage(v interface{}) string { | 
|  | switch d := v.(type) { | 
|  | case protoreflect.EnumType: | 
|  | v = d.Descriptor() | 
|  | case protoreflect.MessageType: | 
|  | v = d.Descriptor() | 
|  | case protoreflect.ExtensionType: | 
|  | v = d.TypeDescriptor() | 
|  | } | 
|  | if d, ok := v.(protoreflect.Descriptor); ok { | 
|  | v = d.ParentFile() | 
|  | } | 
|  | if d, ok := v.(interface{ GoPackagePath() string }); ok { | 
|  | return d.GoPackagePath() | 
|  | } | 
|  | return "" | 
|  | } |