package proto
import (
"bytes"
"compress/gzip"
"fmt"
"io/ioutil"
"reflect"
"strings"
"sync"
"google.golang.org/protobuf/reflect/protodesc"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
"google.golang.org/protobuf/runtime/protoimpl"
)
type filePath = string
type fileDescGZIP = []byte
var fileCache sync .Map
func RegisterFile (s filePath , d fileDescGZIP ) {
zr , err := gzip .NewReader (bytes .NewReader (d ))
if err != nil {
panic (fmt .Sprintf ("proto: invalid compressed file descriptor: %v" , err ))
}
b , err := ioutil .ReadAll (zr )
if err != nil {
panic (fmt .Sprintf ("proto: invalid compressed file descriptor: %v" , err ))
}
protoimpl .DescBuilder {RawDescriptor : b }.Build ()
fileCache .Store (s , d )
}
func FileDescriptor (s filePath ) fileDescGZIP {
if v , ok := fileCache .Load (s ); ok {
return v .(fileDescGZIP )
}
var b []byte
if fd , _ := protoregistry .GlobalFiles .FindFileByPath (s ); fd != nil {
b , _ = Marshal (protodesc .ToFileDescriptorProto (fd ))
}
if len (b ) > 0 {
v , _ := fileCache .LoadOrStore (s , protoimpl .X .CompressGZIP (b ))
return v .(fileDescGZIP )
}
return nil
}
type enumName = string
type enumsByName = map [string ]int32
type enumsByNumber = map [int32 ]string
var enumCache sync .Map
var numFilesCache sync .Map
func RegisterEnum (s enumName , _ enumsByNumber , m enumsByName ) {
if _ , ok := enumCache .Load (s ); ok {
panic ("proto: duplicate enum registered: " + s )
}
enumCache .Store (s , m )
}
func EnumValueMap (s enumName ) enumsByName {
if v , ok := enumCache .Load (s ); ok {
return v .(enumsByName )
}
var protoPkg protoreflect .FullName
if i := strings .LastIndexByte (s , '.' ); i >= 0 {
protoPkg = protoreflect .FullName (s [:i ])
}
v , _ := numFilesCache .Load (protoPkg )
numFiles , _ := v .(int )
if protoregistry .GlobalFiles .NumFilesByPackage (protoPkg ) == numFiles {
return nil
}
numFiles = 0
protoregistry .GlobalFiles .RangeFilesByPackage (protoPkg , func (fd protoreflect .FileDescriptor ) bool {
walkEnums (fd , func (ed protoreflect .EnumDescriptor ) {
name := protoimpl .X .LegacyEnumName (ed )
if _ , ok := enumCache .Load (name ); !ok {
m := make (enumsByName )
evs := ed .Values ()
for i := evs .Len () - 1 ; i >= 0 ; i -- {
ev := evs .Get (i )
m [string (ev .Name ())] = int32 (ev .Number ())
}
enumCache .LoadOrStore (name , m )
}
})
numFiles ++
return true
})
numFilesCache .Store (protoPkg , numFiles )
if v , ok := enumCache .Load (s ); ok {
return v .(enumsByName )
}
return nil
}
func walkEnums(d interface {
Enums () protoreflect .EnumDescriptors
Messages () protoreflect .MessageDescriptors
}, f func (protoreflect .EnumDescriptor )) {
eds := d .Enums ()
for i := eds .Len () - 1 ; i >= 0 ; i -- {
f (eds .Get (i ))
}
mds := d .Messages ()
for i := mds .Len () - 1 ; i >= 0 ; i -- {
walkEnums (mds .Get (i ), f )
}
}
type messageName = string
var messageTypeCache sync .Map
func RegisterType (m Message , s messageName ) {
mt := protoimpl .X .LegacyMessageTypeOf (m , protoreflect .FullName (s ))
if err := protoregistry .GlobalTypes .RegisterMessage (mt ); err != nil {
panic (err )
}
messageTypeCache .Store (s , reflect .TypeOf (m ))
}
func RegisterMapType (m interface {}, s messageName ) {
t := reflect .TypeOf (m )
if t .Kind () != reflect .Map {
panic (fmt .Sprintf ("invalid map kind: %v" , t ))
}
if _ , ok := messageTypeCache .Load (s ); ok {
panic (fmt .Errorf ("proto: duplicate proto message registered: %s" , s ))
}
messageTypeCache .Store (s , t )
}
func MessageType (s messageName ) reflect .Type {
if v , ok := messageTypeCache .Load (s ); ok {
return v .(reflect .Type )
}
var t reflect .Type
if mt , _ := protoregistry .GlobalTypes .FindMessageByName (protoreflect .FullName (s )); mt != nil {
t = messageGoType (mt )
}
if t == nil {
d , _ := protoregistry .GlobalFiles .FindDescriptorByName (protoreflect .FullName (s ))
if md , _ := d .(protoreflect .MessageDescriptor ); md != nil && md .IsMapEntry () {
kt := goTypeForField (md .Fields ().ByNumber (1 ))
vt := goTypeForField (md .Fields ().ByNumber (2 ))
t = reflect .MapOf (kt , vt )
}
}
if t != nil {
v , _ := messageTypeCache .LoadOrStore (s , t )
return v .(reflect .Type )
}
return nil
}
func goTypeForField(fd protoreflect .FieldDescriptor ) reflect .Type {
switch k := fd .Kind (); k {
case protoreflect .EnumKind :
if et , _ := protoregistry .GlobalTypes .FindEnumByName (fd .Enum ().FullName ()); et != nil {
return enumGoType (et )
}
return reflect .TypeOf (protoreflect .EnumNumber (0 ))
case protoreflect .MessageKind , protoreflect .GroupKind :
if mt , _ := protoregistry .GlobalTypes .FindMessageByName (fd .Message ().FullName ()); mt != nil {
return messageGoType (mt )
}
return reflect .TypeOf ((*protoreflect .Message )(nil )).Elem ()
default :
return reflect .TypeOf (fd .Default ().Interface ())
}
}
func enumGoType(et protoreflect .EnumType ) reflect .Type {
return reflect .TypeOf (et .New (0 ))
}
func messageGoType(mt protoreflect .MessageType ) reflect .Type {
return reflect .TypeOf (MessageV1 (mt .Zero ().Interface ()))
}
func MessageName (m Message ) messageName {
if m == nil {
return ""
}
if m , ok := m .(interface { XXX_MessageName () messageName }); ok {
return m .XXX_MessageName ()
}
return messageName (protoimpl .X .MessageDescriptorOf (m ).FullName ())
}
func RegisterExtension (d *ExtensionDesc ) {
if err := protoregistry .GlobalTypes .RegisterExtension (d ); err != nil {
panic (err )
}
}
type extensionsByNumber = map [int32 ]*ExtensionDesc
var extensionCache sync .Map
func RegisteredExtensions (m Message ) extensionsByNumber {
s := MessageName (m )
v , _ := extensionCache .Load (s )
xs , _ := v .(extensionsByNumber )
if protoregistry .GlobalTypes .NumExtensionsByMessage (protoreflect .FullName (s )) == len (xs ) {
return xs
}
xs = make (extensionsByNumber )
protoregistry .GlobalTypes .RangeExtensionsByMessage (protoreflect .FullName (s ), func (xt protoreflect .ExtensionType ) bool {
if xd , ok := xt .(*ExtensionDesc ); ok {
xs [int32 (xt .TypeDescriptor ().Number ())] = xd
} else {
}
return true
})
extensionCache .Store (s , xs )
return xs
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .