github.com/gramework/gramework@v1.8.1-0.20231027140105-82555c9057f5/fasthttprouter_tree.go (about)

     1  // Copyright 2013 Julien Schmidt. All rights reserved.
     2  // Copyright (c) 2015-2016, 招牌疯子
     3  // Copyright (c) 2017, Kirill Danshin
     4  // Use of this source code is governed by a BSD-style license that can be found
     5  // in the 3rd-Party License/fasthttprouter file.
     6  
     7  package gramework
     8  
     9  import (
    10  	"strings"
    11  	"unicode"
    12  	"unicode/utf8"
    13  )
    14  
    15  type (
    16  	nodeType uint8
    17  
    18  	node struct {
    19  		path      string
    20  		wildChild bool
    21  		nType     nodeType
    22  		maxParams uint8
    23  		indices   string
    24  		children  []*node
    25  		handle    RequestHandler
    26  		priority  uint32
    27  		hits      uint32
    28  		router    *router
    29  		prefixes  []string
    30  	}
    31  )
    32  
    33  // Slash constant used to minimize string allocations
    34  const Slash = "/"
    35  
    36  const (
    37  	static nodeType = iota // default
    38  	root
    39  	param
    40  	catchAll
    41  )
    42  
    43  func min(a, b int) int {
    44  	if a <= b {
    45  		return a
    46  	}
    47  	return b
    48  }
    49  
    50  func countParams(path string) uint8 {
    51  	var n uint
    52  	for i := zero; i < len(path); i++ {
    53  		if path[i] != ':' && path[i] != '*' {
    54  			continue
    55  		}
    56  		n++
    57  	}
    58  	if n >= 255 {
    59  		return 255
    60  	}
    61  	return uint8(n)
    62  }
    63  
    64  // increments priority of the given child and reorders if necessary
    65  func (n *node) incrementChildPrio(pos int) int {
    66  	n.children[pos].priority++
    67  	prio := n.children[pos].priority
    68  
    69  	// adjust position (move to front)
    70  	newPos := pos
    71  	for newPos > zero && n.children[newPos-one].priority < prio {
    72  		// swap node positions
    73  		tmpN := n.children[newPos-one]
    74  		n.children[newPos-one] = n.children[newPos]
    75  		n.children[newPos] = tmpN
    76  
    77  		newPos--
    78  	}
    79  
    80  	// build new index char string
    81  	if newPos != pos {
    82  		n.indices = n.indices[:newPos] + // unchanged prefix, might be empty
    83  			n.indices[pos:pos+one] + // the index char we move
    84  			n.indices[newPos:pos] + n.indices[pos+one:] // rest without char at 'pos'
    85  	}
    86  
    87  	return newPos
    88  }
    89  
    90  // addRoute adds a node with the given handle to the path.
    91  // Not concurrency-safe!
    92  func (n *node) addRoute(path string, handle RequestHandler, r *router, prefixes []string) {
    93  	fullPath := path
    94  	n.priority++
    95  	numParams := countParams(path)
    96  	if n.router == nil {
    97  		n.router = r
    98  	}
    99  
   100  	// non-empty tree
   101  	if len(n.path) > zero || len(n.children) > zero {
   102  	walk:
   103  		for {
   104  			// Update maxParams of the current node
   105  			if numParams > n.maxParams {
   106  				n.maxParams = numParams
   107  			}
   108  
   109  			// Find the longest common prefix.
   110  			// This also implies that the common prefix contains no ':' or '*'
   111  			// since the existing key can't contain those chars.
   112  			i := zero
   113  			max := min(len(path), len(n.path))
   114  			for i < max && path[i] == n.path[i] {
   115  				i++
   116  			}
   117  
   118  			// Split edge
   119  			if i < len(n.path) {
   120  				child := node{
   121  					path:      n.path[i:],
   122  					wildChild: n.wildChild,
   123  					nType:     static,
   124  					indices:   n.indices,
   125  					children:  n.children,
   126  					handle:    n.handle,
   127  					priority:  n.priority - one,
   128  					router:    n.router,
   129  					prefixes:  prefixes,
   130  				}
   131  
   132  				// Update maxParams (max of all children)
   133  				for i := range child.children {
   134  					if child.children[i].maxParams > child.maxParams {
   135  						child.maxParams = child.children[i].maxParams
   136  					}
   137  				}
   138  
   139  				n.children = []*node{&child}
   140  				// []byte for proper unicode char conversion, see #65
   141  				n.indices = string([]byte{n.path[i]})
   142  				n.path = path[:i]
   143  				n.handle = nil
   144  				n.wildChild = false
   145  			}
   146  
   147  			// Make new node a child of this node
   148  			if i < len(path) {
   149  				path = path[i:]
   150  
   151  				if n.wildChild {
   152  					n = n.children[zero]
   153  					n.priority++
   154  
   155  					// Update maxParams of the child node
   156  					if numParams > n.maxParams {
   157  						n.maxParams = numParams
   158  					}
   159  					numParams--
   160  
   161  					// Check if the wildcard matches
   162  					if len(path) >= len(n.path) && n.path == path[:len(n.path)] &&
   163  						// Check for longer wildcard, e.g. :name and :names
   164  						(len(n.path) >= len(path) || path[len(n.path)] == SlashByte) {
   165  						continue walk
   166  					} else {
   167  						// Wildcard conflict
   168  						pathSeg := strings.SplitN(path, PathSlash, 2)[zero]
   169  						prefix := fullPath[:strings.Index(fullPath, pathSeg)] + n.path
   170  						panic("'" + pathSeg +
   171  							"' in new path '" + fullPath +
   172  							"' conflicts with existing wildcard '" + n.path +
   173  							"' in existing prefix '" + prefix +
   174  							"'")
   175  					}
   176  				}
   177  
   178  				c := path[zero]
   179  
   180  				// slash after param
   181  				if n.nType == param && c == SlashByte && len(n.children) == one {
   182  					n = n.children[zero]
   183  					n.priority++
   184  					continue walk
   185  				}
   186  
   187  				// Check if a child with the next path byte exists
   188  				for i := zero; i < len(n.indices); i++ {
   189  					if c == n.indices[i] {
   190  						i = n.incrementChildPrio(i)
   191  						n = n.children[i]
   192  						continue walk
   193  					}
   194  				}
   195  
   196  				// Otherwise insert it
   197  				if c != ':' && c != '*' {
   198  					// []byte for proper unicode char conversion, see #65
   199  					n.indices += string([]byte{c})
   200  					child := &node{
   201  						maxParams: numParams,
   202  						router:    n.router,
   203  					}
   204  					n.children = append(n.children, child)
   205  					n.incrementChildPrio(len(n.indices) - one)
   206  					n = child
   207  				}
   208  				n.insertChild(numParams, path, fullPath, handle, prefixes)
   209  				return
   210  
   211  			} else if i == len(path) { // Make node a (in-path) leaf
   212  				if n.handle != nil {
   213  					panic("a handle is already registered for path '" + fullPath + "'")
   214  				}
   215  				n.handle = handle
   216  			}
   217  			return
   218  		}
   219  	} else { // Empty tree
   220  		n.insertChild(numParams, path, fullPath, handle, prefixes)
   221  		n.nType = root
   222  	}
   223  }
   224  
   225  func (n *node) insertChild(numParams uint8, path, fullPath string, handle RequestHandler, prefixes []string) {
   226  	var offset int // already handled bytes of the path
   227  
   228  	// find prefix until first wildcard (beginning with ':' or '*')
   229  	for i, max := zero, len(path); numParams > zero; i++ {
   230  		c := path[i]
   231  		if c != ':' && c != '*' {
   232  			continue
   233  		}
   234  
   235  		// find wildcard end (either '/' or path end)
   236  		end := i + one
   237  		for end < max && path[end] != SlashByte {
   238  			switch path[end] {
   239  			// the wildcard name must not contain ':' and '*'
   240  			case ':', '*':
   241  				panic("only one wildcard per path segment is allowed, has: '" +
   242  					path[i:] + "' in path '" + fullPath + "'")
   243  			default:
   244  				end++
   245  			}
   246  		}
   247  
   248  		// check if this Node existing children which would be
   249  		// unreachable if we insert the wildcard here
   250  		if len(n.children) > zero {
   251  			panic("wildcard route '" + path[i:end] +
   252  				"' conflicts with existing children in path '" + fullPath + "'")
   253  		}
   254  
   255  		// check if the wildcard has a name
   256  		if end-i < 2 {
   257  			panic("wildcards must be named with a non-empty name in path '" + fullPath + "'")
   258  		}
   259  
   260  		if c == ':' { // param
   261  			// split path at the beginning of the wildcard
   262  			if i > zero {
   263  				n.path = path[offset:i]
   264  				offset = i
   265  			}
   266  
   267  			child := &node{
   268  				nType:     param,
   269  				maxParams: numParams,
   270  				router:    n.router,
   271  				prefixes:  prefixes,
   272  			}
   273  			n.children = []*node{child}
   274  			n.wildChild = true
   275  			n = child
   276  			n.priority++
   277  			numParams--
   278  
   279  			// if the path doesn't end with the wildcard, then there
   280  			// will be another non-wildcard subpath starting with '/'
   281  			if end < max {
   282  				n.path = path[offset:end]
   283  				offset = end
   284  
   285  				child := &node{
   286  					maxParams: numParams,
   287  					priority:  one,
   288  					router:    n.router,
   289  					prefixes:  prefixes,
   290  				}
   291  				n.children = []*node{child}
   292  				n = child
   293  			}
   294  
   295  		} else { // catchAll
   296  			if end != max || numParams > one {
   297  				panic("catch-all routes are only allowed at the end of the path in path '" + fullPath + "'")
   298  			}
   299  
   300  			if len(n.path) > zero && n.path[len(n.path)-one] == SlashByte {
   301  				panic("catch-all conflicts with existing handle for the path segment root in path '" + fullPath + "'")
   302  			}
   303  
   304  			// currently fixed width one for '/'
   305  			i--
   306  			if path[i] != SlashByte {
   307  				panic("no / before catch-all in path '" + fullPath + "'")
   308  			}
   309  
   310  			n.path = path[offset:i]
   311  
   312  			// first node: catchAll node with empty path
   313  			child := &node{
   314  				wildChild: true,
   315  				nType:     catchAll,
   316  				maxParams: one,
   317  				router:    n.router,
   318  				prefixes:  prefixes,
   319  			}
   320  			n.children = []*node{child}
   321  			n.indices = string(path[i])
   322  			n = child
   323  			n.priority++
   324  
   325  			// second node: node holding the variable
   326  			child = &node{
   327  				path:      path[i:],
   328  				nType:     catchAll,
   329  				maxParams: one,
   330  				handle:    handle,
   331  				priority:  one,
   332  				router:    n.router,
   333  				prefixes:  prefixes,
   334  			}
   335  			n.children = []*node{child}
   336  
   337  			return
   338  		}
   339  	}
   340  
   341  	// insert remaining path part and handle to the leaf
   342  	n.path = path[offset:]
   343  	n.handle = handle
   344  }
   345  
   346  // GetValue returns the handle registered with the given path (key). The values
   347  // of wildcards are saved to a map.
   348  // If no handle can be found, a TSR (trailing slash redirect) recommendation is
   349  // made if a handle exists with an extra (without the) trailing slash for the
   350  // given path.
   351  func (n *node) GetValue(reqPath string, ctx *Context, method string) (handle RequestHandler, prefixes []string, tsr bool) { //nolint: golint
   352  	if n.router == nil {
   353  		panic("no router!")
   354  	}
   355  	if n.router.cache == nil {
   356  		panic("no cache!")
   357  	}
   358  	if ctx != nil {
   359  		if record, ok := n.router.cache.Get(reqPath, method); ok {
   360  			if record != nil {
   361  				for name, value := range record.values {
   362  					ctx.SetUserValue(name, value)
   363  				}
   364  				if ctx != nil {
   365  					ctx.subPrefixes = record.n.prefixes
   366  				}
   367  				return record.n.handle, record.n.prefixes, record.tsr
   368  			}
   369  		}
   370  		ctx.subPrefixes = n.prefixes
   371  	}
   372  	params := []struct{ k, v string }{}
   373  	path := reqPath
   374  walk: // outer loop for walking the tree
   375  	for {
   376  		if len(path) > len(n.path) {
   377  			if path[:len(n.path)] == n.path {
   378  				path = path[len(n.path):]
   379  				// If this node does not have a wildcard (param or catchAll)
   380  				// child,  we can just look up the next child node and continue
   381  				// to walk down the tree
   382  				if !n.wildChild {
   383  					c := path[zero]
   384  					for i := zero; i < len(n.indices); i++ {
   385  						if c == n.indices[i] {
   386  							n = n.children[i]
   387  							continue walk
   388  						}
   389  					}
   390  
   391  					// Nothing found.
   392  					// We can recommend to redirect to the same URL without a
   393  					// trailing slash if a leaf exists for that path.
   394  					tsr = (path == PathSlash && n.handle != nil)
   395  					return
   396  				}
   397  
   398  				// handle wildcard child
   399  				n = n.children[zero]
   400  				switch n.nType {
   401  				case param:
   402  					// find param end (either '/' or path end)
   403  					end := zero
   404  					for end < len(path) && path[end] != SlashByte {
   405  						end++
   406  					}
   407  
   408  					// handle calls to Router.allowed method with nil context
   409  					if ctx != nil {
   410  						params = append(params, struct{ k, v string }{
   411  							k: n.path[one:],
   412  							v: path[:end],
   413  						})
   414  						ctx.SetUserValue(n.path[one:], path[:end])
   415  					}
   416  
   417  					// we need to go deeper!
   418  					if end < len(path) {
   419  						if len(n.children) > zero {
   420  							path = path[end:]
   421  							n = n.children[zero]
   422  							continue walk
   423  						}
   424  
   425  						// ... but we can't
   426  						tsr = (len(path) == end+one)
   427  						return
   428  					}
   429  
   430  					if handle = n.handle; handle != nil {
   431  						n.hits++
   432  						if n.hits > 32 {
   433  							n.router.cache.PutWild(reqPath, n, tsr, map[string]string{n.path[one:]: path[:end]}, method)
   434  						}
   435  						prefixes = n.prefixes
   436  						return
   437  					} else if len(n.children) == one {
   438  						// No handle found. Check if a handle for this path + a
   439  						// trailing slash exists for TSR recommendation
   440  						n = n.children[zero]
   441  						tsr = (n.path == PathSlash && n.handle != nil)
   442  					}
   443  
   444  					return
   445  
   446  				case catchAll:
   447  					if ctx != nil {
   448  						// save param value
   449  						params = append(params, struct{ k, v string }{
   450  							k: n.path[2:],
   451  							v: path,
   452  						})
   453  						ctx.SetUserValue(n.path[2:], path)
   454  						_ = params // solve false positive
   455  					}
   456  					handle = n.handle
   457  					n.hits++
   458  					if n.hits > 32 {
   459  						n.router.cache.PutWild(reqPath, n, tsr, map[string]string{n.path[2:]: path}, method)
   460  					}
   461  					return
   462  
   463  				default:
   464  					panic("invalid node type")
   465  				}
   466  			}
   467  		} else if path == n.path {
   468  			// We should have reached the node containing the handle.
   469  			// Check if this node has a handle registered.
   470  			if handle = n.handle; handle != nil {
   471  				if len(params) != 0 {
   472  					values := map[string]string{}
   473  					for _, v := range params {
   474  						values[v.k] = v.v
   475  					}
   476  					n.router.cache.PutWild(reqPath, n, tsr, values, method)
   477  					return
   478  				}
   479  				n.router.cache.Put(reqPath, n, tsr, method)
   480  				tsr = (len(n.path) == one && n.handle != nil) ||
   481  					(n.nType == catchAll && n.children[zero].handle != nil) ||
   482  					(path == PathSlash && n.wildChild && n.nType != root)
   483  				return
   484  			}
   485  
   486  			if path == PathSlash && n.wildChild && n.nType != root {
   487  				tsr = true
   488  				return
   489  			}
   490  
   491  			// No handle found. Check if a handle for this path + a
   492  			// trailing slash exists for trailing slash recommendation
   493  			for i := zero; i < len(n.indices); i++ {
   494  				if n.indices[i] == SlashByte {
   495  					n = n.children[i]
   496  					tsr = (len(n.path) == one && n.handle != nil) ||
   497  						(n.nType == catchAll && n.children[zero].handle != nil)
   498  					return
   499  				}
   500  			}
   501  
   502  			return
   503  		}
   504  
   505  		// Nothing found. We can recommend to redirect to the same URL with an
   506  		// extra trailing slash if a leaf exists for that path
   507  		tsr = (path == PathSlash) ||
   508  			(len(n.path) == len(path)+one && n.path[len(path)] == SlashByte &&
   509  				path == n.path[:len(n.path)-one] && n.handle != nil)
   510  		return
   511  	}
   512  }
   513  
   514  // FindCaseInsensitivePath makes a case-insensitive lookup of the given path
   515  // and tries to find a handler.
   516  // It can optionally also fix trailing slashes.
   517  // It returns the case-corrected path and a bool indicating whether the lookup
   518  // was successful.
   519  func (n *node) FindCaseInsensitivePath(path string, fixTrailingSlash bool) ([]byte, bool) {
   520  	return n.findCaseInsensitivePathRec(
   521  		path,
   522  		toLower(path),
   523  		make([]byte, zero, len(path)+one), // preallocate enough memory for new path
   524  		[4]byte{},                         // empty rune buffer
   525  		fixTrailingSlash,
   526  	)
   527  }
   528  
   529  // shift bytes in array by n bytes left
   530  func shiftNRuneBytes(rb [4]byte, n int) [4]byte {
   531  	switch n {
   532  	case zero:
   533  		return rb
   534  	case one:
   535  		return [4]byte{rb[one], rb[2], rb[3], zero}
   536  	case 2:
   537  		return [4]byte{rb[2], rb[3]}
   538  	case 3:
   539  		return [4]byte{rb[3]}
   540  	default:
   541  		return [4]byte{}
   542  	}
   543  }
   544  
   545  // recursive case-insensitive lookup function used by n.findCaseInsensitivePath
   546  func (n *node) findCaseInsensitivePathRec(path, loPath string, ciPath []byte, rb [4]byte, fixTrailingSlash bool) ([]byte, bool) {
   547  	loNPath := toLower(n.path)
   548  
   549  walk: // outer loop for walking the tree
   550  	for len(loPath) >= len(loNPath) && (len(loNPath) == zero || loPath[one:len(loNPath)] == loNPath[one:]) {
   551  		// add common path to result
   552  		ciPath = append(ciPath, n.path...)
   553  
   554  		if path = path[len(n.path):]; len(path) > zero {
   555  			loOld := loPath
   556  			loPath = loPath[len(loNPath):]
   557  
   558  			// If this node does not have a wildcard (param or catchAll) child,
   559  			// we can just look up the next child node and continue to walk down
   560  			// the tree
   561  			if !n.wildChild {
   562  				// skip rune bytes already processed
   563  				rb = shiftNRuneBytes(rb, len(loNPath))
   564  
   565  				if rb[zero] != zero {
   566  					// old rune not finished
   567  					for i := zero; i < len(n.indices); i++ {
   568  						if n.indices[i] == rb[zero] {
   569  							// continue with child node
   570  							n = n.children[i]
   571  							loNPath = toLower(n.path)
   572  							continue walk
   573  						}
   574  					}
   575  				} else {
   576  					// process a new rune
   577  					var rv rune
   578  
   579  					// find rune start
   580  					// runes are up to 4 byte long,
   581  					// -4 would definitely be another rune
   582  					var off int
   583  					for max := min(len(loNPath), 3); off < max; off++ {
   584  						if i := len(loNPath) - off; utf8.RuneStart(loOld[i]) {
   585  							// read rune from cached lowercase path
   586  							rv, _ = utf8.DecodeRuneInString(loOld[i:])
   587  							break
   588  						}
   589  					}
   590  
   591  					// calculate lowercase bytes of current rune
   592  					utf8.EncodeRune(rb[:], rv)
   593  					// skipp already processed bytes
   594  					rb = shiftNRuneBytes(rb, off)
   595  
   596  					for i := zero; i < len(n.indices); i++ {
   597  						// lowercase matches
   598  						if n.indices[i] == rb[zero] {
   599  							// must use a recursive approach since both the
   600  							// uppercase byte and the lowercase byte might exist
   601  							// as an index
   602  							if out, found := n.children[i].findCaseInsensitivePathRec(
   603  								path, loPath, ciPath, rb, fixTrailingSlash,
   604  							); found {
   605  								return out, true
   606  							}
   607  							break
   608  						}
   609  					}
   610  
   611  					// same for uppercase rune, if it differs
   612  					if up := unicode.ToUpper(rv); up != rv {
   613  						utf8.EncodeRune(rb[:], up)
   614  						rb = shiftNRuneBytes(rb, off)
   615  
   616  						for i := zero; i < len(n.indices); i++ {
   617  							// uppercase matches
   618  							if n.indices[i] == rb[zero] {
   619  								// continue with child node
   620  								n = n.children[i]
   621  								loNPath = toLower(n.path)
   622  								continue walk
   623  							}
   624  						}
   625  					}
   626  				}
   627  
   628  				// Nothing found. We can recommend to redirect to the same URL
   629  				// without a trailing slash if a leaf exists for that path
   630  				return ciPath, (fixTrailingSlash && path == PathSlash && n.handle != nil)
   631  			}
   632  
   633  			n = n.children[zero]
   634  			switch n.nType {
   635  			case param:
   636  				// find param end (either '/' or path end)
   637  				k := zero
   638  				for k < len(path) && path[k] != SlashByte {
   639  					k++
   640  				}
   641  
   642  				// add param value to case insensitive path
   643  				ciPath = append(ciPath, path[:k]...)
   644  
   645  				// we need to go deeper!
   646  				if k < len(path) {
   647  					if len(n.children) > zero {
   648  						// continue with child node
   649  						n = n.children[zero]
   650  						loNPath = toLower(n.path)
   651  						loPath = loPath[k:]
   652  						path = path[k:]
   653  						continue
   654  					}
   655  
   656  					// ... but we can't
   657  					if fixTrailingSlash && len(path) == k+one {
   658  						return ciPath, true
   659  					}
   660  					return ciPath, false
   661  				}
   662  
   663  				if n.handle != nil {
   664  					return ciPath, true
   665  				} else if fixTrailingSlash && len(n.children) == one {
   666  					// No handle found. Check if a handle for this path + a
   667  					// trailing slash exists
   668  					n = n.children[zero]
   669  					if n.path == PathSlash && n.handle != nil {
   670  						return append(ciPath, SlashByte), true
   671  					}
   672  				}
   673  				return ciPath, false
   674  
   675  			case catchAll:
   676  				return append(ciPath, path...), true
   677  
   678  			default:
   679  				panic("invalid node type")
   680  			}
   681  		} else {
   682  			// We should have reached the node containing the handle.
   683  			// Check if this node has a handle registered.
   684  			if n.handle != nil {
   685  				return ciPath, true
   686  			}
   687  
   688  			// No handle found.
   689  			// Try to fix the path by adding a trailing slash
   690  			if fixTrailingSlash {
   691  				for i := zero; i < len(n.indices); i++ {
   692  					if n.indices[i] == SlashByte {
   693  						n = n.children[i]
   694  						if (len(n.path) == one && n.handle != nil) ||
   695  							(n.nType == catchAll && n.children[zero].handle != nil) {
   696  							return append(ciPath, SlashByte), true
   697  						}
   698  						return ciPath, false
   699  					}
   700  				}
   701  			}
   702  			return ciPath, false
   703  		}
   704  	}
   705  
   706  	// Nothing found.
   707  	// Try to fix the path by adding / removing a trailing slash
   708  	if fixTrailingSlash {
   709  		if path == PathSlash {
   710  			return ciPath, true
   711  		}
   712  		if len(loPath)+one == len(loNPath) && loNPath[len(loPath)] == SlashByte &&
   713  			loPath[one:] == loNPath[one:len(loPath)] && n.handle != nil {
   714  			return append(ciPath, n.path...), true
   715  		}
   716  	}
   717  	return ciPath, false
   718  }