mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-12-23 18:52:11 +00:00
acc333c40b
When GTS is running in a container runtime which has configured CPU or memory limits or under an init system that uses cgroups to impose CPU and memory limits the values the Go runtime sees for GOMAXPROCS and GOMEMLIMIT are still based on the host resources, not the cgroup. At least for the throttling middlewares which use GOMAXPROCS to configure their queue size, this can result in GTS running with values too big compared to the resources that will actuall be available to it. This introduces 2 dependencies which can pick up resource contraints from the current cgroup and tune the Go runtime accordingly. This should result in the different queues being appropriately sized and in general more predictable performance. These dependencies are a no-op on non-Linux systems or if running in a cgroup that doesn't set a limit on CPU or memory. The automatic tuning of GOMEMLIMIT can be disabled by either explicitly setting GOMEMLIMIT yourself or by setting AUTOMEMLIMIT=off. The automatic tuning of GOMAXPROCS can similarly be counteracted by setting GOMAXPROCS yourself.
611 lines
17 KiB
Go
611 lines
17 KiB
Go
// Protocol Buffers for Go with Gadgets
|
|
//
|
|
// Copyright (c) 2013, The GoGo Authors. All rights reserved.
|
|
// http://github.com/gogo/protobuf
|
|
//
|
|
// Go support for Protocol Buffers - Google's data interchange format
|
|
//
|
|
// Copyright 2010 The Go Authors. All rights reserved.
|
|
// https://github.com/golang/protobuf
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are
|
|
// met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above
|
|
// copyright notice, this list of conditions and the following disclaimer
|
|
// in the documentation and/or other materials provided with the
|
|
// distribution.
|
|
// * Neither the name of Google Inc. nor the names of its
|
|
// contributors may be used to endorse or promote products derived from
|
|
// this software without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
package proto
|
|
|
|
/*
|
|
* Routines for encoding data into the wire format for protocol buffers.
|
|
*/
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"reflect"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
const debug bool = false
|
|
|
|
// Constants that identify the encoding of a value on the wire.
|
|
const (
|
|
WireVarint = 0
|
|
WireFixed64 = 1
|
|
WireBytes = 2
|
|
WireStartGroup = 3
|
|
WireEndGroup = 4
|
|
WireFixed32 = 5
|
|
)
|
|
|
|
// tagMap is an optimization over map[int]int for typical protocol buffer
|
|
// use-cases. Encoded protocol buffers are often in tag order with small tag
|
|
// numbers.
|
|
type tagMap struct {
|
|
fastTags []int
|
|
slowTags map[int]int
|
|
}
|
|
|
|
// tagMapFastLimit is the upper bound on the tag number that will be stored in
|
|
// the tagMap slice rather than its map.
|
|
const tagMapFastLimit = 1024
|
|
|
|
func (p *tagMap) get(t int) (int, bool) {
|
|
if t > 0 && t < tagMapFastLimit {
|
|
if t >= len(p.fastTags) {
|
|
return 0, false
|
|
}
|
|
fi := p.fastTags[t]
|
|
return fi, fi >= 0
|
|
}
|
|
fi, ok := p.slowTags[t]
|
|
return fi, ok
|
|
}
|
|
|
|
func (p *tagMap) put(t int, fi int) {
|
|
if t > 0 && t < tagMapFastLimit {
|
|
for len(p.fastTags) < t+1 {
|
|
p.fastTags = append(p.fastTags, -1)
|
|
}
|
|
p.fastTags[t] = fi
|
|
return
|
|
}
|
|
if p.slowTags == nil {
|
|
p.slowTags = make(map[int]int)
|
|
}
|
|
p.slowTags[t] = fi
|
|
}
|
|
|
|
// StructProperties represents properties for all the fields of a struct.
|
|
// decoderTags and decoderOrigNames should only be used by the decoder.
|
|
type StructProperties struct {
|
|
Prop []*Properties // properties for each field
|
|
reqCount int // required count
|
|
decoderTags tagMap // map from proto tag to struct field number
|
|
decoderOrigNames map[string]int // map from original name to struct field number
|
|
order []int // list of struct field numbers in tag order
|
|
|
|
// OneofTypes contains information about the oneof fields in this message.
|
|
// It is keyed by the original name of a field.
|
|
OneofTypes map[string]*OneofProperties
|
|
}
|
|
|
|
// OneofProperties represents information about a specific field in a oneof.
|
|
type OneofProperties struct {
|
|
Type reflect.Type // pointer to generated struct type for this oneof field
|
|
Field int // struct field number of the containing oneof in the message
|
|
Prop *Properties
|
|
}
|
|
|
|
// Implement the sorting interface so we can sort the fields in tag order, as recommended by the spec.
|
|
// See encode.go, (*Buffer).enc_struct.
|
|
|
|
func (sp *StructProperties) Len() int { return len(sp.order) }
|
|
func (sp *StructProperties) Less(i, j int) bool {
|
|
return sp.Prop[sp.order[i]].Tag < sp.Prop[sp.order[j]].Tag
|
|
}
|
|
func (sp *StructProperties) Swap(i, j int) { sp.order[i], sp.order[j] = sp.order[j], sp.order[i] }
|
|
|
|
// Properties represents the protocol-specific behavior of a single struct field.
|
|
type Properties struct {
|
|
Name string // name of the field, for error messages
|
|
OrigName string // original name before protocol compiler (always set)
|
|
JSONName string // name to use for JSON; determined by protoc
|
|
Wire string
|
|
WireType int
|
|
Tag int
|
|
Required bool
|
|
Optional bool
|
|
Repeated bool
|
|
Packed bool // relevant for repeated primitives only
|
|
Enum string // set for enum types only
|
|
proto3 bool // whether this is known to be a proto3 field
|
|
oneof bool // whether this is a oneof field
|
|
|
|
Default string // default value
|
|
HasDefault bool // whether an explicit default was provided
|
|
CustomType string
|
|
CastType string
|
|
StdTime bool
|
|
StdDuration bool
|
|
WktPointer bool
|
|
|
|
stype reflect.Type // set for struct types only
|
|
ctype reflect.Type // set for custom types only
|
|
sprop *StructProperties // set for struct types only
|
|
|
|
mtype reflect.Type // set for map types only
|
|
MapKeyProp *Properties // set for map types only
|
|
MapValProp *Properties // set for map types only
|
|
}
|
|
|
|
// String formats the properties in the protobuf struct field tag style.
|
|
func (p *Properties) String() string {
|
|
s := p.Wire
|
|
s += ","
|
|
s += strconv.Itoa(p.Tag)
|
|
if p.Required {
|
|
s += ",req"
|
|
}
|
|
if p.Optional {
|
|
s += ",opt"
|
|
}
|
|
if p.Repeated {
|
|
s += ",rep"
|
|
}
|
|
if p.Packed {
|
|
s += ",packed"
|
|
}
|
|
s += ",name=" + p.OrigName
|
|
if p.JSONName != p.OrigName {
|
|
s += ",json=" + p.JSONName
|
|
}
|
|
if p.proto3 {
|
|
s += ",proto3"
|
|
}
|
|
if p.oneof {
|
|
s += ",oneof"
|
|
}
|
|
if len(p.Enum) > 0 {
|
|
s += ",enum=" + p.Enum
|
|
}
|
|
if p.HasDefault {
|
|
s += ",def=" + p.Default
|
|
}
|
|
return s
|
|
}
|
|
|
|
// Parse populates p by parsing a string in the protobuf struct field tag style.
|
|
func (p *Properties) Parse(s string) {
|
|
// "bytes,49,opt,name=foo,def=hello!"
|
|
fields := strings.Split(s, ",") // breaks def=, but handled below.
|
|
if len(fields) < 2 {
|
|
log.Printf("proto: tag has too few fields: %q", s)
|
|
return
|
|
}
|
|
|
|
p.Wire = fields[0]
|
|
switch p.Wire {
|
|
case "varint":
|
|
p.WireType = WireVarint
|
|
case "fixed32":
|
|
p.WireType = WireFixed32
|
|
case "fixed64":
|
|
p.WireType = WireFixed64
|
|
case "zigzag32":
|
|
p.WireType = WireVarint
|
|
case "zigzag64":
|
|
p.WireType = WireVarint
|
|
case "bytes", "group":
|
|
p.WireType = WireBytes
|
|
// no numeric converter for non-numeric types
|
|
default:
|
|
log.Printf("proto: tag has unknown wire type: %q", s)
|
|
return
|
|
}
|
|
|
|
var err error
|
|
p.Tag, err = strconv.Atoi(fields[1])
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
outer:
|
|
for i := 2; i < len(fields); i++ {
|
|
f := fields[i]
|
|
switch {
|
|
case f == "req":
|
|
p.Required = true
|
|
case f == "opt":
|
|
p.Optional = true
|
|
case f == "rep":
|
|
p.Repeated = true
|
|
case f == "packed":
|
|
p.Packed = true
|
|
case strings.HasPrefix(f, "name="):
|
|
p.OrigName = f[5:]
|
|
case strings.HasPrefix(f, "json="):
|
|
p.JSONName = f[5:]
|
|
case strings.HasPrefix(f, "enum="):
|
|
p.Enum = f[5:]
|
|
case f == "proto3":
|
|
p.proto3 = true
|
|
case f == "oneof":
|
|
p.oneof = true
|
|
case strings.HasPrefix(f, "def="):
|
|
p.HasDefault = true
|
|
p.Default = f[4:] // rest of string
|
|
if i+1 < len(fields) {
|
|
// Commas aren't escaped, and def is always last.
|
|
p.Default += "," + strings.Join(fields[i+1:], ",")
|
|
break outer
|
|
}
|
|
case strings.HasPrefix(f, "embedded="):
|
|
p.OrigName = strings.Split(f, "=")[1]
|
|
case strings.HasPrefix(f, "customtype="):
|
|
p.CustomType = strings.Split(f, "=")[1]
|
|
case strings.HasPrefix(f, "casttype="):
|
|
p.CastType = strings.Split(f, "=")[1]
|
|
case f == "stdtime":
|
|
p.StdTime = true
|
|
case f == "stdduration":
|
|
p.StdDuration = true
|
|
case f == "wktptr":
|
|
p.WktPointer = true
|
|
}
|
|
}
|
|
}
|
|
|
|
var protoMessageType = reflect.TypeOf((*Message)(nil)).Elem()
|
|
|
|
// setFieldProps initializes the field properties for submessages and maps.
|
|
func (p *Properties) setFieldProps(typ reflect.Type, f *reflect.StructField, lockGetProp bool) {
|
|
isMap := typ.Kind() == reflect.Map
|
|
if len(p.CustomType) > 0 && !isMap {
|
|
p.ctype = typ
|
|
p.setTag(lockGetProp)
|
|
return
|
|
}
|
|
if p.StdTime && !isMap {
|
|
p.setTag(lockGetProp)
|
|
return
|
|
}
|
|
if p.StdDuration && !isMap {
|
|
p.setTag(lockGetProp)
|
|
return
|
|
}
|
|
if p.WktPointer && !isMap {
|
|
p.setTag(lockGetProp)
|
|
return
|
|
}
|
|
switch t1 := typ; t1.Kind() {
|
|
case reflect.Struct:
|
|
p.stype = typ
|
|
case reflect.Ptr:
|
|
if t1.Elem().Kind() == reflect.Struct {
|
|
p.stype = t1.Elem()
|
|
}
|
|
case reflect.Slice:
|
|
switch t2 := t1.Elem(); t2.Kind() {
|
|
case reflect.Ptr:
|
|
switch t3 := t2.Elem(); t3.Kind() {
|
|
case reflect.Struct:
|
|
p.stype = t3
|
|
}
|
|
case reflect.Struct:
|
|
p.stype = t2
|
|
}
|
|
|
|
case reflect.Map:
|
|
|
|
p.mtype = t1
|
|
p.MapKeyProp = &Properties{}
|
|
p.MapKeyProp.init(reflect.PtrTo(p.mtype.Key()), "Key", f.Tag.Get("protobuf_key"), nil, lockGetProp)
|
|
p.MapValProp = &Properties{}
|
|
vtype := p.mtype.Elem()
|
|
if vtype.Kind() != reflect.Ptr && vtype.Kind() != reflect.Slice {
|
|
// The value type is not a message (*T) or bytes ([]byte),
|
|
// so we need encoders for the pointer to this type.
|
|
vtype = reflect.PtrTo(vtype)
|
|
}
|
|
|
|
p.MapValProp.CustomType = p.CustomType
|
|
p.MapValProp.StdDuration = p.StdDuration
|
|
p.MapValProp.StdTime = p.StdTime
|
|
p.MapValProp.WktPointer = p.WktPointer
|
|
p.MapValProp.init(vtype, "Value", f.Tag.Get("protobuf_val"), nil, lockGetProp)
|
|
}
|
|
p.setTag(lockGetProp)
|
|
}
|
|
|
|
func (p *Properties) setTag(lockGetProp bool) {
|
|
if p.stype != nil {
|
|
if lockGetProp {
|
|
p.sprop = GetProperties(p.stype)
|
|
} else {
|
|
p.sprop = getPropertiesLocked(p.stype)
|
|
}
|
|
}
|
|
}
|
|
|
|
var (
|
|
marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
|
|
)
|
|
|
|
// Init populates the properties from a protocol buffer struct tag.
|
|
func (p *Properties) Init(typ reflect.Type, name, tag string, f *reflect.StructField) {
|
|
p.init(typ, name, tag, f, true)
|
|
}
|
|
|
|
func (p *Properties) init(typ reflect.Type, name, tag string, f *reflect.StructField, lockGetProp bool) {
|
|
// "bytes,49,opt,def=hello!"
|
|
p.Name = name
|
|
p.OrigName = name
|
|
if tag == "" {
|
|
return
|
|
}
|
|
p.Parse(tag)
|
|
p.setFieldProps(typ, f, lockGetProp)
|
|
}
|
|
|
|
var (
|
|
propertiesMu sync.RWMutex
|
|
propertiesMap = make(map[reflect.Type]*StructProperties)
|
|
)
|
|
|
|
// GetProperties returns the list of properties for the type represented by t.
|
|
// t must represent a generated struct type of a protocol message.
|
|
func GetProperties(t reflect.Type) *StructProperties {
|
|
if t.Kind() != reflect.Struct {
|
|
panic("proto: type must have kind struct")
|
|
}
|
|
|
|
// Most calls to GetProperties in a long-running program will be
|
|
// retrieving details for types we have seen before.
|
|
propertiesMu.RLock()
|
|
sprop, ok := propertiesMap[t]
|
|
propertiesMu.RUnlock()
|
|
if ok {
|
|
return sprop
|
|
}
|
|
|
|
propertiesMu.Lock()
|
|
sprop = getPropertiesLocked(t)
|
|
propertiesMu.Unlock()
|
|
return sprop
|
|
}
|
|
|
|
type (
|
|
oneofFuncsIface interface {
|
|
XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{})
|
|
}
|
|
oneofWrappersIface interface {
|
|
XXX_OneofWrappers() []interface{}
|
|
}
|
|
)
|
|
|
|
// getPropertiesLocked requires that propertiesMu is held.
|
|
func getPropertiesLocked(t reflect.Type) *StructProperties {
|
|
if prop, ok := propertiesMap[t]; ok {
|
|
return prop
|
|
}
|
|
|
|
prop := new(StructProperties)
|
|
// in case of recursive protos, fill this in now.
|
|
propertiesMap[t] = prop
|
|
|
|
// build properties
|
|
prop.Prop = make([]*Properties, t.NumField())
|
|
prop.order = make([]int, t.NumField())
|
|
|
|
isOneofMessage := false
|
|
for i := 0; i < t.NumField(); i++ {
|
|
f := t.Field(i)
|
|
p := new(Properties)
|
|
name := f.Name
|
|
p.init(f.Type, name, f.Tag.Get("protobuf"), &f, false)
|
|
|
|
oneof := f.Tag.Get("protobuf_oneof") // special case
|
|
if oneof != "" {
|
|
isOneofMessage = true
|
|
// Oneof fields don't use the traditional protobuf tag.
|
|
p.OrigName = oneof
|
|
}
|
|
prop.Prop[i] = p
|
|
prop.order[i] = i
|
|
if debug {
|
|
print(i, " ", f.Name, " ", t.String(), " ")
|
|
if p.Tag > 0 {
|
|
print(p.String())
|
|
}
|
|
print("\n")
|
|
}
|
|
}
|
|
|
|
// Re-order prop.order.
|
|
sort.Sort(prop)
|
|
|
|
if isOneofMessage {
|
|
var oots []interface{}
|
|
switch m := reflect.Zero(reflect.PtrTo(t)).Interface().(type) {
|
|
case oneofFuncsIface:
|
|
_, _, _, oots = m.XXX_OneofFuncs()
|
|
case oneofWrappersIface:
|
|
oots = m.XXX_OneofWrappers()
|
|
}
|
|
if len(oots) > 0 {
|
|
// Interpret oneof metadata.
|
|
prop.OneofTypes = make(map[string]*OneofProperties)
|
|
for _, oot := range oots {
|
|
oop := &OneofProperties{
|
|
Type: reflect.ValueOf(oot).Type(), // *T
|
|
Prop: new(Properties),
|
|
}
|
|
sft := oop.Type.Elem().Field(0)
|
|
oop.Prop.Name = sft.Name
|
|
oop.Prop.Parse(sft.Tag.Get("protobuf"))
|
|
// There will be exactly one interface field that
|
|
// this new value is assignable to.
|
|
for i := 0; i < t.NumField(); i++ {
|
|
f := t.Field(i)
|
|
if f.Type.Kind() != reflect.Interface {
|
|
continue
|
|
}
|
|
if !oop.Type.AssignableTo(f.Type) {
|
|
continue
|
|
}
|
|
oop.Field = i
|
|
break
|
|
}
|
|
prop.OneofTypes[oop.Prop.OrigName] = oop
|
|
}
|
|
}
|
|
}
|
|
|
|
// build required counts
|
|
// build tags
|
|
reqCount := 0
|
|
prop.decoderOrigNames = make(map[string]int)
|
|
for i, p := range prop.Prop {
|
|
if strings.HasPrefix(p.Name, "XXX_") {
|
|
// Internal fields should not appear in tags/origNames maps.
|
|
// They are handled specially when encoding and decoding.
|
|
continue
|
|
}
|
|
if p.Required {
|
|
reqCount++
|
|
}
|
|
prop.decoderTags.put(p.Tag, i)
|
|
prop.decoderOrigNames[p.OrigName] = i
|
|
}
|
|
prop.reqCount = reqCount
|
|
|
|
return prop
|
|
}
|
|
|
|
// A global registry of enum types.
|
|
// The generated code will register the generated maps by calling RegisterEnum.
|
|
|
|
var enumValueMaps = make(map[string]map[string]int32)
|
|
var enumStringMaps = make(map[string]map[int32]string)
|
|
|
|
// RegisterEnum is called from the generated code to install the enum descriptor
|
|
// maps into the global table to aid parsing text format protocol buffers.
|
|
func RegisterEnum(typeName string, unusedNameMap map[int32]string, valueMap map[string]int32) {
|
|
if _, ok := enumValueMaps[typeName]; ok {
|
|
panic("proto: duplicate enum registered: " + typeName)
|
|
}
|
|
enumValueMaps[typeName] = valueMap
|
|
if _, ok := enumStringMaps[typeName]; ok {
|
|
panic("proto: duplicate enum registered: " + typeName)
|
|
}
|
|
enumStringMaps[typeName] = unusedNameMap
|
|
}
|
|
|
|
// EnumValueMap returns the mapping from names to integers of the
|
|
// enum type enumType, or a nil if not found.
|
|
func EnumValueMap(enumType string) map[string]int32 {
|
|
return enumValueMaps[enumType]
|
|
}
|
|
|
|
// A registry of all linked message types.
|
|
// The string is a fully-qualified proto name ("pkg.Message").
|
|
var (
|
|
protoTypedNils = make(map[string]Message) // a map from proto names to typed nil pointers
|
|
protoMapTypes = make(map[string]reflect.Type) // a map from proto names to map types
|
|
revProtoTypes = make(map[reflect.Type]string)
|
|
)
|
|
|
|
// RegisterType is called from generated code and maps from the fully qualified
|
|
// proto name to the type (pointer to struct) of the protocol buffer.
|
|
func RegisterType(x Message, name string) {
|
|
if _, ok := protoTypedNils[name]; ok {
|
|
// TODO: Some day, make this a panic.
|
|
log.Printf("proto: duplicate proto type registered: %s", name)
|
|
return
|
|
}
|
|
t := reflect.TypeOf(x)
|
|
if v := reflect.ValueOf(x); v.Kind() == reflect.Ptr && v.Pointer() == 0 {
|
|
// Generated code always calls RegisterType with nil x.
|
|
// This check is just for extra safety.
|
|
protoTypedNils[name] = x
|
|
} else {
|
|
protoTypedNils[name] = reflect.Zero(t).Interface().(Message)
|
|
}
|
|
revProtoTypes[t] = name
|
|
}
|
|
|
|
// RegisterMapType is called from generated code and maps from the fully qualified
|
|
// proto name to the native map type of the proto map definition.
|
|
func RegisterMapType(x interface{}, name string) {
|
|
if reflect.TypeOf(x).Kind() != reflect.Map {
|
|
panic(fmt.Sprintf("RegisterMapType(%T, %q); want map", x, name))
|
|
}
|
|
if _, ok := protoMapTypes[name]; ok {
|
|
log.Printf("proto: duplicate proto type registered: %s", name)
|
|
return
|
|
}
|
|
t := reflect.TypeOf(x)
|
|
protoMapTypes[name] = t
|
|
revProtoTypes[t] = name
|
|
}
|
|
|
|
// MessageName returns the fully-qualified proto name for the given message type.
|
|
func MessageName(x Message) string {
|
|
type xname interface {
|
|
XXX_MessageName() string
|
|
}
|
|
if m, ok := x.(xname); ok {
|
|
return m.XXX_MessageName()
|
|
}
|
|
return revProtoTypes[reflect.TypeOf(x)]
|
|
}
|
|
|
|
// MessageType returns the message type (pointer to struct) for a named message.
|
|
// The type is not guaranteed to implement proto.Message if the name refers to a
|
|
// map entry.
|
|
func MessageType(name string) reflect.Type {
|
|
if t, ok := protoTypedNils[name]; ok {
|
|
return reflect.TypeOf(t)
|
|
}
|
|
return protoMapTypes[name]
|
|
}
|
|
|
|
// A registry of all linked proto files.
|
|
var (
|
|
protoFiles = make(map[string][]byte) // file name => fileDescriptor
|
|
)
|
|
|
|
// RegisterFile is called from generated code and maps from the
|
|
// full file name of a .proto file to its compressed FileDescriptorProto.
|
|
func RegisterFile(filename string, fileDescriptor []byte) {
|
|
protoFiles[filename] = fileDescriptor
|
|
}
|
|
|
|
// FileDescriptor returns the compressed FileDescriptorProto for a .proto file.
|
|
func FileDescriptor(filename string) []byte { return protoFiles[filename] }
|