package runtime

import (
	
	
	
	

	
	
)

var (
	// ErrNotMatch indicates that the given HTTP request path does not match to the pattern.
	ErrNotMatch = errors.New("not match to the path pattern")
	// ErrInvalidPattern indicates that the given definition of Pattern is not valid.
	ErrInvalidPattern = errors.New("invalid pattern")
)

type MalformedSequenceError string

func ( MalformedSequenceError) () string {
	return "malformed path escape " + strconv.Quote(string())
}

type op struct {
	code    utilities.OpCode
	operand int
}

// Pattern is a template pattern of http request paths defined in
// https://github.com/googleapis/googleapis/blob/master/google/api/http.proto
type Pattern struct {
	// ops is a list of operations
	ops []op
	// pool is a constant pool indexed by the operands or vars.
	pool []string
	// vars is a list of variables names to be bound by this pattern
	vars []string
	// stacksize is the max depth of the stack
	stacksize int
	// tailLen is the length of the fixed-size segments after a deep wildcard
	tailLen int
	// verb is the VERB part of the path pattern. It is empty if the pattern does not have VERB part.
	verb string
}

// NewPattern returns a new Pattern from the given definition values.
// "ops" is a sequence of op codes. "pool" is a constant pool.
// "verb" is the verb part of the pattern. It is empty if the pattern does not have the part.
// "version" must be 1 for now.
// It returns an error if the given definition is invalid.
func ( int,  []int,  []string,  string) (Pattern, error) {
	if  != 1 {
		grpclog.Errorf("unsupported version: %d", )
		return Pattern{}, ErrInvalidPattern
	}

	 := len()
	if %2 != 0 {
		grpclog.Errorf("odd number of ops codes: %d", )
		return Pattern{}, ErrInvalidPattern
	}

	var (
		        []op
		,  int
		         int
		       bool
		            []string
	)
	for  := 0;  < ;  += 2 {
		 := op{code: utilities.OpCode([]), operand: [+1]}
		switch .code {
		case utilities.OpNop:
			continue
		case utilities.OpPush:
			if  {
				++
			}
			++
		case utilities.OpPushM:
			if  {
				grpclog.Error("pushM appears twice")
				return Pattern{}, ErrInvalidPattern
			}
			 = true
			++
		case utilities.OpLitPush:
			if .operand < 0 || len() <= .operand {
				grpclog.Errorf("negative literal index: %d", .operand)
				return Pattern{}, ErrInvalidPattern
			}
			if  {
				++
			}
			++
		case utilities.OpConcatN:
			if .operand <= 0 {
				grpclog.Errorf("negative concat size: %d", .operand)
				return Pattern{}, ErrInvalidPattern
			}
			 -= .operand
			if  < 0 {
				grpclog.Error("stack underflow")
				return Pattern{}, ErrInvalidPattern
			}
			++
		case utilities.OpCapture:
			if .operand < 0 || len() <= .operand {
				grpclog.Errorf("variable name index out of bound: %d", .operand)
				return Pattern{}, ErrInvalidPattern
			}
			 := [.operand]
			.operand = len()
			 = append(, )
			--
			if  < 0 {
				grpclog.Error("stack underflow")
				return Pattern{}, ErrInvalidPattern
			}
		default:
			grpclog.Errorf("invalid opcode: %d", .code)
			return Pattern{}, ErrInvalidPattern
		}

		if  <  {
			 = 
		}
		 = append(, )
	}
	return Pattern{
		ops:       ,
		pool:      ,
		vars:      ,
		stacksize: ,
		tailLen:   ,
		verb:      ,
	}, nil
}

// MustPattern is a helper function which makes it easier to call NewPattern in variable initialization.
func ( Pattern,  error) Pattern {
	if  != nil {
		grpclog.Fatalf("Pattern initialization failed: %v", )
	}
	return 
}

// MatchAndEscape examines components to determine if they match to a Pattern.
// MatchAndEscape will return an error if no Patterns matched or if a pattern
// matched but contained malformed escape sequences. If successful, the function
// returns a mapping from field paths to their captured values.
func ( Pattern) ( []string,  string,  UnescapingMode) (map[string]string, error) {
	if .verb !=  {
		if .verb != "" {
			return nil, ErrNotMatch
		}
		if len() == 0 {
			 = []string{":" + }
		} else {
			 = append([]string{}, ...)
			[len()-1] += ":" + 
		}
	}

	var  int
	 := make([]string, 0, .stacksize)
	 := make([]string, len(.vars))
	 := len()
	for ,  := range .ops {
		var  error

		switch .code {
		case utilities.OpNop:
			continue
		case utilities.OpPush, utilities.OpLitPush:
			if  >=  {
				return nil, ErrNotMatch
			}
			 := []
			if .code == utilities.OpLitPush {
				if  := .pool[.operand];  !=  {
					return nil, ErrNotMatch
				}
			} else if .code == utilities.OpPush {
				if ,  = unescape(, , false);  != nil {
					return nil, 
				}
			}
			 = append(, )
			++
		case utilities.OpPushM:
			 := len()
			if  < +.tailLen {
				return nil, ErrNotMatch
			}
			 -= .tailLen
			 := strings.Join([:], "/")
			if ,  = unescape(, , true);  != nil {
				return nil, 
			}
			 = append(, )
			 = 
		case utilities.OpConcatN:
			 := .operand
			 := len() - 
			 = append([:], strings.Join([:], "/"))
		case utilities.OpCapture:
			 := len() - 1
			[.operand] = []
			 = [:]
		}
	}
	if  <  {
		return nil, ErrNotMatch
	}
	 := make(map[string]string)
	for ,  := range  {
		[.vars[]] = 
	}
	return , nil
}

// MatchAndEscape examines components to determine if they match to a Pattern.
// It will never perform per-component unescaping (see: UnescapingModeLegacy).
// MatchAndEscape will return an error if no Patterns matched. If successful,
// the function returns a mapping from field paths to their captured values.
//
// Deprecated: Use MatchAndEscape.
func ( Pattern) ( []string,  string) (map[string]string, error) {
	return .MatchAndEscape(, , UnescapingModeDefault)
}

// Verb returns the verb part of the Pattern.
func ( Pattern) () string { return .verb }

func ( Pattern) () string {
	var  []string
	for ,  := range .ops {
		switch .code {
		case utilities.OpNop:
			continue
		case utilities.OpPush:
			 = append(, "*")
		case utilities.OpLitPush:
			 = append(, .pool[.operand])
		case utilities.OpPushM:
			 = append(, "**")
		case utilities.OpConcatN:
			 := .operand
			 := len() - 
			 = append([:], strings.Join([:], "/"))
		case utilities.OpCapture:
			 := len() - 1
			[] = fmt.Sprintf("{%s=%s}", .vars[.operand], [])
		}
	}
	 := strings.Join(, "/")
	if .verb != "" {
		return fmt.Sprintf("/%s:%s", , .verb)
	}
	return "/" + 
}

/*
 * The following code is adopted and modified from Go's standard library
 * and carries the attached license.
 *
 *     Copyright 2009 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.
 */

// ishex returns whether or not the given byte is a valid hex character
func ishex( byte) bool {
	switch {
	case '0' <=  &&  <= '9':
		return true
	case 'a' <=  &&  <= 'f':
		return true
	case 'A' <=  &&  <= 'F':
		return true
	}
	return false
}

func isRFC6570Reserved( byte) bool {
	switch  {
	case '!', '#', '$', '&', '\'', '(', ')', '*',
		'+', ',', '/', ':', ';', '=', '?', '@', '[', ']':
		return true
	default:
		return false
	}
}

// unhex converts a hex point to the bit representation
func unhex( byte) byte {
	switch {
	case '0' <=  &&  <= '9':
		return  - '0'
	case 'a' <=  &&  <= 'f':
		return  - 'a' + 10
	case 'A' <=  &&  <= 'F':
		return  - 'A' + 10
	}
	return 0
}

// shouldUnescapeWithMode returns true if the character is escapable with the
// given mode
func shouldUnescapeWithMode( byte,  UnescapingMode) bool {
	switch  {
	case UnescapingModeAllExceptReserved:
		if isRFC6570Reserved() {
			return false
		}
	case UnescapingModeAllExceptSlash:
		if  == '/' {
			return false
		}
	case UnescapingModeAllCharacters:
		return true
	}
	return true
}

// unescape unescapes a path string using the provided mode
func unescape( string,  UnescapingMode,  bool) (string, error) {
	// TODO(v3): remove UnescapingModeLegacy
	if  == UnescapingModeLegacy {
		return , nil
	}

	if ! {
		 = UnescapingModeAllCharacters
	}

	// Count %, check that they're well-formed.
	 := 0
	for  := 0;  < len(); {
		if [] == '%' {
			++
			if +2 >= len() || !ishex([+1]) || !ishex([+2]) {
				 = [:]
				if len() > 3 {
					 = [:3]
				}

				return "", MalformedSequenceError()
			}
			 += 3
		} else {
			++
		}
	}

	if  == 0 {
		return , nil
	}

	var  strings.Builder
	.Grow(len())
	for  := 0;  < len(); ++ {
		switch [] {
		case '%':
			 := unhex([+1])<<4 | unhex([+2])
			if shouldUnescapeWithMode(, ) {
				.WriteByte()
				 += 2
				continue
			}
			fallthrough
		default:
			.WriteByte([])
		}
	}

	return .String(), nil
}