...

Source file src/github.com/golang/protobuf/proto/registry.go

Documentation: github.com/golang/protobuf/proto

     1  // Copyright 2019 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package proto
     6  
     7  import (
     8  	"bytes"
     9  	"compress/gzip"
    10  	"fmt"
    11  	"io/ioutil"
    12  	"reflect"
    13  	"strings"
    14  	"sync"
    15  
    16  	"google.golang.org/protobuf/reflect/protodesc"
    17  	"google.golang.org/protobuf/reflect/protoreflect"
    18  	"google.golang.org/protobuf/reflect/protoregistry"
    19  	"google.golang.org/protobuf/runtime/protoimpl"
    20  )
    21  
    22  // filePath is the path to the proto source file.
    23  type filePath = string // e.g., "google/protobuf/descriptor.proto"
    24  
    25  // fileDescGZIP is the compressed contents of the encoded FileDescriptorProto.
    26  type fileDescGZIP = []byte
    27  
    28  var fileCache sync.Map // map[filePath]fileDescGZIP
    29  
    30  // RegisterFile is called from generated code to register the compressed
    31  // FileDescriptorProto with the file path for a proto source file.
    32  //
    33  // Deprecated: Use protoregistry.GlobalFiles.RegisterFile instead.
    34  func RegisterFile(s filePath, d fileDescGZIP) {
    35  	// Decompress the descriptor.
    36  	zr, err := gzip.NewReader(bytes.NewReader(d))
    37  	if err != nil {
    38  		panic(fmt.Sprintf("proto: invalid compressed file descriptor: %v", err))
    39  	}
    40  	b, err := ioutil.ReadAll(zr)
    41  	if err != nil {
    42  		panic(fmt.Sprintf("proto: invalid compressed file descriptor: %v", err))
    43  	}
    44  
    45  	// Construct a protoreflect.FileDescriptor from the raw descriptor.
    46  	// Note that DescBuilder.Build automatically registers the constructed
    47  	// file descriptor with the v2 registry.
    48  	protoimpl.DescBuilder{RawDescriptor: b}.Build()
    49  
    50  	// Locally cache the raw descriptor form for the file.
    51  	fileCache.Store(s, d)
    52  }
    53  
    54  // FileDescriptor returns the compressed FileDescriptorProto given the file path
    55  // for a proto source file. It returns nil if not found.
    56  //
    57  // Deprecated: Use protoregistry.GlobalFiles.FindFileByPath instead.
    58  func FileDescriptor(s filePath) fileDescGZIP {
    59  	if v, ok := fileCache.Load(s); ok {
    60  		return v.(fileDescGZIP)
    61  	}
    62  
    63  	// Find the descriptor in the v2 registry.
    64  	var b []byte
    65  	if fd, _ := protoregistry.GlobalFiles.FindFileByPath(s); fd != nil {
    66  		b, _ = Marshal(protodesc.ToFileDescriptorProto(fd))
    67  	}
    68  
    69  	// Locally cache the raw descriptor form for the file.
    70  	if len(b) > 0 {
    71  		v, _ := fileCache.LoadOrStore(s, protoimpl.X.CompressGZIP(b))
    72  		return v.(fileDescGZIP)
    73  	}
    74  	return nil
    75  }
    76  
    77  // enumName is the name of an enum. For historical reasons, the enum name is
    78  // neither the full Go name nor the full protobuf name of the enum.
    79  // The name is the dot-separated combination of just the proto package that the
    80  // enum is declared within followed by the Go type name of the generated enum.
    81  type enumName = string // e.g., "my.proto.package.GoMessage_GoEnum"
    82  
    83  // enumsByName maps enum values by name to their numeric counterpart.
    84  type enumsByName = map[string]int32
    85  
    86  // enumsByNumber maps enum values by number to their name counterpart.
    87  type enumsByNumber = map[int32]string
    88  
    89  var enumCache sync.Map     // map[enumName]enumsByName
    90  var numFilesCache sync.Map // map[protoreflect.FullName]int
    91  
    92  // RegisterEnum is called from the generated code to register the mapping of
    93  // enum value names to enum numbers for the enum identified by s.
    94  //
    95  // Deprecated: Use protoregistry.GlobalTypes.RegisterEnum instead.
    96  func RegisterEnum(s enumName, _ enumsByNumber, m enumsByName) {
    97  	if _, ok := enumCache.Load(s); ok {
    98  		panic("proto: duplicate enum registered: " + s)
    99  	}
   100  	enumCache.Store(s, m)
   101  
   102  	// This does not forward registration to the v2 registry since this API
   103  	// lacks sufficient information to construct a complete v2 enum descriptor.
   104  }
   105  
   106  // EnumValueMap returns the mapping from enum value names to enum numbers for
   107  // the enum of the given name. It returns nil if not found.
   108  //
   109  // Deprecated: Use protoregistry.GlobalTypes.FindEnumByName instead.
   110  func EnumValueMap(s enumName) enumsByName {
   111  	if v, ok := enumCache.Load(s); ok {
   112  		return v.(enumsByName)
   113  	}
   114  
   115  	// Check whether the cache is stale. If the number of files in the current
   116  	// package differs, then it means that some enums may have been recently
   117  	// registered upstream that we do not know about.
   118  	var protoPkg protoreflect.FullName
   119  	if i := strings.LastIndexByte(s, '.'); i >= 0 {
   120  		protoPkg = protoreflect.FullName(s[:i])
   121  	}
   122  	v, _ := numFilesCache.Load(protoPkg)
   123  	numFiles, _ := v.(int)
   124  	if protoregistry.GlobalFiles.NumFilesByPackage(protoPkg) == numFiles {
   125  		return nil // cache is up-to-date; was not found earlier
   126  	}
   127  
   128  	// Update the enum cache for all enums declared in the given proto package.
   129  	numFiles = 0
   130  	protoregistry.GlobalFiles.RangeFilesByPackage(protoPkg, func(fd protoreflect.FileDescriptor) bool {
   131  		walkEnums(fd, func(ed protoreflect.EnumDescriptor) {
   132  			name := protoimpl.X.LegacyEnumName(ed)
   133  			if _, ok := enumCache.Load(name); !ok {
   134  				m := make(enumsByName)
   135  				evs := ed.Values()
   136  				for i := evs.Len() - 1; i >= 0; i-- {
   137  					ev := evs.Get(i)
   138  					m[string(ev.Name())] = int32(ev.Number())
   139  				}
   140  				enumCache.LoadOrStore(name, m)
   141  			}
   142  		})
   143  		numFiles++
   144  		return true
   145  	})
   146  	numFilesCache.Store(protoPkg, numFiles)
   147  
   148  	// Check cache again for enum map.
   149  	if v, ok := enumCache.Load(s); ok {
   150  		return v.(enumsByName)
   151  	}
   152  	return nil
   153  }
   154  
   155  // walkEnums recursively walks all enums declared in d.
   156  func walkEnums(d interface {
   157  	Enums() protoreflect.EnumDescriptors
   158  	Messages() protoreflect.MessageDescriptors
   159  }, f func(protoreflect.EnumDescriptor)) {
   160  	eds := d.Enums()
   161  	for i := eds.Len() - 1; i >= 0; i-- {
   162  		f(eds.Get(i))
   163  	}
   164  	mds := d.Messages()
   165  	for i := mds.Len() - 1; i >= 0; i-- {
   166  		walkEnums(mds.Get(i), f)
   167  	}
   168  }
   169  
   170  // messageName is the full name of protobuf message.
   171  type messageName = string
   172  
   173  var messageTypeCache sync.Map // map[messageName]reflect.Type
   174  
   175  // RegisterType is called from generated code to register the message Go type
   176  // for a message of the given name.
   177  //
   178  // Deprecated: Use protoregistry.GlobalTypes.RegisterMessage instead.
   179  func RegisterType(m Message, s messageName) {
   180  	mt := protoimpl.X.LegacyMessageTypeOf(m, protoreflect.FullName(s))
   181  	if err := protoregistry.GlobalTypes.RegisterMessage(mt); err != nil {
   182  		panic(err)
   183  	}
   184  	messageTypeCache.Store(s, reflect.TypeOf(m))
   185  }
   186  
   187  // RegisterMapType is called from generated code to register the Go map type
   188  // for a protobuf message representing a map entry.
   189  //
   190  // Deprecated: Do not use.
   191  func RegisterMapType(m interface{}, s messageName) {
   192  	t := reflect.TypeOf(m)
   193  	if t.Kind() != reflect.Map {
   194  		panic(fmt.Sprintf("invalid map kind: %v", t))
   195  	}
   196  	if _, ok := messageTypeCache.Load(s); ok {
   197  		panic(fmt.Errorf("proto: duplicate proto message registered: %s", s))
   198  	}
   199  	messageTypeCache.Store(s, t)
   200  }
   201  
   202  // MessageType returns the message type for a named message.
   203  // It returns nil if not found.
   204  //
   205  // Deprecated: Use protoregistry.GlobalTypes.FindMessageByName instead.
   206  func MessageType(s messageName) reflect.Type {
   207  	if v, ok := messageTypeCache.Load(s); ok {
   208  		return v.(reflect.Type)
   209  	}
   210  
   211  	// Derive the message type from the v2 registry.
   212  	var t reflect.Type
   213  	if mt, _ := protoregistry.GlobalTypes.FindMessageByName(protoreflect.FullName(s)); mt != nil {
   214  		t = messageGoType(mt)
   215  	}
   216  
   217  	// If we could not get a concrete type, it is possible that it is a
   218  	// pseudo-message for a map entry.
   219  	if t == nil {
   220  		d, _ := protoregistry.GlobalFiles.FindDescriptorByName(protoreflect.FullName(s))
   221  		if md, _ := d.(protoreflect.MessageDescriptor); md != nil && md.IsMapEntry() {
   222  			kt := goTypeForField(md.Fields().ByNumber(1))
   223  			vt := goTypeForField(md.Fields().ByNumber(2))
   224  			t = reflect.MapOf(kt, vt)
   225  		}
   226  	}
   227  
   228  	// Locally cache the message type for the given name.
   229  	if t != nil {
   230  		v, _ := messageTypeCache.LoadOrStore(s, t)
   231  		return v.(reflect.Type)
   232  	}
   233  	return nil
   234  }
   235  
   236  func goTypeForField(fd protoreflect.FieldDescriptor) reflect.Type {
   237  	switch k := fd.Kind(); k {
   238  	case protoreflect.EnumKind:
   239  		if et, _ := protoregistry.GlobalTypes.FindEnumByName(fd.Enum().FullName()); et != nil {
   240  			return enumGoType(et)
   241  		}
   242  		return reflect.TypeOf(protoreflect.EnumNumber(0))
   243  	case protoreflect.MessageKind, protoreflect.GroupKind:
   244  		if mt, _ := protoregistry.GlobalTypes.FindMessageByName(fd.Message().FullName()); mt != nil {
   245  			return messageGoType(mt)
   246  		}
   247  		return reflect.TypeOf((*protoreflect.Message)(nil)).Elem()
   248  	default:
   249  		return reflect.TypeOf(fd.Default().Interface())
   250  	}
   251  }
   252  
   253  func enumGoType(et protoreflect.EnumType) reflect.Type {
   254  	return reflect.TypeOf(et.New(0))
   255  }
   256  
   257  func messageGoType(mt protoreflect.MessageType) reflect.Type {
   258  	return reflect.TypeOf(MessageV1(mt.Zero().Interface()))
   259  }
   260  
   261  // MessageName returns the full protobuf name for the given message type.
   262  //
   263  // Deprecated: Use protoreflect.MessageDescriptor.FullName instead.
   264  func MessageName(m Message) messageName {
   265  	if m == nil {
   266  		return ""
   267  	}
   268  	if m, ok := m.(interface{ XXX_MessageName() messageName }); ok {
   269  		return m.XXX_MessageName()
   270  	}
   271  	return messageName(protoimpl.X.MessageDescriptorOf(m).FullName())
   272  }
   273  
   274  // RegisterExtension is called from the generated code to register
   275  // the extension descriptor.
   276  //
   277  // Deprecated: Use protoregistry.GlobalTypes.RegisterExtension instead.
   278  func RegisterExtension(d *ExtensionDesc) {
   279  	if err := protoregistry.GlobalTypes.RegisterExtension(d); err != nil {
   280  		panic(err)
   281  	}
   282  }
   283  
   284  type extensionsByNumber = map[int32]*ExtensionDesc
   285  
   286  var extensionCache sync.Map // map[messageName]extensionsByNumber
   287  
   288  // RegisteredExtensions returns a map of the registered extensions for the
   289  // provided protobuf message, indexed by the extension field number.
   290  //
   291  // Deprecated: Use protoregistry.GlobalTypes.RangeExtensionsByMessage instead.
   292  func RegisteredExtensions(m Message) extensionsByNumber {
   293  	// Check whether the cache is stale. If the number of extensions for
   294  	// the given message differs, then it means that some extensions were
   295  	// recently registered upstream that we do not know about.
   296  	s := MessageName(m)
   297  	v, _ := extensionCache.Load(s)
   298  	xs, _ := v.(extensionsByNumber)
   299  	if protoregistry.GlobalTypes.NumExtensionsByMessage(protoreflect.FullName(s)) == len(xs) {
   300  		return xs // cache is up-to-date
   301  	}
   302  
   303  	// Cache is stale, re-compute the extensions map.
   304  	xs = make(extensionsByNumber)
   305  	protoregistry.GlobalTypes.RangeExtensionsByMessage(protoreflect.FullName(s), func(xt protoreflect.ExtensionType) bool {
   306  		if xd, ok := xt.(*ExtensionDesc); ok {
   307  			xs[int32(xt.TypeDescriptor().Number())] = xd
   308  		} else {
   309  			// TODO: This implies that the protoreflect.ExtensionType is a
   310  			// custom type not generated by protoc-gen-go. We could try and
   311  			// convert the type to an ExtensionDesc.
   312  		}
   313  		return true
   314  	})
   315  	extensionCache.Store(s, xs)
   316  	return xs
   317  }
   318  

View as plain text