| // Package internal contains some code that should not be exported but needs to |
| // be shared across more than one of the protoreflect sub-packages. |
| package internal |
| |
| import ( |
| "bytes" |
| "compress/gzip" |
| "fmt" |
| "io/ioutil" |
| |
| "github.com/golang/protobuf/proto" |
| dpb "github.com/golang/protobuf/protoc-gen-go/descriptor" |
| ) |
| |
| // TODO: replace this alias configuration with desc.RegisterImportPath? |
| |
| // StdFileAliases are the standard protos included with protoc, but older versions of |
| // their respective packages registered them using incorrect paths. |
| var StdFileAliases = map[string]string{ |
| // Files for the github.com/golang/protobuf/ptypes package at one point were |
| // registered using the path where the proto files are mirrored in GOPATH, |
| // inside the golang/protobuf repo. |
| // (Fixed as of https://github.com/golang/protobuf/pull/412) |
| "google/protobuf/any.proto": "github.com/golang/protobuf/ptypes/any/any.proto", |
| "google/protobuf/duration.proto": "github.com/golang/protobuf/ptypes/duration/duration.proto", |
| "google/protobuf/empty.proto": "github.com/golang/protobuf/ptypes/empty/empty.proto", |
| "google/protobuf/struct.proto": "github.com/golang/protobuf/ptypes/struct/struct.proto", |
| "google/protobuf/timestamp.proto": "github.com/golang/protobuf/ptypes/timestamp/timestamp.proto", |
| "google/protobuf/wrappers.proto": "github.com/golang/protobuf/ptypes/wrappers/wrappers.proto", |
| // Files for the google.golang.org/genproto/protobuf package at one point |
| // were registered with an anomalous "src/" prefix. |
| // (Fixed as of https://github.com/google/go-genproto/pull/31) |
| "google/protobuf/api.proto": "src/google/protobuf/api.proto", |
| "google/protobuf/field_mask.proto": "src/google/protobuf/field_mask.proto", |
| "google/protobuf/source_context.proto": "src/google/protobuf/source_context.proto", |
| "google/protobuf/type.proto": "src/google/protobuf/type.proto", |
| |
| // Other standard files (descriptor.proto and compiler/plugin.proto) are |
| // registered correctly, so we don't need rules for them here. |
| } |
| |
| func init() { |
| // We provide aliasing in both directions, to support files with the |
| // proper import path linked against older versions of the generated |
| // files AND files that used the aliased import path but linked against |
| // newer versions of the generated files (which register with the |
| // correct path). |
| |
| // Get all files defined above |
| keys := make([]string, 0, len(StdFileAliases)) |
| for k := range StdFileAliases { |
| keys = append(keys, k) |
| } |
| // And add inverse mappings |
| for _, k := range keys { |
| alias := StdFileAliases[k] |
| StdFileAliases[alias] = k |
| } |
| } |
| |
| type ErrNoSuchFile string |
| |
| func (e ErrNoSuchFile) Error() string { |
| return fmt.Sprintf("no such file: %q", string(e)) |
| } |
| |
| // LoadFileDescriptor loads a registered descriptor and decodes it. If the given |
| // name cannot be loaded but is a known standard name, an alias will be tried, |
| // so the standard files can be loaded even if linked against older "known bad" |
| // versions of packages. |
| func LoadFileDescriptor(file string) (*dpb.FileDescriptorProto, error) { |
| fdb := proto.FileDescriptor(file) |
| aliased := false |
| if fdb == nil { |
| var ok bool |
| alias, ok := StdFileAliases[file] |
| if ok { |
| aliased = true |
| if fdb = proto.FileDescriptor(alias); fdb == nil { |
| return nil, ErrNoSuchFile(file) |
| } |
| } else { |
| return nil, ErrNoSuchFile(file) |
| } |
| } |
| |
| fd, err := DecodeFileDescriptor(file, fdb) |
| if err != nil { |
| return nil, err |
| } |
| |
| if aliased { |
| // the file descriptor will have the alias used to load it, but |
| // we need it to have the specified name in order to link it |
| fd.Name = proto.String(file) |
| } |
| |
| return fd, nil |
| } |
| |
| // DecodeFileDescriptor decodes the bytes of a registered file descriptor. |
| // Registered file descriptors are first "proto encoded" (e.g. binary format |
| // for the descriptor protos) and then gzipped. So this function gunzips and |
| // then unmarshals into a descriptor proto. |
| func DecodeFileDescriptor(element string, fdb []byte) (*dpb.FileDescriptorProto, error) { |
| raw, err := decompress(fdb) |
| if err != nil { |
| return nil, fmt.Errorf("failed to decompress %q descriptor: %v", element, err) |
| } |
| fd := dpb.FileDescriptorProto{} |
| if err := proto.Unmarshal(raw, &fd); err != nil { |
| return nil, fmt.Errorf("bad descriptor for %q: %v", element, err) |
| } |
| return &fd, nil |
| } |
| |
| func decompress(b []byte) ([]byte, error) { |
| r, err := gzip.NewReader(bytes.NewReader(b)) |
| if err != nil { |
| return nil, fmt.Errorf("bad gzipped descriptor: %v", err) |
| } |
| out, err := ioutil.ReadAll(r) |
| if err != nil { |
| return nil, fmt.Errorf("bad gzipped descriptor: %v", err) |
| } |
| return out, nil |
| } |