| // Copyright 2019 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 filedesc |
| |
| import ( |
| "fmt" |
| "math" |
| "sort" |
| "sync" |
| |
| "google.golang.org/protobuf/internal/genid" |
| |
| "google.golang.org/protobuf/encoding/protowire" |
| "google.golang.org/protobuf/internal/descfmt" |
| "google.golang.org/protobuf/internal/errors" |
| "google.golang.org/protobuf/internal/pragma" |
| "google.golang.org/protobuf/reflect/protoreflect" |
| pref "google.golang.org/protobuf/reflect/protoreflect" |
| ) |
| |
| type FileImports []pref.FileImport |
| |
| func (p *FileImports) Len() int { return len(*p) } |
| func (p *FileImports) Get(i int) pref.FileImport { return (*p)[i] } |
| func (p *FileImports) Format(s fmt.State, r rune) { descfmt.FormatList(s, r, p) } |
| func (p *FileImports) ProtoInternal(pragma.DoNotImplement) {} |
| |
| type Names struct { |
| List []pref.Name |
| once sync.Once |
| has map[pref.Name]int // protected by once |
| } |
| |
| func (p *Names) Len() int { return len(p.List) } |
| func (p *Names) Get(i int) pref.Name { return p.List[i] } |
| func (p *Names) Has(s pref.Name) bool { return p.lazyInit().has[s] > 0 } |
| func (p *Names) Format(s fmt.State, r rune) { descfmt.FormatList(s, r, p) } |
| func (p *Names) ProtoInternal(pragma.DoNotImplement) {} |
| func (p *Names) lazyInit() *Names { |
| p.once.Do(func() { |
| if len(p.List) > 0 { |
| p.has = make(map[pref.Name]int, len(p.List)) |
| for _, s := range p.List { |
| p.has[s] = p.has[s] + 1 |
| } |
| } |
| }) |
| return p |
| } |
| |
| // CheckValid reports any errors with the set of names with an error message |
| // that completes the sentence: "ranges is invalid because it has ..." |
| func (p *Names) CheckValid() error { |
| for s, n := range p.lazyInit().has { |
| switch { |
| case n > 1: |
| return errors.New("duplicate name: %q", s) |
| case false && !s.IsValid(): |
| // NOTE: The C++ implementation does not validate the identifier. |
| // See https://github.com/protocolbuffers/protobuf/issues/6335. |
| return errors.New("invalid name: %q", s) |
| } |
| } |
| return nil |
| } |
| |
| type EnumRanges struct { |
| List [][2]pref.EnumNumber // start inclusive; end inclusive |
| once sync.Once |
| sorted [][2]pref.EnumNumber // protected by once |
| } |
| |
| func (p *EnumRanges) Len() int { return len(p.List) } |
| func (p *EnumRanges) Get(i int) [2]pref.EnumNumber { return p.List[i] } |
| func (p *EnumRanges) Has(n pref.EnumNumber) bool { |
| for ls := p.lazyInit().sorted; len(ls) > 0; { |
| i := len(ls) / 2 |
| switch r := enumRange(ls[i]); { |
| case n < r.Start(): |
| ls = ls[:i] // search lower |
| case n > r.End(): |
| ls = ls[i+1:] // search upper |
| default: |
| return true |
| } |
| } |
| return false |
| } |
| func (p *EnumRanges) Format(s fmt.State, r rune) { descfmt.FormatList(s, r, p) } |
| func (p *EnumRanges) ProtoInternal(pragma.DoNotImplement) {} |
| func (p *EnumRanges) lazyInit() *EnumRanges { |
| p.once.Do(func() { |
| p.sorted = append(p.sorted, p.List...) |
| sort.Slice(p.sorted, func(i, j int) bool { |
| return p.sorted[i][0] < p.sorted[j][0] |
| }) |
| }) |
| return p |
| } |
| |
| // CheckValid reports any errors with the set of names with an error message |
| // that completes the sentence: "ranges is invalid because it has ..." |
| func (p *EnumRanges) CheckValid() error { |
| var rp enumRange |
| for i, r := range p.lazyInit().sorted { |
| r := enumRange(r) |
| switch { |
| case !(r.Start() <= r.End()): |
| return errors.New("invalid range: %v", r) |
| case !(rp.End() < r.Start()) && i > 0: |
| return errors.New("overlapping ranges: %v with %v", rp, r) |
| } |
| rp = r |
| } |
| return nil |
| } |
| |
| type enumRange [2]protoreflect.EnumNumber |
| |
| func (r enumRange) Start() protoreflect.EnumNumber { return r[0] } // inclusive |
| func (r enumRange) End() protoreflect.EnumNumber { return r[1] } // inclusive |
| func (r enumRange) String() string { |
| if r.Start() == r.End() { |
| return fmt.Sprintf("%d", r.Start()) |
| } |
| return fmt.Sprintf("%d to %d", r.Start(), r.End()) |
| } |
| |
| type FieldRanges struct { |
| List [][2]pref.FieldNumber // start inclusive; end exclusive |
| once sync.Once |
| sorted [][2]pref.FieldNumber // protected by once |
| } |
| |
| func (p *FieldRanges) Len() int { return len(p.List) } |
| func (p *FieldRanges) Get(i int) [2]pref.FieldNumber { return p.List[i] } |
| func (p *FieldRanges) Has(n pref.FieldNumber) bool { |
| for ls := p.lazyInit().sorted; len(ls) > 0; { |
| i := len(ls) / 2 |
| switch r := fieldRange(ls[i]); { |
| case n < r.Start(): |
| ls = ls[:i] // search lower |
| case n > r.End(): |
| ls = ls[i+1:] // search upper |
| default: |
| return true |
| } |
| } |
| return false |
| } |
| func (p *FieldRanges) Format(s fmt.State, r rune) { descfmt.FormatList(s, r, p) } |
| func (p *FieldRanges) ProtoInternal(pragma.DoNotImplement) {} |
| func (p *FieldRanges) lazyInit() *FieldRanges { |
| p.once.Do(func() { |
| p.sorted = append(p.sorted, p.List...) |
| sort.Slice(p.sorted, func(i, j int) bool { |
| return p.sorted[i][0] < p.sorted[j][0] |
| }) |
| }) |
| return p |
| } |
| |
| // CheckValid reports any errors with the set of ranges with an error message |
| // that completes the sentence: "ranges is invalid because it has ..." |
| func (p *FieldRanges) CheckValid(isMessageSet bool) error { |
| var rp fieldRange |
| for i, r := range p.lazyInit().sorted { |
| r := fieldRange(r) |
| switch { |
| case !isValidFieldNumber(r.Start(), isMessageSet): |
| return errors.New("invalid field number: %d", r.Start()) |
| case !isValidFieldNumber(r.End(), isMessageSet): |
| return errors.New("invalid field number: %d", r.End()) |
| case !(r.Start() <= r.End()): |
| return errors.New("invalid range: %v", r) |
| case !(rp.End() < r.Start()) && i > 0: |
| return errors.New("overlapping ranges: %v with %v", rp, r) |
| } |
| rp = r |
| } |
| return nil |
| } |
| |
| // isValidFieldNumber reports whether the field number is valid. |
| // Unlike the FieldNumber.IsValid method, it allows ranges that cover the |
| // reserved number range. |
| func isValidFieldNumber(n protoreflect.FieldNumber, isMessageSet bool) bool { |
| return protowire.MinValidNumber <= n && (n <= protowire.MaxValidNumber || isMessageSet) |
| } |
| |
| // CheckOverlap reports an error if p and q overlap. |
| func (p *FieldRanges) CheckOverlap(q *FieldRanges) error { |
| rps := p.lazyInit().sorted |
| rqs := q.lazyInit().sorted |
| for pi, qi := 0, 0; pi < len(rps) && qi < len(rqs); { |
| rp := fieldRange(rps[pi]) |
| rq := fieldRange(rqs[qi]) |
| if !(rp.End() < rq.Start() || rq.End() < rp.Start()) { |
| return errors.New("overlapping ranges: %v with %v", rp, rq) |
| } |
| if rp.Start() < rq.Start() { |
| pi++ |
| } else { |
| qi++ |
| } |
| } |
| return nil |
| } |
| |
| type fieldRange [2]protoreflect.FieldNumber |
| |
| func (r fieldRange) Start() protoreflect.FieldNumber { return r[0] } // inclusive |
| func (r fieldRange) End() protoreflect.FieldNumber { return r[1] - 1 } // inclusive |
| func (r fieldRange) String() string { |
| if r.Start() == r.End() { |
| return fmt.Sprintf("%d", r.Start()) |
| } |
| return fmt.Sprintf("%d to %d", r.Start(), r.End()) |
| } |
| |
| type FieldNumbers struct { |
| List []pref.FieldNumber |
| once sync.Once |
| has map[pref.FieldNumber]struct{} // protected by once |
| } |
| |
| func (p *FieldNumbers) Len() int { return len(p.List) } |
| func (p *FieldNumbers) Get(i int) pref.FieldNumber { return p.List[i] } |
| func (p *FieldNumbers) Has(n pref.FieldNumber) bool { |
| p.once.Do(func() { |
| if len(p.List) > 0 { |
| p.has = make(map[pref.FieldNumber]struct{}, len(p.List)) |
| for _, n := range p.List { |
| p.has[n] = struct{}{} |
| } |
| } |
| }) |
| _, ok := p.has[n] |
| return ok |
| } |
| func (p *FieldNumbers) Format(s fmt.State, r rune) { descfmt.FormatList(s, r, p) } |
| func (p *FieldNumbers) ProtoInternal(pragma.DoNotImplement) {} |
| |
| type OneofFields struct { |
| List []pref.FieldDescriptor |
| once sync.Once |
| byName map[pref.Name]pref.FieldDescriptor // protected by once |
| byJSON map[string]pref.FieldDescriptor // protected by once |
| byText map[string]pref.FieldDescriptor // protected by once |
| byNum map[pref.FieldNumber]pref.FieldDescriptor // protected by once |
| } |
| |
| func (p *OneofFields) Len() int { return len(p.List) } |
| func (p *OneofFields) Get(i int) pref.FieldDescriptor { return p.List[i] } |
| func (p *OneofFields) ByName(s pref.Name) pref.FieldDescriptor { return p.lazyInit().byName[s] } |
| func (p *OneofFields) ByJSONName(s string) pref.FieldDescriptor { return p.lazyInit().byJSON[s] } |
| func (p *OneofFields) ByTextName(s string) pref.FieldDescriptor { return p.lazyInit().byText[s] } |
| func (p *OneofFields) ByNumber(n pref.FieldNumber) pref.FieldDescriptor { return p.lazyInit().byNum[n] } |
| func (p *OneofFields) Format(s fmt.State, r rune) { descfmt.FormatList(s, r, p) } |
| func (p *OneofFields) ProtoInternal(pragma.DoNotImplement) {} |
| |
| func (p *OneofFields) lazyInit() *OneofFields { |
| p.once.Do(func() { |
| if len(p.List) > 0 { |
| p.byName = make(map[pref.Name]pref.FieldDescriptor, len(p.List)) |
| p.byJSON = make(map[string]pref.FieldDescriptor, len(p.List)) |
| p.byText = make(map[string]pref.FieldDescriptor, len(p.List)) |
| p.byNum = make(map[pref.FieldNumber]pref.FieldDescriptor, len(p.List)) |
| for _, f := range p.List { |
| // Field names and numbers are guaranteed to be unique. |
| p.byName[f.Name()] = f |
| p.byJSON[f.JSONName()] = f |
| p.byText[f.TextName()] = f |
| p.byNum[f.Number()] = f |
| } |
| } |
| }) |
| return p |
| } |
| |
| type SourceLocations struct { |
| // List is a list of SourceLocations. |
| // The SourceLocation.Next field does not need to be populated |
| // as it will be lazily populated upon first need. |
| List []pref.SourceLocation |
| |
| // File is the parent file descriptor that these locations are relative to. |
| // If non-nil, ByDescriptor verifies that the provided descriptor |
| // is a child of this file descriptor. |
| File pref.FileDescriptor |
| |
| once sync.Once |
| byPath map[pathKey]int |
| } |
| |
| func (p *SourceLocations) Len() int { return len(p.List) } |
| func (p *SourceLocations) Get(i int) pref.SourceLocation { return p.lazyInit().List[i] } |
| func (p *SourceLocations) byKey(k pathKey) pref.SourceLocation { |
| if i, ok := p.lazyInit().byPath[k]; ok { |
| return p.List[i] |
| } |
| return pref.SourceLocation{} |
| } |
| func (p *SourceLocations) ByPath(path pref.SourcePath) pref.SourceLocation { |
| return p.byKey(newPathKey(path)) |
| } |
| func (p *SourceLocations) ByDescriptor(desc pref.Descriptor) pref.SourceLocation { |
| if p.File != nil && desc != nil && p.File != desc.ParentFile() { |
| return pref.SourceLocation{} // mismatching parent files |
| } |
| var pathArr [16]int32 |
| path := pathArr[:0] |
| for { |
| switch desc.(type) { |
| case pref.FileDescriptor: |
| // Reverse the path since it was constructed in reverse. |
| for i, j := 0, len(path)-1; i < j; i, j = i+1, j-1 { |
| path[i], path[j] = path[j], path[i] |
| } |
| return p.byKey(newPathKey(path)) |
| case pref.MessageDescriptor: |
| path = append(path, int32(desc.Index())) |
| desc = desc.Parent() |
| switch desc.(type) { |
| case pref.FileDescriptor: |
| path = append(path, int32(genid.FileDescriptorProto_MessageType_field_number)) |
| case pref.MessageDescriptor: |
| path = append(path, int32(genid.DescriptorProto_NestedType_field_number)) |
| default: |
| return pref.SourceLocation{} |
| } |
| case pref.FieldDescriptor: |
| isExtension := desc.(pref.FieldDescriptor).IsExtension() |
| path = append(path, int32(desc.Index())) |
| desc = desc.Parent() |
| if isExtension { |
| switch desc.(type) { |
| case pref.FileDescriptor: |
| path = append(path, int32(genid.FileDescriptorProto_Extension_field_number)) |
| case pref.MessageDescriptor: |
| path = append(path, int32(genid.DescriptorProto_Extension_field_number)) |
| default: |
| return pref.SourceLocation{} |
| } |
| } else { |
| switch desc.(type) { |
| case pref.MessageDescriptor: |
| path = append(path, int32(genid.DescriptorProto_Field_field_number)) |
| default: |
| return pref.SourceLocation{} |
| } |
| } |
| case pref.OneofDescriptor: |
| path = append(path, int32(desc.Index())) |
| desc = desc.Parent() |
| switch desc.(type) { |
| case pref.MessageDescriptor: |
| path = append(path, int32(genid.DescriptorProto_OneofDecl_field_number)) |
| default: |
| return pref.SourceLocation{} |
| } |
| case pref.EnumDescriptor: |
| path = append(path, int32(desc.Index())) |
| desc = desc.Parent() |
| switch desc.(type) { |
| case pref.FileDescriptor: |
| path = append(path, int32(genid.FileDescriptorProto_EnumType_field_number)) |
| case pref.MessageDescriptor: |
| path = append(path, int32(genid.DescriptorProto_EnumType_field_number)) |
| default: |
| return pref.SourceLocation{} |
| } |
| case pref.EnumValueDescriptor: |
| path = append(path, int32(desc.Index())) |
| desc = desc.Parent() |
| switch desc.(type) { |
| case pref.EnumDescriptor: |
| path = append(path, int32(genid.EnumDescriptorProto_Value_field_number)) |
| default: |
| return pref.SourceLocation{} |
| } |
| case pref.ServiceDescriptor: |
| path = append(path, int32(desc.Index())) |
| desc = desc.Parent() |
| switch desc.(type) { |
| case pref.FileDescriptor: |
| path = append(path, int32(genid.FileDescriptorProto_Service_field_number)) |
| default: |
| return pref.SourceLocation{} |
| } |
| case pref.MethodDescriptor: |
| path = append(path, int32(desc.Index())) |
| desc = desc.Parent() |
| switch desc.(type) { |
| case pref.ServiceDescriptor: |
| path = append(path, int32(genid.ServiceDescriptorProto_Method_field_number)) |
| default: |
| return pref.SourceLocation{} |
| } |
| default: |
| return pref.SourceLocation{} |
| } |
| } |
| } |
| func (p *SourceLocations) lazyInit() *SourceLocations { |
| p.once.Do(func() { |
| if len(p.List) > 0 { |
| // Collect all the indexes for a given path. |
| pathIdxs := make(map[pathKey][]int, len(p.List)) |
| for i, l := range p.List { |
| k := newPathKey(l.Path) |
| pathIdxs[k] = append(pathIdxs[k], i) |
| } |
| |
| // Update the next index for all locations. |
| p.byPath = make(map[pathKey]int, len(p.List)) |
| for k, idxs := range pathIdxs { |
| for i := 0; i < len(idxs)-1; i++ { |
| p.List[idxs[i]].Next = idxs[i+1] |
| } |
| p.List[idxs[len(idxs)-1]].Next = 0 |
| p.byPath[k] = idxs[0] // record the first location for this path |
| } |
| } |
| }) |
| return p |
| } |
| func (p *SourceLocations) ProtoInternal(pragma.DoNotImplement) {} |
| |
| // pathKey is a comparable representation of protoreflect.SourcePath. |
| type pathKey struct { |
| arr [16]uint8 // first n-1 path segments; last element is the length |
| str string // used if the path does not fit in arr |
| } |
| |
| func newPathKey(p pref.SourcePath) (k pathKey) { |
| if len(p) < len(k.arr) { |
| for i, ps := range p { |
| if ps < 0 || math.MaxUint8 <= ps { |
| return pathKey{str: p.String()} |
| } |
| k.arr[i] = uint8(ps) |
| } |
| k.arr[len(k.arr)-1] = uint8(len(p)) |
| return k |
| } |
| return pathKey{str: p.String()} |
| } |