blob: 4d15b28e9ee2d8a187974a66c73693d02dee1dae [file] [log] [blame]
Rob Pikeaaa3a622010-03-20 22:32:34 -07001// Go support for Protocol Buffers - Google's data interchange format
2//
3// Copyright 2010 Google Inc. All rights reserved.
4// http://code.google.com/p/goprotobuf/
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions are
8// met:
9//
10// * Redistributions of source code must retain the above copyright
11// notice, this list of conditions and the following disclaimer.
12// * Redistributions in binary form must reproduce the above
13// copyright notice, this list of conditions and the following disclaimer
14// in the documentation and/or other materials provided with the
15// distribution.
16// * Neither the name of Google Inc. nor the names of its
17// contributors may be used to endorse or promote products derived from
18// this software without specific prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32package proto
33
34/*
35 * Routines for encoding data into the wire format for protocol buffers.
36 */
37
38import (
39 "fmt"
40 "os"
41 "reflect"
42 "runtime"
43 "strconv"
44 "strings"
45 "sync"
46 "unsafe"
47)
48
49const debug bool = false
50
51// Constants that identify the encoding of a value on the wire.
52const (
53 WireVarint = 0
54 WireFixed64 = 1
55 WireBytes = 2
56 WireStartGroup = 3
57 WireEndGroup = 4
58 WireFixed32 = 5
59)
60
61const startSize = 10 // initial slice/string sizes
62
63// Encoders are defined in encoder.go
64// An encoder outputs the full representation of a field, including its
65// tag and encoder type.
66type encoder func(p *Buffer, prop *Properties, base uintptr) os.Error
67
68// A valueEncoder encodes a single integer in a particular encoding.
69type valueEncoder func(o *Buffer, x uint64) os.Error
70
71// Decoders are defined in decode.go
72// A decoder creates a value from its wire representation.
73// Unrecognized subelements are saved in unrec.
74type decoder func(p *Buffer, prop *Properties, base uintptr, sbase uintptr) os.Error
75
76// A valueDecoder decodes a single integer in a particular encoding.
77type valueDecoder func(o *Buffer) (x uint64, err os.Error)
78
79// StructProperties represents properties for all the fields of a struct.
80type StructProperties struct {
David Symonds79eae332010-10-16 11:33:20 +110081 Prop []*Properties // properties for each field
82 reqCount int // required count
83 tags map[int]int // map from proto tag to struct field number
84 origNames map[string]int // map from original name to struct field number
85 nscratch uintptr // size of scratch space
Rob Pikeaaa3a622010-03-20 22:32:34 -070086}
87
88// Properties represents the protocol-specific behavior of a single struct field.
89type Properties struct {
90 Name string // name of the field, for error messages
91 OrigName string // original name before protocol compiler (always set)
92 Wire string
93 WireType int
94 Tag int
95 Required bool
96 Optional bool
97 Repeated bool
David Symonds5b7775e2010-12-01 10:09:04 +110098 Packed bool // relevant for repeated primitives only
Rob Pikeaaa3a622010-03-20 22:32:34 -070099 Enum string // set for enum types only
100 Default string // default value
101 def_uint64 uint64
102
103 enc encoder
104 valEnc valueEncoder // set for bool and numeric types only
105 offset uintptr
106 tagcode []byte // encoding of EncodeVarint((Tag<<3)|WireType)
107 tagbuf [8]byte
108 stype *reflect.PtrType
109
110 dec decoder
111 valDec valueDecoder // set for bool and numeric types only
112 scratch uintptr
113 sizeof int // calculations of scratch space
114 alignof int
David Symonds5b7775e2010-12-01 10:09:04 +1100115
116 // If this is a packable field, this will be the decoder for the packed version of the field.
117 packedDec decoder
Rob Pikeaaa3a622010-03-20 22:32:34 -0700118}
119
120// String formats the properties in the "PB(...)" struct tag style.
121func (p *Properties) String() string {
122 s := p.Wire
123 s = ","
124 s += strconv.Itoa(p.Tag)
125 if p.Required {
126 s += ",req"
127 }
128 if p.Optional {
129 s += ",opt"
130 }
131 if p.Repeated {
132 s += ",rep"
133 }
David Symonds5b7775e2010-12-01 10:09:04 +1100134 if p.Packed {
135 s += ",packed"
136 }
Rob Pikeaaa3a622010-03-20 22:32:34 -0700137 if p.OrigName != p.Name {
138 s += ",name=" + p.OrigName
139 }
140 if len(p.Enum) > 0 {
141 s += ",enum=" + p.Enum
142 }
143 if len(p.Default) > 0 {
144 s += ",def=" + p.Default
145 }
146 return s
147}
148
149// Parse populates p by parsing a string in the "PB(...)" struct tag style.
150func (p *Properties) Parse(s string) {
151 // "bytes,49,opt,def=hello!,name=foo"
Rob Pike53385442010-06-30 22:22:43 -0700152 fields := strings.Split(s, ",", -1) // breaks def=, but handled below.
Rob Pikeaaa3a622010-03-20 22:32:34 -0700153 if len(fields) < 2 {
154 fmt.Fprintf(os.Stderr, "proto: tag has too few fields: %q\n", s)
155 return
156 }
157
158 p.Wire = fields[0]
159 switch p.Wire {
160 case "varint":
161 p.WireType = WireVarint
162 p.valEnc = (*Buffer).EncodeVarint
163 p.valDec = (*Buffer).DecodeVarint
164 case "fixed32":
165 p.WireType = WireFixed32
166 p.valEnc = (*Buffer).EncodeFixed32
167 p.valDec = (*Buffer).DecodeFixed32
168 case "fixed64":
169 p.WireType = WireFixed64
170 p.valEnc = (*Buffer).EncodeFixed64
171 p.valDec = (*Buffer).DecodeFixed64
172 case "zigzag32":
173 p.WireType = WireVarint
174 p.valEnc = (*Buffer).EncodeZigzag32
175 p.valDec = (*Buffer).DecodeZigzag32
176 case "zigzag64":
177 p.WireType = WireVarint
178 p.valEnc = (*Buffer).EncodeZigzag64
179 p.valDec = (*Buffer).DecodeZigzag64
180 case "bytes", "group":
181 p.WireType = WireBytes
182 // no numeric converter for non-numeric types
183 default:
184 fmt.Fprintf(os.Stderr, "proto: tag has unknown wire type: %q\n", s)
185 return
186 }
187
188 var err os.Error
189 p.Tag, err = strconv.Atoi(fields[1])
190 if err != nil {
191 return
192 }
193
194 for i := 2; i < len(fields); i++ {
195 f := fields[i]
196 switch {
197 case f == "req":
198 p.Required = true
199 case f == "opt":
200 p.Optional = true
201 case f == "rep":
202 p.Repeated = true
David Symonds5b7775e2010-12-01 10:09:04 +1100203 case f == "packed":
204 p.Packed = true
Rob Pikeaaa3a622010-03-20 22:32:34 -0700205 case len(f) >= 5 && f[0:5] == "name=":
206 p.OrigName = f[5:len(f)]
207 case len(f) >= 5 && f[0:5] == "enum=":
208 p.Enum = f[5:len(f)]
209 case len(f) >= 4 && f[0:4] == "def=":
210 p.Default = f[4:len(f)] // rest of string
211 if i+1 < len(fields) {
212 // Commas aren't escaped, and def is always last.
213 p.Default += "," + strings.Join(fields[i+1:len(fields)], ",")
214 break
215 }
216 }
217 }
218}
219
220// Initialize the fields for encoding and decoding.
221func (p *Properties) setEncAndDec(typ reflect.Type) {
222 var vbool bool
223 var vbyte byte
224 var vint32 int32
225 var vint64 int64
226 var vfloat32 float32
227 var vfloat64 float64
228 var vstring string
229 var vslice []byte
230
231 p.enc = nil
232 p.dec = nil
233
234 switch t1 := typ.(type) {
235 default:
236 fmt.Fprintf(os.Stderr, "proto: no coders for %T\n", t1)
237 break
238
239 case *reflect.PtrType:
240 switch t2 := t1.Elem().(type) {
241 default:
Rob Pikeab5b8022010-06-21 17:47:58 -0700242 BadType:
Rob Pikeaaa3a622010-03-20 22:32:34 -0700243 fmt.Fprintf(os.Stderr, "proto: no encoder function for %T -> %T\n", t1, t2)
244 break
245 case *reflect.BoolType:
246 p.enc = (*Buffer).enc_bool
247 p.dec = (*Buffer).dec_bool
248 p.alignof = unsafe.Alignof(vbool)
249 p.sizeof = unsafe.Sizeof(vbool)
Rob Pikeab5b8022010-06-21 17:47:58 -0700250 case *reflect.IntType, *reflect.UintType:
251 switch t2.Bits() {
252 case 32:
253 p.enc = (*Buffer).enc_int32
254 p.dec = (*Buffer).dec_int32
255 p.alignof = unsafe.Alignof(vint32)
256 p.sizeof = unsafe.Sizeof(vint32)
257 case 64:
258 p.enc = (*Buffer).enc_int64
259 p.dec = (*Buffer).dec_int64
260 p.alignof = unsafe.Alignof(vint64)
261 p.sizeof = unsafe.Sizeof(vint64)
262 default:
263 goto BadType
264 }
265 case *reflect.FloatType:
266 switch t2.Bits() {
267 case 32:
268 p.enc = (*Buffer).enc_int32 // can just treat them as bits
269 p.dec = (*Buffer).dec_int32
270 p.alignof = unsafe.Alignof(vfloat32)
271 p.sizeof = unsafe.Sizeof(vfloat32)
272 case 64:
273 p.enc = (*Buffer).enc_int64 // can just treat them as bits
274 p.dec = (*Buffer).dec_int64
275 p.alignof = unsafe.Alignof(vfloat64)
276 p.sizeof = unsafe.Sizeof(vfloat64)
277 default:
278 goto BadType
279 }
Rob Pikeaaa3a622010-03-20 22:32:34 -0700280 case *reflect.StringType:
281 p.enc = (*Buffer).enc_string
282 p.dec = (*Buffer).dec_string
283 p.alignof = unsafe.Alignof(vstring)
284 p.sizeof = unsafe.Sizeof(vstring) + startSize*unsafe.Sizeof(vbyte)
285 case *reflect.StructType:
286 p.stype = t1
287 if p.Wire == "bytes" {
288 p.enc = (*Buffer).enc_struct_message
289 p.dec = (*Buffer).dec_struct_message
290 } else {
291 p.enc = (*Buffer).enc_struct_group
292 p.dec = (*Buffer).dec_struct_group
293 }
294 }
295
296 case *reflect.SliceType:
297 switch t2 := t1.Elem().(type) {
298 default:
Rob Pikeab5b8022010-06-21 17:47:58 -0700299 BadSliceType:
300 fmt.Fprintf(os.Stderr, "proto: no slice oenc for %T = []%T\n", t1, t2)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700301 break
Rob Pikeaaa3a622010-03-20 22:32:34 -0700302 case *reflect.BoolType:
David Symonds5b7775e2010-12-01 10:09:04 +1100303 if p.Packed {
304 p.enc = (*Buffer).enc_slice_packed_bool
305 } else {
306 p.enc = (*Buffer).enc_slice_bool
307 }
Rob Pikeaaa3a622010-03-20 22:32:34 -0700308 p.dec = (*Buffer).dec_slice_bool
David Symonds5b7775e2010-12-01 10:09:04 +1100309 p.packedDec = (*Buffer).dec_slice_packed_bool
Rob Pikeaaa3a622010-03-20 22:32:34 -0700310 p.alignof = unsafe.Alignof(vbool)
311 p.sizeof = startSize * unsafe.Sizeof(vbool)
Rob Pikeab5b8022010-06-21 17:47:58 -0700312 case *reflect.IntType, *reflect.UintType:
313 switch t2.Bits() {
314 case 32:
David Symonds5b7775e2010-12-01 10:09:04 +1100315 if p.Packed {
316 p.enc = (*Buffer).enc_slice_packed_int32
317 } else {
318 p.enc = (*Buffer).enc_slice_int32
319 }
Rob Pikeab5b8022010-06-21 17:47:58 -0700320 p.dec = (*Buffer).dec_slice_int32
David Symonds5b7775e2010-12-01 10:09:04 +1100321 p.packedDec = (*Buffer).dec_slice_packed_int32
Rob Pikeab5b8022010-06-21 17:47:58 -0700322 p.alignof = unsafe.Alignof(vint32)
323 p.sizeof = startSize * unsafe.Sizeof(vint32)
324 case 64:
David Symonds5b7775e2010-12-01 10:09:04 +1100325 if p.Packed {
326 p.enc = (*Buffer).enc_slice_packed_int64
327 } else {
328 p.enc = (*Buffer).enc_slice_int64
329 }
Rob Pikeab5b8022010-06-21 17:47:58 -0700330 p.dec = (*Buffer).dec_slice_int64
David Symonds5b7775e2010-12-01 10:09:04 +1100331 p.packedDec = (*Buffer).dec_slice_packed_int64
Rob Pikeab5b8022010-06-21 17:47:58 -0700332 p.alignof = unsafe.Alignof(vint64)
333 p.sizeof = startSize * unsafe.Sizeof(vint64)
334 case 8:
335 if t2.Kind() == reflect.Uint8 {
336 p.enc = (*Buffer).enc_slice_byte
337 p.dec = (*Buffer).dec_slice_byte
338 p.alignof = unsafe.Alignof(vbyte)
339 p.sizeof = startSize * unsafe.Sizeof(vbyte)
340 }
341 default:
342 goto BadSliceType
343 }
344 case *reflect.FloatType:
345 switch t2.Bits() {
346 case 32:
David Symonds5b7775e2010-12-01 10:09:04 +1100347 // can just treat them as bits
348 if p.Packed {
349 p.enc = (*Buffer).enc_slice_packed_int32
350 } else {
351 p.enc = (*Buffer).enc_slice_int32
352 }
Rob Pikeab5b8022010-06-21 17:47:58 -0700353 p.dec = (*Buffer).dec_slice_int32
David Symonds5b7775e2010-12-01 10:09:04 +1100354 p.packedDec = (*Buffer).dec_slice_packed_int32
Rob Pikeab5b8022010-06-21 17:47:58 -0700355 p.alignof = unsafe.Alignof(vfloat32)
356 p.sizeof = startSize * unsafe.Sizeof(vfloat32)
357 case 64:
David Symonds5b7775e2010-12-01 10:09:04 +1100358 // can just treat them as bits
359 if p.Packed {
360 p.enc = (*Buffer).enc_slice_packed_int64
361 } else {
362 p.enc = (*Buffer).enc_slice_int64
363 }
Rob Pikeab5b8022010-06-21 17:47:58 -0700364 p.dec = (*Buffer).dec_slice_int64
David Symonds5b7775e2010-12-01 10:09:04 +1100365 p.packedDec = (*Buffer).dec_slice_packed_int64
Rob Pikeab5b8022010-06-21 17:47:58 -0700366 p.alignof = unsafe.Alignof(vfloat64)
367 p.sizeof = startSize * unsafe.Sizeof(vfloat64)
368 default:
369 goto BadSliceType
370 }
Rob Pikeaaa3a622010-03-20 22:32:34 -0700371 case *reflect.StringType:
372 p.enc = (*Buffer).enc_slice_string
373 p.dec = (*Buffer).dec_slice_string
374 p.alignof = unsafe.Alignof(vstring)
375 p.sizeof = startSize * unsafe.Sizeof(vstring)
376 case *reflect.PtrType:
377 switch t3 := t2.Elem().(type) {
378 default:
Rob Pikeab5b8022010-06-21 17:47:58 -0700379 fmt.Fprintf(os.Stderr, "proto: no ptr oenc for %T -> %T -> %T\n", t1, t2, t3)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700380 break
381 case *reflect.StructType:
382 p.stype = t2
383 p.enc = (*Buffer).enc_slice_struct_group
384 p.dec = (*Buffer).dec_slice_struct_group
385 if p.Wire == "bytes" {
386 p.enc = (*Buffer).enc_slice_struct_message
387 p.dec = (*Buffer).dec_slice_struct_message
388 }
389 p.alignof = unsafe.Alignof(vslice)
390 p.sizeof = startSize * unsafe.Sizeof(vslice)
391 }
392 case *reflect.SliceType:
Rob Pikeab5b8022010-06-21 17:47:58 -0700393 switch t2.Elem().Kind() {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700394 default:
Rob Pikeab5b8022010-06-21 17:47:58 -0700395 fmt.Fprintf(os.Stderr, "proto: no slice elem oenc for %T -> %T -> %T\n", t1, t2, t2.Elem())
Rob Pikeaaa3a622010-03-20 22:32:34 -0700396 break
Rob Pikeab5b8022010-06-21 17:47:58 -0700397 case reflect.Uint8:
Rob Pikeaaa3a622010-03-20 22:32:34 -0700398 p.enc = (*Buffer).enc_slice_slice_byte
399 p.dec = (*Buffer).dec_slice_slice_byte
400 p.alignof = unsafe.Alignof(vslice)
401 p.sizeof = startSize * unsafe.Sizeof(vslice)
402 }
403 }
404 }
405
406 // precalculate tag code
David Symonds5b7775e2010-12-01 10:09:04 +1100407 wire := p.WireType
408 if p.Packed {
409 wire = WireBytes
410 }
411 x := p.Tag<<3 | wire
Rob Pikeaaa3a622010-03-20 22:32:34 -0700412 i := 0
413 for i = 0; x > 127; i++ {
414 p.tagbuf[i] = 0x80 | uint8(x&0x7F)
415 x >>= 7
416 }
417 p.tagbuf[i] = uint8(x)
418 p.tagcode = p.tagbuf[0 : i+1]
419}
420
421// Init populates the properties from a protocol buffer struct field.
422func (p *Properties) Init(typ reflect.Type, name, tag string, offset uintptr) {
423 // "PB(bytes,49,opt,def=hello!)"
424 // TODO: should not assume the only thing is PB(...)
425 p.Name = name
426 p.OrigName = name
427 p.offset = offset
428
429 if len(tag) < 4 || tag[0:3] != "PB(" || tag[len(tag)-1] != ')' {
430 return
431 }
432 p.Parse(tag[3 : len(tag)-1])
433 p.setEncAndDec(typ)
434}
435
436var (
437 mutex sync.Mutex
438 propertiesMap = make(map[*reflect.StructType]*StructProperties)
439)
440
441// GetProperties returns the list of properties for the type represented by t.
442func GetProperties(t *reflect.StructType) *StructProperties {
443 mutex.Lock()
444 if prop, ok := propertiesMap[t]; ok {
445 mutex.Unlock()
446 stats.Chit++
447 return prop
448 }
449 stats.Cmiss++
450
451 prop := new(StructProperties)
452
453 // build properties
454 prop.Prop = make([]*Properties, t.NumField())
David Symonds79eae332010-10-16 11:33:20 +1100455 prop.origNames = make(map[string]int)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700456 for i := 0; i < t.NumField(); i++ {
457 f := t.Field(i)
458 p := new(Properties)
459 p.Init(f.Type, f.Name, f.Tag, f.Offset)
460 if f.Name == "XXX_extensions" { // special case
461 var vmap map[int32][]byte
462 p.enc = (*Buffer).enc_map
463 p.dec = nil // not needed
464 p.alignof = unsafe.Alignof(vmap)
465 p.sizeof = unsafe.Sizeof(vmap)
466 }
467 prop.Prop[i] = p
David Symonds79eae332010-10-16 11:33:20 +1100468 prop.origNames[p.OrigName] = i
Rob Pikeaaa3a622010-03-20 22:32:34 -0700469 if debug {
470 print(i, " ", f.Name, " ", t.String(), " ")
471 if p.Tag > 0 {
472 print(p.String())
473 }
474 print("\n")
475 }
476 if p.enc == nil && !strings.HasPrefix(f.Name, "XXX_") {
477 fmt.Fprintln(os.Stderr, "proto: no encoder for", f.Name, f.Type.String(), "[GetProperties]")
478 }
479 }
480
481 // build required counts
482 // build scratch offsets
483 // build tags
484 reqCount := 0
485 scratch := uintptr(0)
486 prop.tags = make(map[int]int)
487 for i, p := range prop.Prop {
488 if p.Required {
489 reqCount++
490 }
491 scratch = align(scratch, p.alignof)
492 p.scratch = scratch
493 scratch += uintptr(p.sizeof)
494 prop.tags[p.Tag] = i
495 }
496 prop.reqCount = reqCount
497 prop.nscratch = scratch
498
499 propertiesMap[t] = prop
500 mutex.Unlock()
501 return prop
502}
503
504// Alignment of the data in the scratch area. It doesn't have to be
505// exact, just conservative. Returns the first number >= o that divides s.
506func align(o uintptr, s int) uintptr {
507 if s != 0 {
508 for o%uintptr(s) != 0 {
509 o++
510 }
511 }
512 return o
513}
514
515// Return the field index of the named field.
516// Returns nil if there is no such field.
517func fieldIndex(t *reflect.StructType, name string) []int {
518 if field, ok := t.FieldByName(name); ok {
519 return field.Index
520 }
521 return nil
522}
523
524// Return the Properties object for the x[0]'th field of the structure.
525func propByIndex(t *reflect.StructType, x []int) *Properties {
526 if len(x) != 1 {
527 fmt.Fprintf(os.Stderr, "proto: field index dimension %d (not 1) for type %s\n", len(x), t)
528 return nil
529 }
530 prop := GetProperties(t)
531 return prop.Prop[x[0]]
532}
533
534// Get the address and type of a pointer to the structure from an interface.
535// unsafe.Reflect can do this, but does multiple mallocs.
536func getbase(pb interface{}) (t *reflect.PtrType, b uintptr, err os.Error) {
537 // get pointer
538 x := *(*[2]uintptr)(unsafe.Pointer(&pb))
539 b = x[1]
540 if b == 0 {
541 err = ErrNil
542 return
543 }
544
545 // get the reflect type of the struct.
546 t1 := unsafe.Typeof(pb).(*runtime.PtrType)
547 t = (*reflect.PtrType)(unsafe.Pointer(t1))
548 return
549}
550
551// Allocate the aux space containing all the decoded data. The structure
552// handed into Unmarshal is filled with pointers to this newly allocated
553// data.
554func getsbase(prop *StructProperties) uintptr {
555 var vbyteptr *byte
556 if prop.nscratch == 0 {
557 return 0
558 }
559
560 // allocate the decode space as pointers
561 // so that the GC will scan it for pointers
562 n := uintptr(unsafe.Sizeof(vbyteptr))
563 b := make([]*byte, (prop.nscratch+n-1)/n)
564 sbase := uintptr(unsafe.Pointer(&b[0]))
565 return sbase
566}
567
568// A global registry of enum types.
569// The generated code will register the generated maps by calling RegisterEnum.
570
571var enumNameMaps = make(map[string]map[int32]string)
572var enumValueMaps = make(map[string]map[string]int32)
573
574// RegisterEnum is called from the generated code to install the enum descriptor
575// maps into the global table to aid parsing ASCII protocol buffers.
576func RegisterEnum(typeName string, nameMap map[int32]string, valueMap map[string]int32) {
577 if _, ok := enumNameMaps[typeName]; ok {
Rob Pike79c63792010-03-24 17:48:35 -0700578 panic("proto: duplicate enum registered: " + typeName)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700579 }
580 enumNameMaps[typeName] = nameMap
581 enumValueMaps[typeName] = valueMap
582}