func()

in runtime/router/trie.go [129:247]


func (t *tnode) set(path string, value http.Handler, lastKeyCharSlash, lastPathCharSlash, colonAsPattern, isWhitelisted bool) error {
	// find the longest common prefix
	var shorterLength, i int
	keyLength, pathLength := len(t.key), len(path)
	if keyLength > pathLength {
		shorterLength = pathLength
	} else {
		shorterLength = keyLength
	}
	for i < shorterLength && t.key[i] == path[i] {
		i++
	}

	// Find the first character that differs between "path" and this node's key, if it exists.
	// If we encounter a colon wildcard, ensure that the wildcard in path matches the wildcard
	// in this node's key for that segment. The segment is a colon wildcard only when the colon
	// is immediately after slash, e.g. "/:foo", "/x/:y". "/a:b" is not a colon wildcard segment.
	var keyMatchIdx, pathMatchIdx int
	for keyMatchIdx < keyLength && pathMatchIdx < pathLength {
		if t.isSetWildCardPattern(path, keyMatchIdx, pathMatchIdx, lastKeyCharSlash, lastPathCharSlash, isWhitelisted) {
			keyStartIdx, pathStartIdx := keyMatchIdx, pathMatchIdx
			same := t.key[keyMatchIdx] == path[pathMatchIdx]
			for keyMatchIdx < keyLength && t.key[keyMatchIdx] != '/' {
				keyMatchIdx++
			}
			for pathMatchIdx < pathLength && path[pathMatchIdx] != '/' {
				pathMatchIdx++
			}
			if same && (keyMatchIdx-keyStartIdx) != (pathMatchIdx-pathStartIdx) {
				return &paramMismatch{
					t.key[keyStartIdx:keyMatchIdx],
					path[pathStartIdx:pathMatchIdx],
					t.key,
				}
			}
		} else if t.key[keyMatchIdx] == path[pathMatchIdx] {
			keyMatchIdx++
			pathMatchIdx++
		} else {
			break
		}
		lastKeyCharSlash = t.key[keyMatchIdx-1] == '/'
		lastPathCharSlash = path[pathMatchIdx-1] == '/'
	}

	// If the node key is fully matched, we match the rest path with children nodes to see if a value
	// already exists for the path.
	if keyMatchIdx == keyLength {
		for _, c := range t.children {
			if _, _, err := c.get(path[pathMatchIdx:], lastKeyCharSlash, lastPathCharSlash, colonAsPattern, isWhitelisted); err == nil {
				return errExist
			}
		}
	}

	// node key is longer than longest common prefix
	if i < keyLength {
		// key/path suffix being "*" means a conflict
		if path[i:] == "*" || t.key[i:] == "*" {
			return errExist
		}

		// split the node key, add new node with node key minus longest common prefix
		split := &tnode{
			key:      t.key[i:],
			value:    t.value,
			children: t.children,
		}
		t.key = t.key[:i]
		t.value = nil
		t.children = []*tnode{split}

		// path is equal to longest common prefix
		// set value on current node after split
		if i == pathLength {
			t.value = value
		} else {
			// path is longer than longest common prefix
			// add new node with path minus longest common prefix
			newNode := &tnode{
				key:   path[i:],
				value: value,
			}
			t.addChildren(newNode, lastPathCharSlash)
		}
	}

	// node key is equal to longest common prefix
	if i == keyLength {
		// path is equal to longest common prefix
		if i == pathLength {
			// node is guaranteed to have zero value,
			// otherwise it would have caused errExist earlier
			t.value = value
		} else {
			// path is longer than node key, try to recurse on node children
			for _, c := range t.children {
				if c.key[0] == path[i] {
					lastKeyCharSlash = i > 0 && t.key[i-1] == '/'
					lastPathCharSlash = i > 0 && path[i-1] == '/'
					err := c.set(path[i:], value, lastKeyCharSlash, lastPathCharSlash, colonAsPattern, isWhitelisted)
					if e, ok := err.(*paramMismatch); ok {
						e.existingPath = t.key + e.existingPath
						return e
					}
					return err
				}
			}
			// no children to recurse, add node with path minus longest common path
			newNode := &tnode{
				key:   path[i:],
				value: value,
			}
			t.addChildren(newNode, lastPathCharSlash)
		}
	}

	return nil
}