blob: 98ab142aeee6780e6e71c6ad4dfa3f63e27a6b67 [file] [log] [blame]
khenaidoo7d3c5582021-08-11 18:09:44 -04001// 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
5package filedesc
6
7import (
8 "bytes"
9 "fmt"
10 "sync"
11 "sync/atomic"
12
13 "google.golang.org/protobuf/internal/descfmt"
14 "google.golang.org/protobuf/internal/descopts"
15 "google.golang.org/protobuf/internal/encoding/defval"
16 "google.golang.org/protobuf/internal/encoding/messageset"
17 "google.golang.org/protobuf/internal/genid"
18 "google.golang.org/protobuf/internal/pragma"
19 "google.golang.org/protobuf/internal/strs"
20 pref "google.golang.org/protobuf/reflect/protoreflect"
21 "google.golang.org/protobuf/reflect/protoregistry"
22)
23
24// The types in this file may have a suffix:
25// • L0: Contains fields common to all descriptors (except File) and
26// must be initialized up front.
27// • L1: Contains fields specific to a descriptor and
28// must be initialized up front.
29// • L2: Contains fields that are lazily initialized when constructing
30// from the raw file descriptor. When constructing as a literal, the L2
31// fields must be initialized up front.
32//
33// The types are exported so that packages like reflect/protodesc can
34// directly construct descriptors.
35
36type (
37 File struct {
38 fileRaw
39 L1 FileL1
40
41 once uint32 // atomically set if L2 is valid
42 mu sync.Mutex // protects L2
43 L2 *FileL2
44 }
45 FileL1 struct {
46 Syntax pref.Syntax
47 Path string
48 Package pref.FullName
49
50 Enums Enums
51 Messages Messages
52 Extensions Extensions
53 Services Services
54 }
55 FileL2 struct {
56 Options func() pref.ProtoMessage
57 Imports FileImports
58 Locations SourceLocations
59 }
60)
61
62func (fd *File) ParentFile() pref.FileDescriptor { return fd }
63func (fd *File) Parent() pref.Descriptor { return nil }
64func (fd *File) Index() int { return 0 }
65func (fd *File) Syntax() pref.Syntax { return fd.L1.Syntax }
66func (fd *File) Name() pref.Name { return fd.L1.Package.Name() }
67func (fd *File) FullName() pref.FullName { return fd.L1.Package }
68func (fd *File) IsPlaceholder() bool { return false }
69func (fd *File) Options() pref.ProtoMessage {
70 if f := fd.lazyInit().Options; f != nil {
71 return f()
72 }
73 return descopts.File
74}
75func (fd *File) Path() string { return fd.L1.Path }
76func (fd *File) Package() pref.FullName { return fd.L1.Package }
77func (fd *File) Imports() pref.FileImports { return &fd.lazyInit().Imports }
78func (fd *File) Enums() pref.EnumDescriptors { return &fd.L1.Enums }
79func (fd *File) Messages() pref.MessageDescriptors { return &fd.L1.Messages }
80func (fd *File) Extensions() pref.ExtensionDescriptors { return &fd.L1.Extensions }
81func (fd *File) Services() pref.ServiceDescriptors { return &fd.L1.Services }
82func (fd *File) SourceLocations() pref.SourceLocations { return &fd.lazyInit().Locations }
83func (fd *File) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, fd) }
84func (fd *File) ProtoType(pref.FileDescriptor) {}
85func (fd *File) ProtoInternal(pragma.DoNotImplement) {}
86
87func (fd *File) lazyInit() *FileL2 {
88 if atomic.LoadUint32(&fd.once) == 0 {
89 fd.lazyInitOnce()
90 }
91 return fd.L2
92}
93
94func (fd *File) lazyInitOnce() {
95 fd.mu.Lock()
96 if fd.L2 == nil {
97 fd.lazyRawInit() // recursively initializes all L2 structures
98 }
99 atomic.StoreUint32(&fd.once, 1)
100 fd.mu.Unlock()
101}
102
103// GoPackagePath is a pseudo-internal API for determining the Go package path
104// that this file descriptor is declared in.
105//
106// WARNING: This method is exempt from the compatibility promise and may be
107// removed in the future without warning.
108func (fd *File) GoPackagePath() string {
109 return fd.builder.GoPackagePath
110}
111
112type (
113 Enum struct {
114 Base
115 L1 EnumL1
116 L2 *EnumL2 // protected by fileDesc.once
117 }
118 EnumL1 struct {
119 eagerValues bool // controls whether EnumL2.Values is already populated
120 }
121 EnumL2 struct {
122 Options func() pref.ProtoMessage
123 Values EnumValues
124 ReservedNames Names
125 ReservedRanges EnumRanges
126 }
127
128 EnumValue struct {
129 Base
130 L1 EnumValueL1
131 }
132 EnumValueL1 struct {
133 Options func() pref.ProtoMessage
134 Number pref.EnumNumber
135 }
136)
137
138func (ed *Enum) Options() pref.ProtoMessage {
139 if f := ed.lazyInit().Options; f != nil {
140 return f()
141 }
142 return descopts.Enum
143}
144func (ed *Enum) Values() pref.EnumValueDescriptors {
145 if ed.L1.eagerValues {
146 return &ed.L2.Values
147 }
148 return &ed.lazyInit().Values
149}
150func (ed *Enum) ReservedNames() pref.Names { return &ed.lazyInit().ReservedNames }
151func (ed *Enum) ReservedRanges() pref.EnumRanges { return &ed.lazyInit().ReservedRanges }
152func (ed *Enum) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, ed) }
153func (ed *Enum) ProtoType(pref.EnumDescriptor) {}
154func (ed *Enum) lazyInit() *EnumL2 {
155 ed.L0.ParentFile.lazyInit() // implicitly initializes L2
156 return ed.L2
157}
158
159func (ed *EnumValue) Options() pref.ProtoMessage {
160 if f := ed.L1.Options; f != nil {
161 return f()
162 }
163 return descopts.EnumValue
164}
165func (ed *EnumValue) Number() pref.EnumNumber { return ed.L1.Number }
166func (ed *EnumValue) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, ed) }
167func (ed *EnumValue) ProtoType(pref.EnumValueDescriptor) {}
168
169type (
170 Message struct {
171 Base
172 L1 MessageL1
173 L2 *MessageL2 // protected by fileDesc.once
174 }
175 MessageL1 struct {
176 Enums Enums
177 Messages Messages
178 Extensions Extensions
179 IsMapEntry bool // promoted from google.protobuf.MessageOptions
180 IsMessageSet bool // promoted from google.protobuf.MessageOptions
181 }
182 MessageL2 struct {
183 Options func() pref.ProtoMessage
184 Fields Fields
185 Oneofs Oneofs
186 ReservedNames Names
187 ReservedRanges FieldRanges
188 RequiredNumbers FieldNumbers // must be consistent with Fields.Cardinality
189 ExtensionRanges FieldRanges
190 ExtensionRangeOptions []func() pref.ProtoMessage // must be same length as ExtensionRanges
191 }
192
193 Field struct {
194 Base
195 L1 FieldL1
196 }
197 FieldL1 struct {
198 Options func() pref.ProtoMessage
199 Number pref.FieldNumber
200 Cardinality pref.Cardinality // must be consistent with Message.RequiredNumbers
201 Kind pref.Kind
202 StringName stringName
203 IsProto3Optional bool // promoted from google.protobuf.FieldDescriptorProto
204 IsWeak bool // promoted from google.protobuf.FieldOptions
205 HasPacked bool // promoted from google.protobuf.FieldOptions
206 IsPacked bool // promoted from google.protobuf.FieldOptions
207 HasEnforceUTF8 bool // promoted from google.protobuf.FieldOptions
208 EnforceUTF8 bool // promoted from google.protobuf.FieldOptions
209 Default defaultValue
210 ContainingOneof pref.OneofDescriptor // must be consistent with Message.Oneofs.Fields
211 Enum pref.EnumDescriptor
212 Message pref.MessageDescriptor
213 }
214
215 Oneof struct {
216 Base
217 L1 OneofL1
218 }
219 OneofL1 struct {
220 Options func() pref.ProtoMessage
221 Fields OneofFields // must be consistent with Message.Fields.ContainingOneof
222 }
223)
224
225func (md *Message) Options() pref.ProtoMessage {
226 if f := md.lazyInit().Options; f != nil {
227 return f()
228 }
229 return descopts.Message
230}
231func (md *Message) IsMapEntry() bool { return md.L1.IsMapEntry }
232func (md *Message) Fields() pref.FieldDescriptors { return &md.lazyInit().Fields }
233func (md *Message) Oneofs() pref.OneofDescriptors { return &md.lazyInit().Oneofs }
234func (md *Message) ReservedNames() pref.Names { return &md.lazyInit().ReservedNames }
235func (md *Message) ReservedRanges() pref.FieldRanges { return &md.lazyInit().ReservedRanges }
236func (md *Message) RequiredNumbers() pref.FieldNumbers { return &md.lazyInit().RequiredNumbers }
237func (md *Message) ExtensionRanges() pref.FieldRanges { return &md.lazyInit().ExtensionRanges }
238func (md *Message) ExtensionRangeOptions(i int) pref.ProtoMessage {
239 if f := md.lazyInit().ExtensionRangeOptions[i]; f != nil {
240 return f()
241 }
242 return descopts.ExtensionRange
243}
244func (md *Message) Enums() pref.EnumDescriptors { return &md.L1.Enums }
245func (md *Message) Messages() pref.MessageDescriptors { return &md.L1.Messages }
246func (md *Message) Extensions() pref.ExtensionDescriptors { return &md.L1.Extensions }
247func (md *Message) ProtoType(pref.MessageDescriptor) {}
248func (md *Message) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, md) }
249func (md *Message) lazyInit() *MessageL2 {
250 md.L0.ParentFile.lazyInit() // implicitly initializes L2
251 return md.L2
252}
253
254// IsMessageSet is a pseudo-internal API for checking whether a message
255// should serialize in the proto1 message format.
256//
257// WARNING: This method is exempt from the compatibility promise and may be
258// removed in the future without warning.
259func (md *Message) IsMessageSet() bool {
260 return md.L1.IsMessageSet
261}
262
263func (fd *Field) Options() pref.ProtoMessage {
264 if f := fd.L1.Options; f != nil {
265 return f()
266 }
267 return descopts.Field
268}
269func (fd *Field) Number() pref.FieldNumber { return fd.L1.Number }
270func (fd *Field) Cardinality() pref.Cardinality { return fd.L1.Cardinality }
271func (fd *Field) Kind() pref.Kind { return fd.L1.Kind }
272func (fd *Field) HasJSONName() bool { return fd.L1.StringName.hasJSON }
273func (fd *Field) JSONName() string { return fd.L1.StringName.getJSON(fd) }
274func (fd *Field) TextName() string { return fd.L1.StringName.getText(fd) }
275func (fd *Field) HasPresence() bool {
276 return fd.L1.Cardinality != pref.Repeated && (fd.L0.ParentFile.L1.Syntax == pref.Proto2 || fd.L1.Message != nil || fd.L1.ContainingOneof != nil)
277}
278func (fd *Field) HasOptionalKeyword() bool {
279 return (fd.L0.ParentFile.L1.Syntax == pref.Proto2 && fd.L1.Cardinality == pref.Optional && fd.L1.ContainingOneof == nil) || fd.L1.IsProto3Optional
280}
281func (fd *Field) IsPacked() bool {
282 if !fd.L1.HasPacked && fd.L0.ParentFile.L1.Syntax != pref.Proto2 && fd.L1.Cardinality == pref.Repeated {
283 switch fd.L1.Kind {
284 case pref.StringKind, pref.BytesKind, pref.MessageKind, pref.GroupKind:
285 default:
286 return true
287 }
288 }
289 return fd.L1.IsPacked
290}
291func (fd *Field) IsExtension() bool { return false }
292func (fd *Field) IsWeak() bool { return fd.L1.IsWeak }
293func (fd *Field) IsList() bool { return fd.Cardinality() == pref.Repeated && !fd.IsMap() }
294func (fd *Field) IsMap() bool { return fd.Message() != nil && fd.Message().IsMapEntry() }
295func (fd *Field) MapKey() pref.FieldDescriptor {
296 if !fd.IsMap() {
297 return nil
298 }
299 return fd.Message().Fields().ByNumber(genid.MapEntry_Key_field_number)
300}
301func (fd *Field) MapValue() pref.FieldDescriptor {
302 if !fd.IsMap() {
303 return nil
304 }
305 return fd.Message().Fields().ByNumber(genid.MapEntry_Value_field_number)
306}
307func (fd *Field) HasDefault() bool { return fd.L1.Default.has }
308func (fd *Field) Default() pref.Value { return fd.L1.Default.get(fd) }
309func (fd *Field) DefaultEnumValue() pref.EnumValueDescriptor { return fd.L1.Default.enum }
310func (fd *Field) ContainingOneof() pref.OneofDescriptor { return fd.L1.ContainingOneof }
311func (fd *Field) ContainingMessage() pref.MessageDescriptor {
312 return fd.L0.Parent.(pref.MessageDescriptor)
313}
314func (fd *Field) Enum() pref.EnumDescriptor {
315 return fd.L1.Enum
316}
317func (fd *Field) Message() pref.MessageDescriptor {
318 if fd.L1.IsWeak {
319 if d, _ := protoregistry.GlobalFiles.FindDescriptorByName(fd.L1.Message.FullName()); d != nil {
320 return d.(pref.MessageDescriptor)
321 }
322 }
323 return fd.L1.Message
324}
325func (fd *Field) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, fd) }
326func (fd *Field) ProtoType(pref.FieldDescriptor) {}
327
328// EnforceUTF8 is a pseudo-internal API to determine whether to enforce UTF-8
329// validation for the string field. This exists for Google-internal use only
330// since proto3 did not enforce UTF-8 validity prior to the open-source release.
331// If this method does not exist, the default is to enforce valid UTF-8.
332//
333// WARNING: This method is exempt from the compatibility promise and may be
334// removed in the future without warning.
335func (fd *Field) EnforceUTF8() bool {
336 if fd.L1.HasEnforceUTF8 {
337 return fd.L1.EnforceUTF8
338 }
339 return fd.L0.ParentFile.L1.Syntax == pref.Proto3
340}
341
342func (od *Oneof) IsSynthetic() bool {
343 return od.L0.ParentFile.L1.Syntax == pref.Proto3 && len(od.L1.Fields.List) == 1 && od.L1.Fields.List[0].HasOptionalKeyword()
344}
345func (od *Oneof) Options() pref.ProtoMessage {
346 if f := od.L1.Options; f != nil {
347 return f()
348 }
349 return descopts.Oneof
350}
351func (od *Oneof) Fields() pref.FieldDescriptors { return &od.L1.Fields }
352func (od *Oneof) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, od) }
353func (od *Oneof) ProtoType(pref.OneofDescriptor) {}
354
355type (
356 Extension struct {
357 Base
358 L1 ExtensionL1
359 L2 *ExtensionL2 // protected by fileDesc.once
360 }
361 ExtensionL1 struct {
362 Number pref.FieldNumber
363 Extendee pref.MessageDescriptor
364 Cardinality pref.Cardinality
365 Kind pref.Kind
366 }
367 ExtensionL2 struct {
368 Options func() pref.ProtoMessage
369 StringName stringName
370 IsProto3Optional bool // promoted from google.protobuf.FieldDescriptorProto
371 IsPacked bool // promoted from google.protobuf.FieldOptions
372 Default defaultValue
373 Enum pref.EnumDescriptor
374 Message pref.MessageDescriptor
375 }
376)
377
378func (xd *Extension) Options() pref.ProtoMessage {
379 if f := xd.lazyInit().Options; f != nil {
380 return f()
381 }
382 return descopts.Field
383}
384func (xd *Extension) Number() pref.FieldNumber { return xd.L1.Number }
385func (xd *Extension) Cardinality() pref.Cardinality { return xd.L1.Cardinality }
386func (xd *Extension) Kind() pref.Kind { return xd.L1.Kind }
387func (xd *Extension) HasJSONName() bool { return xd.lazyInit().StringName.hasJSON }
388func (xd *Extension) JSONName() string { return xd.lazyInit().StringName.getJSON(xd) }
389func (xd *Extension) TextName() string { return xd.lazyInit().StringName.getText(xd) }
390func (xd *Extension) HasPresence() bool { return xd.L1.Cardinality != pref.Repeated }
391func (xd *Extension) HasOptionalKeyword() bool {
392 return (xd.L0.ParentFile.L1.Syntax == pref.Proto2 && xd.L1.Cardinality == pref.Optional) || xd.lazyInit().IsProto3Optional
393}
394func (xd *Extension) IsPacked() bool { return xd.lazyInit().IsPacked }
395func (xd *Extension) IsExtension() bool { return true }
396func (xd *Extension) IsWeak() bool { return false }
397func (xd *Extension) IsList() bool { return xd.Cardinality() == pref.Repeated }
398func (xd *Extension) IsMap() bool { return false }
399func (xd *Extension) MapKey() pref.FieldDescriptor { return nil }
400func (xd *Extension) MapValue() pref.FieldDescriptor { return nil }
401func (xd *Extension) HasDefault() bool { return xd.lazyInit().Default.has }
402func (xd *Extension) Default() pref.Value { return xd.lazyInit().Default.get(xd) }
403func (xd *Extension) DefaultEnumValue() pref.EnumValueDescriptor { return xd.lazyInit().Default.enum }
404func (xd *Extension) ContainingOneof() pref.OneofDescriptor { return nil }
405func (xd *Extension) ContainingMessage() pref.MessageDescriptor { return xd.L1.Extendee }
406func (xd *Extension) Enum() pref.EnumDescriptor { return xd.lazyInit().Enum }
407func (xd *Extension) Message() pref.MessageDescriptor { return xd.lazyInit().Message }
408func (xd *Extension) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, xd) }
409func (xd *Extension) ProtoType(pref.FieldDescriptor) {}
410func (xd *Extension) ProtoInternal(pragma.DoNotImplement) {}
411func (xd *Extension) lazyInit() *ExtensionL2 {
412 xd.L0.ParentFile.lazyInit() // implicitly initializes L2
413 return xd.L2
414}
415
416type (
417 Service struct {
418 Base
419 L1 ServiceL1
420 L2 *ServiceL2 // protected by fileDesc.once
421 }
422 ServiceL1 struct{}
423 ServiceL2 struct {
424 Options func() pref.ProtoMessage
425 Methods Methods
426 }
427
428 Method struct {
429 Base
430 L1 MethodL1
431 }
432 MethodL1 struct {
433 Options func() pref.ProtoMessage
434 Input pref.MessageDescriptor
435 Output pref.MessageDescriptor
436 IsStreamingClient bool
437 IsStreamingServer bool
438 }
439)
440
441func (sd *Service) Options() pref.ProtoMessage {
442 if f := sd.lazyInit().Options; f != nil {
443 return f()
444 }
445 return descopts.Service
446}
447func (sd *Service) Methods() pref.MethodDescriptors { return &sd.lazyInit().Methods }
448func (sd *Service) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, sd) }
449func (sd *Service) ProtoType(pref.ServiceDescriptor) {}
450func (sd *Service) ProtoInternal(pragma.DoNotImplement) {}
451func (sd *Service) lazyInit() *ServiceL2 {
452 sd.L0.ParentFile.lazyInit() // implicitly initializes L2
453 return sd.L2
454}
455
456func (md *Method) Options() pref.ProtoMessage {
457 if f := md.L1.Options; f != nil {
458 return f()
459 }
460 return descopts.Method
461}
462func (md *Method) Input() pref.MessageDescriptor { return md.L1.Input }
463func (md *Method) Output() pref.MessageDescriptor { return md.L1.Output }
464func (md *Method) IsStreamingClient() bool { return md.L1.IsStreamingClient }
465func (md *Method) IsStreamingServer() bool { return md.L1.IsStreamingServer }
466func (md *Method) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, md) }
467func (md *Method) ProtoType(pref.MethodDescriptor) {}
468func (md *Method) ProtoInternal(pragma.DoNotImplement) {}
469
470// Surrogate files are can be used to create standalone descriptors
471// where the syntax is only information derived from the parent file.
472var (
473 SurrogateProto2 = &File{L1: FileL1{Syntax: pref.Proto2}, L2: &FileL2{}}
474 SurrogateProto3 = &File{L1: FileL1{Syntax: pref.Proto3}, L2: &FileL2{}}
475)
476
477type (
478 Base struct {
479 L0 BaseL0
480 }
481 BaseL0 struct {
482 FullName pref.FullName // must be populated
483 ParentFile *File // must be populated
484 Parent pref.Descriptor
485 Index int
486 }
487)
488
489func (d *Base) Name() pref.Name { return d.L0.FullName.Name() }
490func (d *Base) FullName() pref.FullName { return d.L0.FullName }
491func (d *Base) ParentFile() pref.FileDescriptor {
492 if d.L0.ParentFile == SurrogateProto2 || d.L0.ParentFile == SurrogateProto3 {
493 return nil // surrogate files are not real parents
494 }
495 return d.L0.ParentFile
496}
497func (d *Base) Parent() pref.Descriptor { return d.L0.Parent }
498func (d *Base) Index() int { return d.L0.Index }
499func (d *Base) Syntax() pref.Syntax { return d.L0.ParentFile.Syntax() }
500func (d *Base) IsPlaceholder() bool { return false }
501func (d *Base) ProtoInternal(pragma.DoNotImplement) {}
502
503type stringName struct {
504 hasJSON bool
505 once sync.Once
506 nameJSON string
507 nameText string
508}
509
510// InitJSON initializes the name. It is exported for use by other internal packages.
511func (s *stringName) InitJSON(name string) {
512 s.hasJSON = true
513 s.nameJSON = name
514}
515
516func (s *stringName) lazyInit(fd pref.FieldDescriptor) *stringName {
517 s.once.Do(func() {
518 if fd.IsExtension() {
519 // For extensions, JSON and text are formatted the same way.
520 var name string
521 if messageset.IsMessageSetExtension(fd) {
522 name = string("[" + fd.FullName().Parent() + "]")
523 } else {
524 name = string("[" + fd.FullName() + "]")
525 }
526 s.nameJSON = name
527 s.nameText = name
528 } else {
529 // Format the JSON name.
530 if !s.hasJSON {
531 s.nameJSON = strs.JSONCamelCase(string(fd.Name()))
532 }
533
534 // Format the text name.
535 s.nameText = string(fd.Name())
536 if fd.Kind() == pref.GroupKind {
537 s.nameText = string(fd.Message().Name())
538 }
539 }
540 })
541 return s
542}
543
544func (s *stringName) getJSON(fd pref.FieldDescriptor) string { return s.lazyInit(fd).nameJSON }
545func (s *stringName) getText(fd pref.FieldDescriptor) string { return s.lazyInit(fd).nameText }
546
547func DefaultValue(v pref.Value, ev pref.EnumValueDescriptor) defaultValue {
548 dv := defaultValue{has: v.IsValid(), val: v, enum: ev}
549 if b, ok := v.Interface().([]byte); ok {
550 // Store a copy of the default bytes, so that we can detect
551 // accidental mutations of the original value.
552 dv.bytes = append([]byte(nil), b...)
553 }
554 return dv
555}
556
557func unmarshalDefault(b []byte, k pref.Kind, pf *File, ed pref.EnumDescriptor) defaultValue {
558 var evs pref.EnumValueDescriptors
559 if k == pref.EnumKind {
560 // If the enum is declared within the same file, be careful not to
561 // blindly call the Values method, lest we bind ourselves in a deadlock.
562 if e, ok := ed.(*Enum); ok && e.L0.ParentFile == pf {
563 evs = &e.L2.Values
564 } else {
565 evs = ed.Values()
566 }
567
568 // If we are unable to resolve the enum dependency, use a placeholder
569 // enum value since we will not be able to parse the default value.
570 if ed.IsPlaceholder() && pref.Name(b).IsValid() {
571 v := pref.ValueOfEnum(0)
572 ev := PlaceholderEnumValue(ed.FullName().Parent().Append(pref.Name(b)))
573 return DefaultValue(v, ev)
574 }
575 }
576
577 v, ev, err := defval.Unmarshal(string(b), k, evs, defval.Descriptor)
578 if err != nil {
579 panic(err)
580 }
581 return DefaultValue(v, ev)
582}
583
584type defaultValue struct {
585 has bool
586 val pref.Value
587 enum pref.EnumValueDescriptor
588 bytes []byte
589}
590
591func (dv *defaultValue) get(fd pref.FieldDescriptor) pref.Value {
592 // Return the zero value as the default if unpopulated.
593 if !dv.has {
594 if fd.Cardinality() == pref.Repeated {
595 return pref.Value{}
596 }
597 switch fd.Kind() {
598 case pref.BoolKind:
599 return pref.ValueOfBool(false)
600 case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind:
601 return pref.ValueOfInt32(0)
602 case pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
603 return pref.ValueOfInt64(0)
604 case pref.Uint32Kind, pref.Fixed32Kind:
605 return pref.ValueOfUint32(0)
606 case pref.Uint64Kind, pref.Fixed64Kind:
607 return pref.ValueOfUint64(0)
608 case pref.FloatKind:
609 return pref.ValueOfFloat32(0)
610 case pref.DoubleKind:
611 return pref.ValueOfFloat64(0)
612 case pref.StringKind:
613 return pref.ValueOfString("")
614 case pref.BytesKind:
615 return pref.ValueOfBytes(nil)
616 case pref.EnumKind:
617 if evs := fd.Enum().Values(); evs.Len() > 0 {
618 return pref.ValueOfEnum(evs.Get(0).Number())
619 }
620 return pref.ValueOfEnum(0)
621 }
622 }
623
624 if len(dv.bytes) > 0 && !bytes.Equal(dv.bytes, dv.val.Bytes()) {
625 // TODO: Avoid panic if we're running with the race detector
626 // and instead spawn a goroutine that periodically resets
627 // this value back to the original to induce a race.
628 panic(fmt.Sprintf("detected mutation on the default bytes for %v", fd.FullName()))
629 }
630 return dv.val
631}