Home | History | Annotate | Download | only in proto
      1 // Go support for Protocol Buffers - Google's data interchange format
      2 //
      3 // Copyright 2016 The Go Authors.  All rights reserved.
      4 // https://github.com/golang/protobuf
      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 
     32 package proto
     33 
     34 import (
     35 	"fmt"
     36 	"reflect"
     37 	"strings"
     38 	"sync"
     39 	"sync/atomic"
     40 )
     41 
     42 // Merge merges the src message into dst.
     43 // This assumes that dst and src of the same type and are non-nil.
     44 func (a *InternalMessageInfo) Merge(dst, src Message) {
     45 	mi := atomicLoadMergeInfo(&a.merge)
     46 	if mi == nil {
     47 		mi = getMergeInfo(reflect.TypeOf(dst).Elem())
     48 		atomicStoreMergeInfo(&a.merge, mi)
     49 	}
     50 	mi.merge(toPointer(&dst), toPointer(&src))
     51 }
     52 
     53 type mergeInfo struct {
     54 	typ reflect.Type
     55 
     56 	initialized int32 // 0: only typ is valid, 1: everything is valid
     57 	lock        sync.Mutex
     58 
     59 	fields       []mergeFieldInfo
     60 	unrecognized field // Offset of XXX_unrecognized
     61 }
     62 
     63 type mergeFieldInfo struct {
     64 	field field // Offset of field, guaranteed to be valid
     65 
     66 	// isPointer reports whether the value in the field is a pointer.
     67 	// This is true for the following situations:
     68 	//	* Pointer to struct
     69 	//	* Pointer to basic type (proto2 only)
     70 	//	* Slice (first value in slice header is a pointer)
     71 	//	* String (first value in string header is a pointer)
     72 	isPointer bool
     73 
     74 	// basicWidth reports the width of the field assuming that it is directly
     75 	// embedded in the struct (as is the case for basic types in proto3).
     76 	// The possible values are:
     77 	// 	0: invalid
     78 	//	1: bool
     79 	//	4: int32, uint32, float32
     80 	//	8: int64, uint64, float64
     81 	basicWidth int
     82 
     83 	// Where dst and src are pointers to the types being merged.
     84 	merge func(dst, src pointer)
     85 }
     86 
     87 var (
     88 	mergeInfoMap  = map[reflect.Type]*mergeInfo{}
     89 	mergeInfoLock sync.Mutex
     90 )
     91 
     92 func getMergeInfo(t reflect.Type) *mergeInfo {
     93 	mergeInfoLock.Lock()
     94 	defer mergeInfoLock.Unlock()
     95 	mi := mergeInfoMap[t]
     96 	if mi == nil {
     97 		mi = &mergeInfo{typ: t}
     98 		mergeInfoMap[t] = mi
     99 	}
    100 	return mi
    101 }
    102 
    103 // merge merges src into dst assuming they are both of type *mi.typ.
    104 func (mi *mergeInfo) merge(dst, src pointer) {
    105 	if dst.isNil() {
    106 		panic("proto: nil destination")
    107 	}
    108 	if src.isNil() {
    109 		return // Nothing to do.
    110 	}
    111 
    112 	if atomic.LoadInt32(&mi.initialized) == 0 {
    113 		mi.computeMergeInfo()
    114 	}
    115 
    116 	for _, fi := range mi.fields {
    117 		sfp := src.offset(fi.field)
    118 
    119 		// As an optimization, we can avoid the merge function call cost
    120 		// if we know for sure that the source will have no effect
    121 		// by checking if it is the zero value.
    122 		if unsafeAllowed {
    123 			if fi.isPointer && sfp.getPointer().isNil() { // Could be slice or string
    124 				continue
    125 			}
    126 			if fi.basicWidth > 0 {
    127 				switch {
    128 				case fi.basicWidth == 1 && !*sfp.toBool():
    129 					continue
    130 				case fi.basicWidth == 4 && *sfp.toUint32() == 0:
    131 					continue
    132 				case fi.basicWidth == 8 && *sfp.toUint64() == 0:
    133 					continue
    134 				}
    135 			}
    136 		}
    137 
    138 		dfp := dst.offset(fi.field)
    139 		fi.merge(dfp, sfp)
    140 	}
    141 
    142 	// TODO: Make this faster?
    143 	out := dst.asPointerTo(mi.typ).Elem()
    144 	in := src.asPointerTo(mi.typ).Elem()
    145 	if emIn, err := extendable(in.Addr().Interface()); err == nil {
    146 		emOut, _ := extendable(out.Addr().Interface())
    147 		mIn, muIn := emIn.extensionsRead()
    148 		if mIn != nil {
    149 			mOut := emOut.extensionsWrite()
    150 			muIn.Lock()
    151 			mergeExtension(mOut, mIn)
    152 			muIn.Unlock()
    153 		}
    154 	}
    155 
    156 	if mi.unrecognized.IsValid() {
    157 		if b := *src.offset(mi.unrecognized).toBytes(); len(b) > 0 {
    158 			*dst.offset(mi.unrecognized).toBytes() = append([]byte(nil), b...)
    159 		}
    160 	}
    161 }
    162 
    163 func (mi *mergeInfo) computeMergeInfo() {
    164 	mi.lock.Lock()
    165 	defer mi.lock.Unlock()
    166 	if mi.initialized != 0 {
    167 		return
    168 	}
    169 	t := mi.typ
    170 	n := t.NumField()
    171 
    172 	props := GetProperties(t)
    173 	for i := 0; i < n; i++ {
    174 		f := t.Field(i)
    175 		if strings.HasPrefix(f.Name, "XXX_") {
    176 			continue
    177 		}
    178 
    179 		mfi := mergeFieldInfo{field: toField(&f)}
    180 		tf := f.Type
    181 
    182 		// As an optimization, we can avoid the merge function call cost
    183 		// if we know for sure that the source will have no effect
    184 		// by checking if it is the zero value.
    185 		if unsafeAllowed {
    186 			switch tf.Kind() {
    187 			case reflect.Ptr, reflect.Slice, reflect.String:
    188 				// As a special case, we assume slices and strings are pointers
    189 				// since we know that the first field in the SliceSlice or
    190 				// StringHeader is a data pointer.
    191 				mfi.isPointer = true
    192 			case reflect.Bool:
    193 				mfi.basicWidth = 1
    194 			case reflect.Int32, reflect.Uint32, reflect.Float32:
    195 				mfi.basicWidth = 4
    196 			case reflect.Int64, reflect.Uint64, reflect.Float64:
    197 				mfi.basicWidth = 8
    198 			}
    199 		}
    200 
    201 		// Unwrap tf to get at its most basic type.
    202 		var isPointer, isSlice bool
    203 		if tf.Kind() == reflect.Slice && tf.Elem().Kind() != reflect.Uint8 {
    204 			isSlice = true
    205 			tf = tf.Elem()
    206 		}
    207 		if tf.Kind() == reflect.Ptr {
    208 			isPointer = true
    209 			tf = tf.Elem()
    210 		}
    211 		if isPointer && isSlice && tf.Kind() != reflect.Struct {
    212 			panic("both pointer and slice for basic type in " + tf.Name())
    213 		}
    214 
    215 		switch tf.Kind() {
    216 		case reflect.Int32:
    217 			switch {
    218 			case isSlice: // E.g., []int32
    219 				mfi.merge = func(dst, src pointer) {
    220 					// NOTE: toInt32Slice is not defined (see pointer_reflect.go).
    221 					/*
    222 						sfsp := src.toInt32Slice()
    223 						if *sfsp != nil {
    224 							dfsp := dst.toInt32Slice()
    225 							*dfsp = append(*dfsp, *sfsp...)
    226 							if *dfsp == nil {
    227 								*dfsp = []int64{}
    228 							}
    229 						}
    230 					*/
    231 					sfs := src.getInt32Slice()
    232 					if sfs != nil {
    233 						dfs := dst.getInt32Slice()
    234 						dfs = append(dfs, sfs...)
    235 						if dfs == nil {
    236 							dfs = []int32{}
    237 						}
    238 						dst.setInt32Slice(dfs)
    239 					}
    240 				}
    241 			case isPointer: // E.g., *int32
    242 				mfi.merge = func(dst, src pointer) {
    243 					// NOTE: toInt32Ptr is not defined (see pointer_reflect.go).
    244 					/*
    245 						sfpp := src.toInt32Ptr()
    246 						if *sfpp != nil {
    247 							dfpp := dst.toInt32Ptr()
    248 							if *dfpp == nil {
    249 								*dfpp = Int32(**sfpp)
    250 							} else {
    251 								**dfpp = **sfpp
    252 							}
    253 						}
    254 					*/
    255 					sfp := src.getInt32Ptr()
    256 					if sfp != nil {
    257 						dfp := dst.getInt32Ptr()
    258 						if dfp == nil {
    259 							dst.setInt32Ptr(*sfp)
    260 						} else {
    261 							*dfp = *sfp
    262 						}
    263 					}
    264 				}
    265 			default: // E.g., int32
    266 				mfi.merge = func(dst, src pointer) {
    267 					if v := *src.toInt32(); v != 0 {
    268 						*dst.toInt32() = v
    269 					}
    270 				}
    271 			}
    272 		case reflect.Int64:
    273 			switch {
    274 			case isSlice: // E.g., []int64
    275 				mfi.merge = func(dst, src pointer) {
    276 					sfsp := src.toInt64Slice()
    277 					if *sfsp != nil {
    278 						dfsp := dst.toInt64Slice()
    279 						*dfsp = append(*dfsp, *sfsp...)
    280 						if *dfsp == nil {
    281 							*dfsp = []int64{}
    282 						}
    283 					}
    284 				}
    285 			case isPointer: // E.g., *int64
    286 				mfi.merge = func(dst, src pointer) {
    287 					sfpp := src.toInt64Ptr()
    288 					if *sfpp != nil {
    289 						dfpp := dst.toInt64Ptr()
    290 						if *dfpp == nil {
    291 							*dfpp = Int64(**sfpp)
    292 						} else {
    293 							**dfpp = **sfpp
    294 						}
    295 					}
    296 				}
    297 			default: // E.g., int64
    298 				mfi.merge = func(dst, src pointer) {
    299 					if v := *src.toInt64(); v != 0 {
    300 						*dst.toInt64() = v
    301 					}
    302 				}
    303 			}
    304 		case reflect.Uint32:
    305 			switch {
    306 			case isSlice: // E.g., []uint32
    307 				mfi.merge = func(dst, src pointer) {
    308 					sfsp := src.toUint32Slice()
    309 					if *sfsp != nil {
    310 						dfsp := dst.toUint32Slice()
    311 						*dfsp = append(*dfsp, *sfsp...)
    312 						if *dfsp == nil {
    313 							*dfsp = []uint32{}
    314 						}
    315 					}
    316 				}
    317 			case isPointer: // E.g., *uint32
    318 				mfi.merge = func(dst, src pointer) {
    319 					sfpp := src.toUint32Ptr()
    320 					if *sfpp != nil {
    321 						dfpp := dst.toUint32Ptr()
    322 						if *dfpp == nil {
    323 							*dfpp = Uint32(**sfpp)
    324 						} else {
    325 							**dfpp = **sfpp
    326 						}
    327 					}
    328 				}
    329 			default: // E.g., uint32
    330 				mfi.merge = func(dst, src pointer) {
    331 					if v := *src.toUint32(); v != 0 {
    332 						*dst.toUint32() = v
    333 					}
    334 				}
    335 			}
    336 		case reflect.Uint64:
    337 			switch {
    338 			case isSlice: // E.g., []uint64
    339 				mfi.merge = func(dst, src pointer) {
    340 					sfsp := src.toUint64Slice()
    341 					if *sfsp != nil {
    342 						dfsp := dst.toUint64Slice()
    343 						*dfsp = append(*dfsp, *sfsp...)
    344 						if *dfsp == nil {
    345 							*dfsp = []uint64{}
    346 						}
    347 					}
    348 				}
    349 			case isPointer: // E.g., *uint64
    350 				mfi.merge = func(dst, src pointer) {
    351 					sfpp := src.toUint64Ptr()
    352 					if *sfpp != nil {
    353 						dfpp := dst.toUint64Ptr()
    354 						if *dfpp == nil {
    355 							*dfpp = Uint64(**sfpp)
    356 						} else {
    357 							**dfpp = **sfpp
    358 						}
    359 					}
    360 				}
    361 			default: // E.g., uint64
    362 				mfi.merge = func(dst, src pointer) {
    363 					if v := *src.toUint64(); v != 0 {
    364 						*dst.toUint64() = v
    365 					}
    366 				}
    367 			}
    368 		case reflect.Float32:
    369 			switch {
    370 			case isSlice: // E.g., []float32
    371 				mfi.merge = func(dst, src pointer) {
    372 					sfsp := src.toFloat32Slice()
    373 					if *sfsp != nil {
    374 						dfsp := dst.toFloat32Slice()
    375 						*dfsp = append(*dfsp, *sfsp...)
    376 						if *dfsp == nil {
    377 							*dfsp = []float32{}
    378 						}
    379 					}
    380 				}
    381 			case isPointer: // E.g., *float32
    382 				mfi.merge = func(dst, src pointer) {
    383 					sfpp := src.toFloat32Ptr()
    384 					if *sfpp != nil {
    385 						dfpp := dst.toFloat32Ptr()
    386 						if *dfpp == nil {
    387 							*dfpp = Float32(**sfpp)
    388 						} else {
    389 							**dfpp = **sfpp
    390 						}
    391 					}
    392 				}
    393 			default: // E.g., float32
    394 				mfi.merge = func(dst, src pointer) {
    395 					if v := *src.toFloat32(); v != 0 {
    396 						*dst.toFloat32() = v
    397 					}
    398 				}
    399 			}
    400 		case reflect.Float64:
    401 			switch {
    402 			case isSlice: // E.g., []float64
    403 				mfi.merge = func(dst, src pointer) {
    404 					sfsp := src.toFloat64Slice()
    405 					if *sfsp != nil {
    406 						dfsp := dst.toFloat64Slice()
    407 						*dfsp = append(*dfsp, *sfsp...)
    408 						if *dfsp == nil {
    409 							*dfsp = []float64{}
    410 						}
    411 					}
    412 				}
    413 			case isPointer: // E.g., *float64
    414 				mfi.merge = func(dst, src pointer) {
    415 					sfpp := src.toFloat64Ptr()
    416 					if *sfpp != nil {
    417 						dfpp := dst.toFloat64Ptr()
    418 						if *dfpp == nil {
    419 							*dfpp = Float64(**sfpp)
    420 						} else {
    421 							**dfpp = **sfpp
    422 						}
    423 					}
    424 				}
    425 			default: // E.g., float64
    426 				mfi.merge = func(dst, src pointer) {
    427 					if v := *src.toFloat64(); v != 0 {
    428 						*dst.toFloat64() = v
    429 					}
    430 				}
    431 			}
    432 		case reflect.Bool:
    433 			switch {
    434 			case isSlice: // E.g., []bool
    435 				mfi.merge = func(dst, src pointer) {
    436 					sfsp := src.toBoolSlice()
    437 					if *sfsp != nil {
    438 						dfsp := dst.toBoolSlice()
    439 						*dfsp = append(*dfsp, *sfsp...)
    440 						if *dfsp == nil {
    441 							*dfsp = []bool{}
    442 						}
    443 					}
    444 				}
    445 			case isPointer: // E.g., *bool
    446 				mfi.merge = func(dst, src pointer) {
    447 					sfpp := src.toBoolPtr()
    448 					if *sfpp != nil {
    449 						dfpp := dst.toBoolPtr()
    450 						if *dfpp == nil {
    451 							*dfpp = Bool(**sfpp)
    452 						} else {
    453 							**dfpp = **sfpp
    454 						}
    455 					}
    456 				}
    457 			default: // E.g., bool
    458 				mfi.merge = func(dst, src pointer) {
    459 					if v := *src.toBool(); v {
    460 						*dst.toBool() = v
    461 					}
    462 				}
    463 			}
    464 		case reflect.String:
    465 			switch {
    466 			case isSlice: // E.g., []string
    467 				mfi.merge = func(dst, src pointer) {
    468 					sfsp := src.toStringSlice()
    469 					if *sfsp != nil {
    470 						dfsp := dst.toStringSlice()
    471 						*dfsp = append(*dfsp, *sfsp...)
    472 						if *dfsp == nil {
    473 							*dfsp = []string{}
    474 						}
    475 					}
    476 				}
    477 			case isPointer: // E.g., *string
    478 				mfi.merge = func(dst, src pointer) {
    479 					sfpp := src.toStringPtr()
    480 					if *sfpp != nil {
    481 						dfpp := dst.toStringPtr()
    482 						if *dfpp == nil {
    483 							*dfpp = String(**sfpp)
    484 						} else {
    485 							**dfpp = **sfpp
    486 						}
    487 					}
    488 				}
    489 			default: // E.g., string
    490 				mfi.merge = func(dst, src pointer) {
    491 					if v := *src.toString(); v != "" {
    492 						*dst.toString() = v
    493 					}
    494 				}
    495 			}
    496 		case reflect.Slice:
    497 			isProto3 := props.Prop[i].proto3
    498 			switch {
    499 			case isPointer:
    500 				panic("bad pointer in byte slice case in " + tf.Name())
    501 			case tf.Elem().Kind() != reflect.Uint8:
    502 				panic("bad element kind in byte slice case in " + tf.Name())
    503 			case isSlice: // E.g., [][]byte
    504 				mfi.merge = func(dst, src pointer) {
    505 					sbsp := src.toBytesSlice()
    506 					if *sbsp != nil {
    507 						dbsp := dst.toBytesSlice()
    508 						for _, sb := range *sbsp {
    509 							if sb == nil {
    510 								*dbsp = append(*dbsp, nil)
    511 							} else {
    512 								*dbsp = append(*dbsp, append([]byte{}, sb...))
    513 							}
    514 						}
    515 						if *dbsp == nil {
    516 							*dbsp = [][]byte{}
    517 						}
    518 					}
    519 				}
    520 			default: // E.g., []byte
    521 				mfi.merge = func(dst, src pointer) {
    522 					sbp := src.toBytes()
    523 					if *sbp != nil {
    524 						dbp := dst.toBytes()
    525 						if !isProto3 || len(*sbp) > 0 {
    526 							*dbp = append([]byte{}, *sbp...)
    527 						}
    528 					}
    529 				}
    530 			}
    531 		case reflect.Struct:
    532 			switch {
    533 			case !isPointer:
    534 				panic(fmt.Sprintf("message field %s without pointer", tf))
    535 			case isSlice: // E.g., []*pb.T
    536 				mi := getMergeInfo(tf)
    537 				mfi.merge = func(dst, src pointer) {
    538 					sps := src.getPointerSlice()
    539 					if sps != nil {
    540 						dps := dst.getPointerSlice()
    541 						for _, sp := range sps {
    542 							var dp pointer
    543 							if !sp.isNil() {
    544 								dp = valToPointer(reflect.New(tf))
    545 								mi.merge(dp, sp)
    546 							}
    547 							dps = append(dps, dp)
    548 						}
    549 						if dps == nil {
    550 							dps = []pointer{}
    551 						}
    552 						dst.setPointerSlice(dps)
    553 					}
    554 				}
    555 			default: // E.g., *pb.T
    556 				mi := getMergeInfo(tf)
    557 				mfi.merge = func(dst, src pointer) {
    558 					sp := src.getPointer()
    559 					if !sp.isNil() {
    560 						dp := dst.getPointer()
    561 						if dp.isNil() {
    562 							dp = valToPointer(reflect.New(tf))
    563 							dst.setPointer(dp)
    564 						}
    565 						mi.merge(dp, sp)
    566 					}
    567 				}
    568 			}
    569 		case reflect.Map:
    570 			switch {
    571 			case isPointer || isSlice:
    572 				panic("bad pointer or slice in map case in " + tf.Name())
    573 			default: // E.g., map[K]V
    574 				mfi.merge = func(dst, src pointer) {
    575 					sm := src.asPointerTo(tf).Elem()
    576 					if sm.Len() == 0 {
    577 						return
    578 					}
    579 					dm := dst.asPointerTo(tf).Elem()
    580 					if dm.IsNil() {
    581 						dm.Set(reflect.MakeMap(tf))
    582 					}
    583 
    584 					switch tf.Elem().Kind() {
    585 					case reflect.Ptr: // Proto struct (e.g., *T)
    586 						for _, key := range sm.MapKeys() {
    587 							val := sm.MapIndex(key)
    588 							val = reflect.ValueOf(Clone(val.Interface().(Message)))
    589 							dm.SetMapIndex(key, val)
    590 						}
    591 					case reflect.Slice: // E.g. Bytes type (e.g., []byte)
    592 						for _, key := range sm.MapKeys() {
    593 							val := sm.MapIndex(key)
    594 							val = reflect.ValueOf(append([]byte{}, val.Bytes()...))
    595 							dm.SetMapIndex(key, val)
    596 						}
    597 					default: // Basic type (e.g., string)
    598 						for _, key := range sm.MapKeys() {
    599 							val := sm.MapIndex(key)
    600 							dm.SetMapIndex(key, val)
    601 						}
    602 					}
    603 				}
    604 			}
    605 		case reflect.Interface:
    606 			// Must be oneof field.
    607 			switch {
    608 			case isPointer || isSlice:
    609 				panic("bad pointer or slice in interface case in " + tf.Name())
    610 			default: // E.g., interface{}
    611 				// TODO: Make this faster?
    612 				mfi.merge = func(dst, src pointer) {
    613 					su := src.asPointerTo(tf).Elem()
    614 					if !su.IsNil() {
    615 						du := dst.asPointerTo(tf).Elem()
    616 						typ := su.Elem().Type()
    617 						if du.IsNil() || du.Elem().Type() != typ {
    618 							du.Set(reflect.New(typ.Elem())) // Initialize interface if empty
    619 						}
    620 						sv := su.Elem().Elem().Field(0)
    621 						if sv.Kind() == reflect.Ptr && sv.IsNil() {
    622 							return
    623 						}
    624 						dv := du.Elem().Elem().Field(0)
    625 						if dv.Kind() == reflect.Ptr && dv.IsNil() {
    626 							dv.Set(reflect.New(sv.Type().Elem())) // Initialize proto message if empty
    627 						}
    628 						switch sv.Type().Kind() {
    629 						case reflect.Ptr: // Proto struct (e.g., *T)
    630 							Merge(dv.Interface().(Message), sv.Interface().(Message))
    631 						case reflect.Slice: // E.g. Bytes type (e.g., []byte)
    632 							dv.Set(reflect.ValueOf(append([]byte{}, sv.Bytes()...)))
    633 						default: // Basic type (e.g., string)
    634 							dv.Set(sv)
    635 						}
    636 					}
    637 				}
    638 			}
    639 		default:
    640 			panic(fmt.Sprintf("merger not found for type:%s", tf))
    641 		}
    642 		mi.fields = append(mi.fields, mfi)
    643 	}
    644 
    645 	mi.unrecognized = invalidField
    646 	if f, ok := t.FieldByName("XXX_unrecognized"); ok {
    647 		if f.Type != reflect.TypeOf([]byte{}) {
    648 			panic("expected XXX_unrecognized to be of type []byte")
    649 		}
    650 		mi.unrecognized = toField(&f)
    651 	}
    652 
    653 	atomic.StoreInt32(&mi.initialized, 1)
    654 }
    655