mirror of
https://github.com/jimeh/casecmp.git
synced 2026-02-19 10:26:40 +00:00
initial commit
This commit is contained in:
317
vendor/github.com/qiangxue/fasthttp-routing/store.go
generated
vendored
Normal file
317
vendor/github.com/qiangxue/fasthttp-routing/store.go
generated
vendored
Normal file
@@ -0,0 +1,317 @@
|
||||
// Copyright 2016 Qiang Xue. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package routing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// store is a radix tree that supports storing data with parametric keys and retrieving them back with concrete keys.
|
||||
// When retrieving a data item with a concrete key, the matching parameter names and values will be returned as well.
|
||||
// A parametric key is a string containing tokens in the format of "<name>", "<name:pattern>", or "<:pattern>".
|
||||
// Each token represents a single parameter.
|
||||
type store struct {
|
||||
root *node // the root node of the radix tree
|
||||
count int // the number of data nodes in the tree
|
||||
}
|
||||
|
||||
// newStore creates a new store.
|
||||
func newStore() *store {
|
||||
return &store{
|
||||
root: &node{
|
||||
static: true,
|
||||
children: make([]*node, 256),
|
||||
pchildren: make([]*node, 0),
|
||||
pindex: -1,
|
||||
pnames: []string{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Add adds a new data item with the given parametric key.
|
||||
// The number of parameters in the key is returned.
|
||||
func (s *store) Add(key string, data interface{}) int {
|
||||
s.count++
|
||||
return s.root.add(key, data, s.count)
|
||||
}
|
||||
|
||||
// Get returns the data item matching the given concrete key.
|
||||
// If the data item was added to the store with a parametric key before, the matching
|
||||
// parameter names and values will be returned as well.
|
||||
func (s *store) Get(path string, pvalues []string) (data interface{}, pnames []string) {
|
||||
data, pnames, _ = s.root.get(path, pvalues)
|
||||
return
|
||||
}
|
||||
|
||||
// String dumps the radix tree kept in the store as a string.
|
||||
func (s *store) String() string {
|
||||
return s.root.print(0)
|
||||
}
|
||||
|
||||
// node represents a radix trie node
|
||||
type node struct {
|
||||
static bool // whether the node is a static node or param node
|
||||
|
||||
key string // the key identifying this node
|
||||
data interface{} // the data associated with this node. nil if not a data node.
|
||||
|
||||
order int // the order at which the data was added. used to be pick the first one when matching multiple
|
||||
minOrder int // minimum order among all the child nodes and this node
|
||||
|
||||
children []*node // child static nodes, indexed by the first byte of each child key
|
||||
pchildren []*node // child param nodes
|
||||
|
||||
regex *regexp.Regexp // regular expression for a param node containing regular expression key
|
||||
pindex int // the parameter index, meaningful only for param node
|
||||
pnames []string // the parameter names collected from the root till this node
|
||||
}
|
||||
|
||||
// add adds a new data item to the tree rooted at the current node.
|
||||
// The number of parameters in the key is returned.
|
||||
func (n *node) add(key string, data interface{}, order int) int {
|
||||
matched := 0
|
||||
|
||||
// find the common prefix
|
||||
for ; matched < len(key) && matched < len(n.key); matched++ {
|
||||
if key[matched] != n.key[matched] {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if matched == len(n.key) {
|
||||
if matched == len(key) {
|
||||
// the node key is the same as the key: make the current node as data node
|
||||
// if the node is already a data node, ignore the new data since we only care the first matched node
|
||||
if n.data == nil {
|
||||
n.data = data
|
||||
n.order = order
|
||||
}
|
||||
return n.pindex + 1
|
||||
}
|
||||
|
||||
// the node key is a prefix of the key: create a child node
|
||||
newKey := key[matched:]
|
||||
|
||||
// try adding to a static child
|
||||
if child := n.children[newKey[0]]; child != nil {
|
||||
if pn := child.add(newKey, data, order); pn >= 0 {
|
||||
return pn
|
||||
}
|
||||
}
|
||||
// try adding to a param child
|
||||
for _, child := range n.pchildren {
|
||||
if pn := child.add(newKey, data, order); pn >= 0 {
|
||||
return pn
|
||||
}
|
||||
}
|
||||
|
||||
return n.addChild(newKey, data, order)
|
||||
}
|
||||
|
||||
if matched == 0 || !n.static {
|
||||
// no common prefix, or partial common prefix with a non-static node: should skip this node
|
||||
return -1
|
||||
}
|
||||
|
||||
// the node key shares a partial prefix with the key: split the node key
|
||||
n1 := &node{
|
||||
static: true,
|
||||
key: n.key[matched:],
|
||||
data: n.data,
|
||||
order: n.order,
|
||||
minOrder: n.minOrder,
|
||||
pchildren: n.pchildren,
|
||||
children: n.children,
|
||||
pindex: n.pindex,
|
||||
pnames: n.pnames,
|
||||
}
|
||||
|
||||
n.key = key[0:matched]
|
||||
n.data = nil
|
||||
n.pchildren = make([]*node, 0)
|
||||
n.children = make([]*node, 256)
|
||||
n.children[n1.key[0]] = n1
|
||||
|
||||
return n.add(key, data, order)
|
||||
}
|
||||
|
||||
// addChild creates static and param nodes to store the given data
|
||||
func (n *node) addChild(key string, data interface{}, order int) int {
|
||||
// find the first occurrence of a param token
|
||||
p0, p1 := -1, -1
|
||||
for i := 0; i < len(key); i++ {
|
||||
if p0 < 0 && key[i] == '<' {
|
||||
p0 = i
|
||||
}
|
||||
if p0 >= 0 && key[i] == '>' {
|
||||
p1 = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if p0 > 0 && p1 > 0 || p1 < 0 {
|
||||
// param token occurs after a static string, or no param token: create a static node
|
||||
child := &node{
|
||||
static: true,
|
||||
key: key,
|
||||
minOrder: order,
|
||||
children: make([]*node, 256),
|
||||
pchildren: make([]*node, 0),
|
||||
pindex: n.pindex,
|
||||
pnames: n.pnames,
|
||||
}
|
||||
n.children[key[0]] = child
|
||||
if p1 > 0 {
|
||||
// param token occurs after a static string
|
||||
child.key = key[:p0]
|
||||
n = child
|
||||
} else {
|
||||
// no param token: done adding the child
|
||||
child.data = data
|
||||
child.order = order
|
||||
return child.pindex + 1
|
||||
}
|
||||
}
|
||||
|
||||
// add param node
|
||||
child := &node{
|
||||
static: false,
|
||||
key: key[p0 : p1+1],
|
||||
minOrder: order,
|
||||
children: make([]*node, 256),
|
||||
pchildren: make([]*node, 0),
|
||||
pindex: n.pindex,
|
||||
pnames: n.pnames,
|
||||
}
|
||||
pattern := ""
|
||||
pname := key[p0+1 : p1]
|
||||
for i := p0 + 1; i < p1; i++ {
|
||||
if key[i] == ':' {
|
||||
pname = key[p0+1 : i]
|
||||
pattern = key[i+1 : p1]
|
||||
break
|
||||
}
|
||||
}
|
||||
if pattern != "" {
|
||||
// the param token contains a regular expression
|
||||
child.regex = regexp.MustCompile("^" + pattern)
|
||||
}
|
||||
pnames := make([]string, len(n.pnames)+1)
|
||||
copy(pnames, n.pnames)
|
||||
pnames[len(n.pnames)] = pname
|
||||
child.pnames = pnames
|
||||
child.pindex = len(pnames) - 1
|
||||
n.pchildren = append(n.pchildren, child)
|
||||
|
||||
if p1 == len(key)-1 {
|
||||
// the param token is at the end of the key
|
||||
child.data = data
|
||||
child.order = order
|
||||
return child.pindex + 1
|
||||
}
|
||||
|
||||
// process the rest of the key
|
||||
return child.addChild(key[p1+1:], data, order)
|
||||
}
|
||||
|
||||
// get returns the data item with the key matching the tree rooted at the current node
|
||||
func (n *node) get(key string, pvalues []string) (data interface{}, pnames []string, order int) {
|
||||
order = math.MaxInt32
|
||||
|
||||
repeat:
|
||||
if n.static {
|
||||
// check if the node key is a prefix of the given key
|
||||
// a slightly optimized version of strings.HasPrefix
|
||||
nkl := len(n.key)
|
||||
if nkl > len(key) {
|
||||
return
|
||||
}
|
||||
for i := nkl - 1; i >= 0; i-- {
|
||||
if n.key[i] != key[i] {
|
||||
return
|
||||
}
|
||||
}
|
||||
key = key[nkl:]
|
||||
} else if n.regex != nil {
|
||||
// param node with regular expression
|
||||
if n.regex.String() == "^.*" {
|
||||
pvalues[n.pindex] = key
|
||||
key = ""
|
||||
} else if match := n.regex.FindStringIndex(key); match != nil {
|
||||
pvalues[n.pindex] = key[0:match[1]]
|
||||
key = key[match[1]:]
|
||||
} else {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// param node matching non-"/" characters
|
||||
i, kl := 0, len(key)
|
||||
for ; i < kl; i++ {
|
||||
if key[i] == '/' {
|
||||
pvalues[n.pindex] = key[0:i]
|
||||
key = key[i:]
|
||||
break
|
||||
}
|
||||
}
|
||||
if i == kl {
|
||||
pvalues[n.pindex] = key
|
||||
key = ""
|
||||
}
|
||||
}
|
||||
|
||||
if len(key) > 0 {
|
||||
// find a static child that can match the rest of the key
|
||||
if child := n.children[key[0]]; child != nil {
|
||||
if len(n.pchildren) == 0 {
|
||||
// use goto to avoid recursion when no param children
|
||||
n = child
|
||||
goto repeat
|
||||
}
|
||||
data, pnames, order = child.get(key, pvalues)
|
||||
}
|
||||
} else if n.data != nil {
|
||||
// do not return yet: a param node may match an empty string with smaller order
|
||||
data, pnames, order = n.data, n.pnames, n.order
|
||||
}
|
||||
|
||||
// try matching param children
|
||||
tvalues := pvalues
|
||||
allocated := false
|
||||
for _, child := range n.pchildren {
|
||||
if child.minOrder >= order {
|
||||
continue
|
||||
}
|
||||
if data != nil && !allocated {
|
||||
tvalues = make([]string, len(pvalues))
|
||||
allocated = true
|
||||
}
|
||||
if d, p, s := child.get(key, tvalues); d != nil && s < order {
|
||||
if allocated {
|
||||
for i := child.pindex; i < len(p); i++ {
|
||||
pvalues[i] = tvalues[i]
|
||||
}
|
||||
}
|
||||
data, pnames, order = d, p, s
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (n *node) print(level int) string {
|
||||
r := fmt.Sprintf("%v{key: %v, regex: %v, data: %v, order: %v, minOrder: %v, pindex: %v, pnames: %v}\n", strings.Repeat(" ", level<<2), n.key, n.regex, n.data, n.order, n.minOrder, n.pindex, n.pnames)
|
||||
for _, child := range n.children {
|
||||
if child != nil {
|
||||
r += child.print(level + 1)
|
||||
}
|
||||
}
|
||||
for _, child := range n.pchildren {
|
||||
r += child.print(level + 1)
|
||||
}
|
||||
return r
|
||||
}
|
||||
Reference in New Issue
Block a user