v1.38.0 is used by Kubernetes since https://github.com/kubernetes/kubernetes/pull/100488. Signed-off-by: Kazuyoshi Kato <katokazu@amazon.com>
		
			
				
	
	
		
			577 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			577 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// 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 impl
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"math"
 | 
						|
	"math/bits"
 | 
						|
	"reflect"
 | 
						|
	"unicode/utf8"
 | 
						|
 | 
						|
	"google.golang.org/protobuf/encoding/protowire"
 | 
						|
	"google.golang.org/protobuf/internal/encoding/messageset"
 | 
						|
	"google.golang.org/protobuf/internal/flags"
 | 
						|
	"google.golang.org/protobuf/internal/genid"
 | 
						|
	"google.golang.org/protobuf/internal/strs"
 | 
						|
	pref "google.golang.org/protobuf/reflect/protoreflect"
 | 
						|
	preg "google.golang.org/protobuf/reflect/protoregistry"
 | 
						|
	piface "google.golang.org/protobuf/runtime/protoiface"
 | 
						|
)
 | 
						|
 | 
						|
// ValidationStatus is the result of validating the wire-format encoding of a message.
 | 
						|
type ValidationStatus int
 | 
						|
 | 
						|
const (
 | 
						|
	// ValidationUnknown indicates that unmarshaling the message might succeed or fail.
 | 
						|
	// The validator was unable to render a judgement.
 | 
						|
	//
 | 
						|
	// The only causes of this status are an aberrant message type appearing somewhere
 | 
						|
	// in the message or a failure in the extension resolver.
 | 
						|
	ValidationUnknown ValidationStatus = iota + 1
 | 
						|
 | 
						|
	// ValidationInvalid indicates that unmarshaling the message will fail.
 | 
						|
	ValidationInvalid
 | 
						|
 | 
						|
	// ValidationValid indicates that unmarshaling the message will succeed.
 | 
						|
	ValidationValid
 | 
						|
)
 | 
						|
 | 
						|
func (v ValidationStatus) String() string {
 | 
						|
	switch v {
 | 
						|
	case ValidationUnknown:
 | 
						|
		return "ValidationUnknown"
 | 
						|
	case ValidationInvalid:
 | 
						|
		return "ValidationInvalid"
 | 
						|
	case ValidationValid:
 | 
						|
		return "ValidationValid"
 | 
						|
	default:
 | 
						|
		return fmt.Sprintf("ValidationStatus(%d)", int(v))
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Validate determines whether the contents of the buffer are a valid wire encoding
 | 
						|
// of the message type.
 | 
						|
//
 | 
						|
// This function is exposed for testing.
 | 
						|
func Validate(mt pref.MessageType, in piface.UnmarshalInput) (out piface.UnmarshalOutput, _ ValidationStatus) {
 | 
						|
	mi, ok := mt.(*MessageInfo)
 | 
						|
	if !ok {
 | 
						|
		return out, ValidationUnknown
 | 
						|
	}
 | 
						|
	if in.Resolver == nil {
 | 
						|
		in.Resolver = preg.GlobalTypes
 | 
						|
	}
 | 
						|
	o, st := mi.validate(in.Buf, 0, unmarshalOptions{
 | 
						|
		flags:    in.Flags,
 | 
						|
		resolver: in.Resolver,
 | 
						|
	})
 | 
						|
	if o.initialized {
 | 
						|
		out.Flags |= piface.UnmarshalInitialized
 | 
						|
	}
 | 
						|
	return out, st
 | 
						|
}
 | 
						|
 | 
						|
type validationInfo struct {
 | 
						|
	mi               *MessageInfo
 | 
						|
	typ              validationType
 | 
						|
	keyType, valType validationType
 | 
						|
 | 
						|
	// For non-required fields, requiredBit is 0.
 | 
						|
	//
 | 
						|
	// For required fields, requiredBit's nth bit is set, where n is a
 | 
						|
	// unique index in the range [0, MessageInfo.numRequiredFields).
 | 
						|
	//
 | 
						|
	// If there are more than 64 required fields, requiredBit is 0.
 | 
						|
	requiredBit uint64
 | 
						|
}
 | 
						|
 | 
						|
type validationType uint8
 | 
						|
 | 
						|
const (
 | 
						|
	validationTypeOther validationType = iota
 | 
						|
	validationTypeMessage
 | 
						|
	validationTypeGroup
 | 
						|
	validationTypeMap
 | 
						|
	validationTypeRepeatedVarint
 | 
						|
	validationTypeRepeatedFixed32
 | 
						|
	validationTypeRepeatedFixed64
 | 
						|
	validationTypeVarint
 | 
						|
	validationTypeFixed32
 | 
						|
	validationTypeFixed64
 | 
						|
	validationTypeBytes
 | 
						|
	validationTypeUTF8String
 | 
						|
	validationTypeMessageSetItem
 | 
						|
)
 | 
						|
 | 
						|
func newFieldValidationInfo(mi *MessageInfo, si structInfo, fd pref.FieldDescriptor, ft reflect.Type) validationInfo {
 | 
						|
	var vi validationInfo
 | 
						|
	switch {
 | 
						|
	case fd.ContainingOneof() != nil && !fd.ContainingOneof().IsSynthetic():
 | 
						|
		switch fd.Kind() {
 | 
						|
		case pref.MessageKind:
 | 
						|
			vi.typ = validationTypeMessage
 | 
						|
			if ot, ok := si.oneofWrappersByNumber[fd.Number()]; ok {
 | 
						|
				vi.mi = getMessageInfo(ot.Field(0).Type)
 | 
						|
			}
 | 
						|
		case pref.GroupKind:
 | 
						|
			vi.typ = validationTypeGroup
 | 
						|
			if ot, ok := si.oneofWrappersByNumber[fd.Number()]; ok {
 | 
						|
				vi.mi = getMessageInfo(ot.Field(0).Type)
 | 
						|
			}
 | 
						|
		case pref.StringKind:
 | 
						|
			if strs.EnforceUTF8(fd) {
 | 
						|
				vi.typ = validationTypeUTF8String
 | 
						|
			}
 | 
						|
		}
 | 
						|
	default:
 | 
						|
		vi = newValidationInfo(fd, ft)
 | 
						|
	}
 | 
						|
	if fd.Cardinality() == pref.Required {
 | 
						|
		// Avoid overflow. The required field check is done with a 64-bit mask, with
 | 
						|
		// any message containing more than 64 required fields always reported as
 | 
						|
		// potentially uninitialized, so it is not important to get a precise count
 | 
						|
		// of the required fields past 64.
 | 
						|
		if mi.numRequiredFields < math.MaxUint8 {
 | 
						|
			mi.numRequiredFields++
 | 
						|
			vi.requiredBit = 1 << (mi.numRequiredFields - 1)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return vi
 | 
						|
}
 | 
						|
 | 
						|
func newValidationInfo(fd pref.FieldDescriptor, ft reflect.Type) validationInfo {
 | 
						|
	var vi validationInfo
 | 
						|
	switch {
 | 
						|
	case fd.IsList():
 | 
						|
		switch fd.Kind() {
 | 
						|
		case pref.MessageKind:
 | 
						|
			vi.typ = validationTypeMessage
 | 
						|
			if ft.Kind() == reflect.Slice {
 | 
						|
				vi.mi = getMessageInfo(ft.Elem())
 | 
						|
			}
 | 
						|
		case pref.GroupKind:
 | 
						|
			vi.typ = validationTypeGroup
 | 
						|
			if ft.Kind() == reflect.Slice {
 | 
						|
				vi.mi = getMessageInfo(ft.Elem())
 | 
						|
			}
 | 
						|
		case pref.StringKind:
 | 
						|
			vi.typ = validationTypeBytes
 | 
						|
			if strs.EnforceUTF8(fd) {
 | 
						|
				vi.typ = validationTypeUTF8String
 | 
						|
			}
 | 
						|
		default:
 | 
						|
			switch wireTypes[fd.Kind()] {
 | 
						|
			case protowire.VarintType:
 | 
						|
				vi.typ = validationTypeRepeatedVarint
 | 
						|
			case protowire.Fixed32Type:
 | 
						|
				vi.typ = validationTypeRepeatedFixed32
 | 
						|
			case protowire.Fixed64Type:
 | 
						|
				vi.typ = validationTypeRepeatedFixed64
 | 
						|
			}
 | 
						|
		}
 | 
						|
	case fd.IsMap():
 | 
						|
		vi.typ = validationTypeMap
 | 
						|
		switch fd.MapKey().Kind() {
 | 
						|
		case pref.StringKind:
 | 
						|
			if strs.EnforceUTF8(fd) {
 | 
						|
				vi.keyType = validationTypeUTF8String
 | 
						|
			}
 | 
						|
		}
 | 
						|
		switch fd.MapValue().Kind() {
 | 
						|
		case pref.MessageKind:
 | 
						|
			vi.valType = validationTypeMessage
 | 
						|
			if ft.Kind() == reflect.Map {
 | 
						|
				vi.mi = getMessageInfo(ft.Elem())
 | 
						|
			}
 | 
						|
		case pref.StringKind:
 | 
						|
			if strs.EnforceUTF8(fd) {
 | 
						|
				vi.valType = validationTypeUTF8String
 | 
						|
			}
 | 
						|
		}
 | 
						|
	default:
 | 
						|
		switch fd.Kind() {
 | 
						|
		case pref.MessageKind:
 | 
						|
			vi.typ = validationTypeMessage
 | 
						|
			if !fd.IsWeak() {
 | 
						|
				vi.mi = getMessageInfo(ft)
 | 
						|
			}
 | 
						|
		case pref.GroupKind:
 | 
						|
			vi.typ = validationTypeGroup
 | 
						|
			vi.mi = getMessageInfo(ft)
 | 
						|
		case pref.StringKind:
 | 
						|
			vi.typ = validationTypeBytes
 | 
						|
			if strs.EnforceUTF8(fd) {
 | 
						|
				vi.typ = validationTypeUTF8String
 | 
						|
			}
 | 
						|
		default:
 | 
						|
			switch wireTypes[fd.Kind()] {
 | 
						|
			case protowire.VarintType:
 | 
						|
				vi.typ = validationTypeVarint
 | 
						|
			case protowire.Fixed32Type:
 | 
						|
				vi.typ = validationTypeFixed32
 | 
						|
			case protowire.Fixed64Type:
 | 
						|
				vi.typ = validationTypeFixed64
 | 
						|
			case protowire.BytesType:
 | 
						|
				vi.typ = validationTypeBytes
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return vi
 | 
						|
}
 | 
						|
 | 
						|
func (mi *MessageInfo) validate(b []byte, groupTag protowire.Number, opts unmarshalOptions) (out unmarshalOutput, result ValidationStatus) {
 | 
						|
	mi.init()
 | 
						|
	type validationState struct {
 | 
						|
		typ              validationType
 | 
						|
		keyType, valType validationType
 | 
						|
		endGroup         protowire.Number
 | 
						|
		mi               *MessageInfo
 | 
						|
		tail             []byte
 | 
						|
		requiredMask     uint64
 | 
						|
	}
 | 
						|
 | 
						|
	// Pre-allocate some slots to avoid repeated slice reallocation.
 | 
						|
	states := make([]validationState, 0, 16)
 | 
						|
	states = append(states, validationState{
 | 
						|
		typ: validationTypeMessage,
 | 
						|
		mi:  mi,
 | 
						|
	})
 | 
						|
	if groupTag > 0 {
 | 
						|
		states[0].typ = validationTypeGroup
 | 
						|
		states[0].endGroup = groupTag
 | 
						|
	}
 | 
						|
	initialized := true
 | 
						|
	start := len(b)
 | 
						|
State:
 | 
						|
	for len(states) > 0 {
 | 
						|
		st := &states[len(states)-1]
 | 
						|
		for len(b) > 0 {
 | 
						|
			// Parse the tag (field number and wire type).
 | 
						|
			var tag uint64
 | 
						|
			if b[0] < 0x80 {
 | 
						|
				tag = uint64(b[0])
 | 
						|
				b = b[1:]
 | 
						|
			} else if len(b) >= 2 && b[1] < 128 {
 | 
						|
				tag = uint64(b[0]&0x7f) + uint64(b[1])<<7
 | 
						|
				b = b[2:]
 | 
						|
			} else {
 | 
						|
				var n int
 | 
						|
				tag, n = protowire.ConsumeVarint(b)
 | 
						|
				if n < 0 {
 | 
						|
					return out, ValidationInvalid
 | 
						|
				}
 | 
						|
				b = b[n:]
 | 
						|
			}
 | 
						|
			var num protowire.Number
 | 
						|
			if n := tag >> 3; n < uint64(protowire.MinValidNumber) || n > uint64(protowire.MaxValidNumber) {
 | 
						|
				return out, ValidationInvalid
 | 
						|
			} else {
 | 
						|
				num = protowire.Number(n)
 | 
						|
			}
 | 
						|
			wtyp := protowire.Type(tag & 7)
 | 
						|
 | 
						|
			if wtyp == protowire.EndGroupType {
 | 
						|
				if st.endGroup == num {
 | 
						|
					goto PopState
 | 
						|
				}
 | 
						|
				return out, ValidationInvalid
 | 
						|
			}
 | 
						|
			var vi validationInfo
 | 
						|
			switch {
 | 
						|
			case st.typ == validationTypeMap:
 | 
						|
				switch num {
 | 
						|
				case genid.MapEntry_Key_field_number:
 | 
						|
					vi.typ = st.keyType
 | 
						|
				case genid.MapEntry_Value_field_number:
 | 
						|
					vi.typ = st.valType
 | 
						|
					vi.mi = st.mi
 | 
						|
					vi.requiredBit = 1
 | 
						|
				}
 | 
						|
			case flags.ProtoLegacy && st.mi.isMessageSet:
 | 
						|
				switch num {
 | 
						|
				case messageset.FieldItem:
 | 
						|
					vi.typ = validationTypeMessageSetItem
 | 
						|
				}
 | 
						|
			default:
 | 
						|
				var f *coderFieldInfo
 | 
						|
				if int(num) < len(st.mi.denseCoderFields) {
 | 
						|
					f = st.mi.denseCoderFields[num]
 | 
						|
				} else {
 | 
						|
					f = st.mi.coderFields[num]
 | 
						|
				}
 | 
						|
				if f != nil {
 | 
						|
					vi = f.validation
 | 
						|
					if vi.typ == validationTypeMessage && vi.mi == nil {
 | 
						|
						// Probable weak field.
 | 
						|
						//
 | 
						|
						// TODO: Consider storing the results of this lookup somewhere
 | 
						|
						// rather than recomputing it on every validation.
 | 
						|
						fd := st.mi.Desc.Fields().ByNumber(num)
 | 
						|
						if fd == nil || !fd.IsWeak() {
 | 
						|
							break
 | 
						|
						}
 | 
						|
						messageName := fd.Message().FullName()
 | 
						|
						messageType, err := preg.GlobalTypes.FindMessageByName(messageName)
 | 
						|
						switch err {
 | 
						|
						case nil:
 | 
						|
							vi.mi, _ = messageType.(*MessageInfo)
 | 
						|
						case preg.NotFound:
 | 
						|
							vi.typ = validationTypeBytes
 | 
						|
						default:
 | 
						|
							return out, ValidationUnknown
 | 
						|
						}
 | 
						|
					}
 | 
						|
					break
 | 
						|
				}
 | 
						|
				// Possible extension field.
 | 
						|
				//
 | 
						|
				// TODO: We should return ValidationUnknown when:
 | 
						|
				//   1. The resolver is not frozen. (More extensions may be added to it.)
 | 
						|
				//   2. The resolver returns preg.NotFound.
 | 
						|
				// In this case, a type added to the resolver in the future could cause
 | 
						|
				// unmarshaling to begin failing. Supporting this requires some way to
 | 
						|
				// determine if the resolver is frozen.
 | 
						|
				xt, err := opts.resolver.FindExtensionByNumber(st.mi.Desc.FullName(), num)
 | 
						|
				if err != nil && err != preg.NotFound {
 | 
						|
					return out, ValidationUnknown
 | 
						|
				}
 | 
						|
				if err == nil {
 | 
						|
					vi = getExtensionFieldInfo(xt).validation
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if vi.requiredBit != 0 {
 | 
						|
				// Check that the field has a compatible wire type.
 | 
						|
				// We only need to consider non-repeated field types,
 | 
						|
				// since repeated fields (and maps) can never be required.
 | 
						|
				ok := false
 | 
						|
				switch vi.typ {
 | 
						|
				case validationTypeVarint:
 | 
						|
					ok = wtyp == protowire.VarintType
 | 
						|
				case validationTypeFixed32:
 | 
						|
					ok = wtyp == protowire.Fixed32Type
 | 
						|
				case validationTypeFixed64:
 | 
						|
					ok = wtyp == protowire.Fixed64Type
 | 
						|
				case validationTypeBytes, validationTypeUTF8String, validationTypeMessage:
 | 
						|
					ok = wtyp == protowire.BytesType
 | 
						|
				case validationTypeGroup:
 | 
						|
					ok = wtyp == protowire.StartGroupType
 | 
						|
				}
 | 
						|
				if ok {
 | 
						|
					st.requiredMask |= vi.requiredBit
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			switch wtyp {
 | 
						|
			case protowire.VarintType:
 | 
						|
				if len(b) >= 10 {
 | 
						|
					switch {
 | 
						|
					case b[0] < 0x80:
 | 
						|
						b = b[1:]
 | 
						|
					case b[1] < 0x80:
 | 
						|
						b = b[2:]
 | 
						|
					case b[2] < 0x80:
 | 
						|
						b = b[3:]
 | 
						|
					case b[3] < 0x80:
 | 
						|
						b = b[4:]
 | 
						|
					case b[4] < 0x80:
 | 
						|
						b = b[5:]
 | 
						|
					case b[5] < 0x80:
 | 
						|
						b = b[6:]
 | 
						|
					case b[6] < 0x80:
 | 
						|
						b = b[7:]
 | 
						|
					case b[7] < 0x80:
 | 
						|
						b = b[8:]
 | 
						|
					case b[8] < 0x80:
 | 
						|
						b = b[9:]
 | 
						|
					case b[9] < 0x80 && b[9] < 2:
 | 
						|
						b = b[10:]
 | 
						|
					default:
 | 
						|
						return out, ValidationInvalid
 | 
						|
					}
 | 
						|
				} else {
 | 
						|
					switch {
 | 
						|
					case len(b) > 0 && b[0] < 0x80:
 | 
						|
						b = b[1:]
 | 
						|
					case len(b) > 1 && b[1] < 0x80:
 | 
						|
						b = b[2:]
 | 
						|
					case len(b) > 2 && b[2] < 0x80:
 | 
						|
						b = b[3:]
 | 
						|
					case len(b) > 3 && b[3] < 0x80:
 | 
						|
						b = b[4:]
 | 
						|
					case len(b) > 4 && b[4] < 0x80:
 | 
						|
						b = b[5:]
 | 
						|
					case len(b) > 5 && b[5] < 0x80:
 | 
						|
						b = b[6:]
 | 
						|
					case len(b) > 6 && b[6] < 0x80:
 | 
						|
						b = b[7:]
 | 
						|
					case len(b) > 7 && b[7] < 0x80:
 | 
						|
						b = b[8:]
 | 
						|
					case len(b) > 8 && b[8] < 0x80:
 | 
						|
						b = b[9:]
 | 
						|
					case len(b) > 9 && b[9] < 2:
 | 
						|
						b = b[10:]
 | 
						|
					default:
 | 
						|
						return out, ValidationInvalid
 | 
						|
					}
 | 
						|
				}
 | 
						|
				continue State
 | 
						|
			case protowire.BytesType:
 | 
						|
				var size uint64
 | 
						|
				if len(b) >= 1 && b[0] < 0x80 {
 | 
						|
					size = uint64(b[0])
 | 
						|
					b = b[1:]
 | 
						|
				} else if len(b) >= 2 && b[1] < 128 {
 | 
						|
					size = uint64(b[0]&0x7f) + uint64(b[1])<<7
 | 
						|
					b = b[2:]
 | 
						|
				} else {
 | 
						|
					var n int
 | 
						|
					size, n = protowire.ConsumeVarint(b)
 | 
						|
					if n < 0 {
 | 
						|
						return out, ValidationInvalid
 | 
						|
					}
 | 
						|
					b = b[n:]
 | 
						|
				}
 | 
						|
				if size > uint64(len(b)) {
 | 
						|
					return out, ValidationInvalid
 | 
						|
				}
 | 
						|
				v := b[:size]
 | 
						|
				b = b[size:]
 | 
						|
				switch vi.typ {
 | 
						|
				case validationTypeMessage:
 | 
						|
					if vi.mi == nil {
 | 
						|
						return out, ValidationUnknown
 | 
						|
					}
 | 
						|
					vi.mi.init()
 | 
						|
					fallthrough
 | 
						|
				case validationTypeMap:
 | 
						|
					if vi.mi != nil {
 | 
						|
						vi.mi.init()
 | 
						|
					}
 | 
						|
					states = append(states, validationState{
 | 
						|
						typ:     vi.typ,
 | 
						|
						keyType: vi.keyType,
 | 
						|
						valType: vi.valType,
 | 
						|
						mi:      vi.mi,
 | 
						|
						tail:    b,
 | 
						|
					})
 | 
						|
					b = v
 | 
						|
					continue State
 | 
						|
				case validationTypeRepeatedVarint:
 | 
						|
					// Packed field.
 | 
						|
					for len(v) > 0 {
 | 
						|
						_, n := protowire.ConsumeVarint(v)
 | 
						|
						if n < 0 {
 | 
						|
							return out, ValidationInvalid
 | 
						|
						}
 | 
						|
						v = v[n:]
 | 
						|
					}
 | 
						|
				case validationTypeRepeatedFixed32:
 | 
						|
					// Packed field.
 | 
						|
					if len(v)%4 != 0 {
 | 
						|
						return out, ValidationInvalid
 | 
						|
					}
 | 
						|
				case validationTypeRepeatedFixed64:
 | 
						|
					// Packed field.
 | 
						|
					if len(v)%8 != 0 {
 | 
						|
						return out, ValidationInvalid
 | 
						|
					}
 | 
						|
				case validationTypeUTF8String:
 | 
						|
					if !utf8.Valid(v) {
 | 
						|
						return out, ValidationInvalid
 | 
						|
					}
 | 
						|
				}
 | 
						|
			case protowire.Fixed32Type:
 | 
						|
				if len(b) < 4 {
 | 
						|
					return out, ValidationInvalid
 | 
						|
				}
 | 
						|
				b = b[4:]
 | 
						|
			case protowire.Fixed64Type:
 | 
						|
				if len(b) < 8 {
 | 
						|
					return out, ValidationInvalid
 | 
						|
				}
 | 
						|
				b = b[8:]
 | 
						|
			case protowire.StartGroupType:
 | 
						|
				switch {
 | 
						|
				case vi.typ == validationTypeGroup:
 | 
						|
					if vi.mi == nil {
 | 
						|
						return out, ValidationUnknown
 | 
						|
					}
 | 
						|
					vi.mi.init()
 | 
						|
					states = append(states, validationState{
 | 
						|
						typ:      validationTypeGroup,
 | 
						|
						mi:       vi.mi,
 | 
						|
						endGroup: num,
 | 
						|
					})
 | 
						|
					continue State
 | 
						|
				case flags.ProtoLegacy && vi.typ == validationTypeMessageSetItem:
 | 
						|
					typeid, v, n, err := messageset.ConsumeFieldValue(b, false)
 | 
						|
					if err != nil {
 | 
						|
						return out, ValidationInvalid
 | 
						|
					}
 | 
						|
					xt, err := opts.resolver.FindExtensionByNumber(st.mi.Desc.FullName(), typeid)
 | 
						|
					switch {
 | 
						|
					case err == preg.NotFound:
 | 
						|
						b = b[n:]
 | 
						|
					case err != nil:
 | 
						|
						return out, ValidationUnknown
 | 
						|
					default:
 | 
						|
						xvi := getExtensionFieldInfo(xt).validation
 | 
						|
						if xvi.mi != nil {
 | 
						|
							xvi.mi.init()
 | 
						|
						}
 | 
						|
						states = append(states, validationState{
 | 
						|
							typ:  xvi.typ,
 | 
						|
							mi:   xvi.mi,
 | 
						|
							tail: b[n:],
 | 
						|
						})
 | 
						|
						b = v
 | 
						|
						continue State
 | 
						|
					}
 | 
						|
				default:
 | 
						|
					n := protowire.ConsumeFieldValue(num, wtyp, b)
 | 
						|
					if n < 0 {
 | 
						|
						return out, ValidationInvalid
 | 
						|
					}
 | 
						|
					b = b[n:]
 | 
						|
				}
 | 
						|
			default:
 | 
						|
				return out, ValidationInvalid
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if st.endGroup != 0 {
 | 
						|
			return out, ValidationInvalid
 | 
						|
		}
 | 
						|
		if len(b) != 0 {
 | 
						|
			return out, ValidationInvalid
 | 
						|
		}
 | 
						|
		b = st.tail
 | 
						|
	PopState:
 | 
						|
		numRequiredFields := 0
 | 
						|
		switch st.typ {
 | 
						|
		case validationTypeMessage, validationTypeGroup:
 | 
						|
			numRequiredFields = int(st.mi.numRequiredFields)
 | 
						|
		case validationTypeMap:
 | 
						|
			// If this is a map field with a message value that contains
 | 
						|
			// required fields, require that the value be present.
 | 
						|
			if st.mi != nil && st.mi.numRequiredFields > 0 {
 | 
						|
				numRequiredFields = 1
 | 
						|
			}
 | 
						|
		}
 | 
						|
		// If there are more than 64 required fields, this check will
 | 
						|
		// always fail and we will report that the message is potentially
 | 
						|
		// uninitialized.
 | 
						|
		if numRequiredFields > 0 && bits.OnesCount64(st.requiredMask) != numRequiredFields {
 | 
						|
			initialized = false
 | 
						|
		}
 | 
						|
		states = states[:len(states)-1]
 | 
						|
	}
 | 
						|
	out.n = start - len(b)
 | 
						|
	if initialized {
 | 
						|
		out.initialized = true
 | 
						|
	}
 | 
						|
	return out, ValidationValid
 | 
						|
}
 |