go vendor

This commit is contained in:
lealife
2017-11-30 19:55:33 +08:00
parent 2856da6888
commit 0fb92efbf3
670 changed files with 199010 additions and 0 deletions

21
vendor/github.com/revel/pathtree/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
Copyright (C) 2013 Rob Figueiredo
All Rights Reserved.
MIT LICENSE
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

228
vendor/github.com/revel/pathtree/tree.go generated vendored Normal file
View File

@ -0,0 +1,228 @@
// pathtree implements a tree for fast path lookup.
//
// Restrictions
//
// - Paths must be a '/'-separated list of strings, like a URL or Unix filesystem.
// - All paths must begin with a '/'.
// - Path elements may not contain a '/'.
// - Path elements beginning with a ':' or '*' will be interpreted as wildcards.
// - Trailing slashes are inconsequential.
//
// Wildcards
//
// Wildcards are named path elements that may match any strings in that
// location. Two different kinds of wildcards are permitted:
// - :var - names beginning with ':' will match any single path element.
// - *var - names beginning with '*' will match one or more path elements.
// (however, no path elements may come after a star wildcard)
//
// Extensions
//
// Single element wildcards in the last path element can optionally end with an
// extension. This allows for routes like '/users/:id.json', which will not
// conflict with '/users/:id'.
//
// Algorithm
//
// Paths are mapped to the tree in the following way:
// - Each '/' is a Node in the tree. The root node is the leading '/'.
// - Each Node has edges to other nodes. The edges are named according to the
// possible path elements at that depth in the path.
// - Any Node may have an associated Leaf. Leafs are terminals containing the
// data associated with the path as traversed from the root to that Node.
//
// Edges are implemented as a map from the path element name to the next node in
// the path.
package pathtree
import (
"errors"
"strings"
)
type Node struct {
edges map[string]*Node // the various path elements leading out of this node.
wildcard *Node // if set, this node had a wildcard as its path element.
leaf *Leaf // if set, this is a terminal node for this leaf.
extensions map[string]*Leaf // if set, this is a terminal node with a leaf that ends in a specific extension.
star *Leaf // if set, this path ends in a star.
leafs int // counter for # leafs in the tree
}
type Leaf struct {
Value interface{} // the value associated with this node
Wildcards []string // the wildcard names, in order they appear in the path
order int // the order this leaf was added
}
// New returns a new path tree.
func New() *Node {
return &Node{edges: make(map[string]*Node)}
}
// Add a path and its associated value to the tree.
// - key must begin with "/"
// - key must not duplicate any existing key.
// Returns an error if those conditions do not hold.
func (n *Node) Add(key string, val interface{}) error {
if key == "" || key[0] != '/' {
return errors.New("Path must begin with /")
}
n.leafs++
return n.add(n.leafs, splitPath(key), nil, val)
}
// Adds a leaf to a terminal node.
// If the last wildcard contains an extension, add it to the 'extensions' map.
func (n *Node) addLeaf(leaf *Leaf) error {
extension := stripExtensionFromLastSegment(leaf.Wildcards)
if extension != "" {
if n.extensions == nil {
n.extensions = make(map[string]*Leaf)
}
if n.extensions[extension] != nil {
return errors.New("duplicate path")
}
n.extensions[extension] = leaf
return nil
}
if n.leaf != nil {
return errors.New("duplicate path")
}
n.leaf = leaf
return nil
}
func (n *Node) add(order int, elements, wildcards []string, val interface{}) error {
if len(elements) == 0 {
leaf := &Leaf{
order: order,
Value: val,
Wildcards: wildcards,
}
return n.addLeaf(leaf)
}
var el string
el, elements = elements[0], elements[1:]
if el == "" {
return errors.New("empty path elements are not allowed")
}
// Handle wildcards.
switch el[0] {
case ':':
if n.wildcard == nil {
n.wildcard = New()
}
return n.wildcard.add(order, elements, append(wildcards, el[1:]), val)
case '*':
if n.star != nil {
return errors.New("duplicate path")
}
n.star = &Leaf{
order: order,
Value: val,
Wildcards: append(wildcards, el[1:]),
}
return nil
}
// It's a normal path element.
e, ok := n.edges[el]
if !ok {
e = New()
n.edges[el] = e
}
return e.add(order, elements, wildcards, val)
}
// Find a given path. Any wildcards traversed along the way are expanded and
// returned, along with the value.
func (n *Node) Find(key string) (leaf *Leaf, expansions []string) {
if len(key) == 0 || key[0] != '/' {
return nil, nil
}
return n.find(splitPath(key), nil)
}
func (n *Node) find(elements, exp []string) (leaf *Leaf, expansions []string) {
if len(elements) == 0 {
// If this node has explicit extensions, check if the path matches one.
if len(exp) > 0 && n.extensions != nil {
lastExp := exp[len(exp)-1]
prefix, extension := extensionForPath(lastExp)
if leaf := n.extensions[extension]; leaf != nil {
exp[len(exp)-1] = prefix
return leaf, exp
}
}
return n.leaf, exp
}
// If this node has a star, calculate the star expansions in advance.
var starExpansion string
if n.star != nil {
starExpansion = strings.Join(elements, "/")
}
// Peel off the next element and look up the associated edge.
var el string
el, elements = elements[0], elements[1:]
if nextNode, ok := n.edges[el]; ok {
leaf, expansions = nextNode.find(elements, exp)
}
// Handle colon
if n.wildcard != nil {
wildcardLeaf, wildcardExpansions := n.wildcard.find(elements, append(exp, el))
if wildcardLeaf != nil && (leaf == nil || leaf.order > wildcardLeaf.order) {
leaf = wildcardLeaf
expansions = wildcardExpansions
}
}
// Handle star
if n.star != nil && (leaf == nil || leaf.order > n.star.order) {
leaf = n.star
expansions = append(exp, starExpansion)
}
return
}
func extensionForPath(path string) (string, string) {
dotPosition := strings.LastIndex(path, ".")
if dotPosition != -1 {
return path[:dotPosition], path[dotPosition:]
}
return "", ""
}
func splitPath(key string) []string {
elements := strings.Split(key, "/")
if elements[0] == "" {
elements = elements[1:]
}
if elements[len(elements)-1] == "" {
elements = elements[:len(elements)-1]
}
return elements
}
// stripExtensionFromLastSegment determines if a string slice representing a path
// ends with a file extension, removes the extension from the input, and returns it.
func stripExtensionFromLastSegment(segments []string) string {
if len(segments) == 0 {
return ""
}
lastSegment := segments[len(segments)-1]
prefix, extension := extensionForPath(lastSegment)
if extension != "" {
segments[len(segments)-1] = prefix
}
return extension
}

179
vendor/github.com/revel/pathtree/tree_test.go generated vendored Normal file
View File

@ -0,0 +1,179 @@
package pathtree
import (
"fmt"
"reflect"
"testing"
)
func TestColon(t *testing.T) {
n := New()
n.Add("/:first/:second/", 1)
n.Add("/:first", 2)
n.Add("/", 3)
found(t, n, "/", nil, 3)
found(t, n, "/a", []string{"a"}, 2)
found(t, n, "/a/", []string{"a"}, 2)
found(t, n, "/a/b", []string{"a", "b"}, 1)
found(t, n, "/a/b/", []string{"a", "b"}, 1)
notfound(t, n, "/a/b/c")
}
func TestStar(t *testing.T) {
n := New()
n.Add("/first/second/*star", 1)
n.Add("/:first/*star/", 2)
n.Add("/*star", 3)
n.Add("/", 4)
found(t, n, "/", nil, 4)
found(t, n, "/a", []string{"a"}, 3)
found(t, n, "/a/", []string{"a"}, 3)
found(t, n, "/a/b", []string{"a", "b"}, 2)
found(t, n, "/a/b/", []string{"a", "b"}, 2)
found(t, n, "/a/b/c", []string{"a", "b/c"}, 2)
found(t, n, "/a/b/c/", []string{"a", "b/c"}, 2)
found(t, n, "/a/b/c/d", []string{"a", "b/c/d"}, 2)
found(t, n, "/first/second", []string{"first", "second"}, 2)
found(t, n, "/first/second/", []string{"first", "second"}, 2)
found(t, n, "/first/second/third", []string{"third"}, 1)
}
func TestMixedTree(t *testing.T) {
n := New()
n.Add("/", 0)
n.Add("/path/to/nowhere", 1)
n.Add("/path/:i/nowhere", 2)
n.Add("/:id/to/nowhere", 3)
n.Add("/:a/:b", 4)
n.Add("/not/found", 5)
found(t, n, "/", nil, 0)
found(t, n, "/path/to/nowhere", nil, 1)
found(t, n, "/path/to/nowhere/", nil, 1)
found(t, n, "/path/from/nowhere", []string{"from"}, 2)
found(t, n, "/walk/to/nowhere", []string{"walk"}, 3)
found(t, n, "/path/to/", []string{"path", "to"}, 4)
found(t, n, "/path/to", []string{"path", "to"}, 4)
found(t, n, "/not/found", []string{"not", "found"}, 4)
notfound(t, n, "/path/to/somewhere")
notfound(t, n, "/path/to/nowhere/else")
notfound(t, n, "/path")
notfound(t, n, "/path/")
notfound(t, n, "")
notfound(t, n, "xyz")
notfound(t, n, "/path//to/nowhere")
}
func TestExtensions(t *testing.T) {
n := New()
n.Add("/:first/:second.json", 1)
n.Add("/a/:second.xml", 2)
n.Add("/:first/:second", 3)
found(t, n, "/a/b", []string{"a", "b"}, 3)
found(t, n, "/a/b.json", []string{"a", "b"}, 1)
found(t, n, "/a/b.xml", []string{"b"}, 2)
found(t, n, "/a/b.c.xml", []string{"b.c"}, 2)
found(t, n, "/other/b.xml", []string{"other", "b.xml"}, 3)
}
func TestErrors(t *testing.T) {
n := New()
fails(t, n.Add("//", 1), "empty path elements not allowed")
}
func BenchmarkTree100(b *testing.B) {
n := New()
n.Add("/", "root")
// Exact matches
for i := 0; i < 100; i++ {
depth := i%5 + 1
key := ""
for j := 0; j < depth-1; j++ {
key += fmt.Sprintf("/dir%d", j)
}
key += fmt.Sprintf("/resource%d", i)
n.Add(key, "literal")
// b.Logf("Adding %s", key)
}
// Wildcards at each level if no exact matches work.
for i := 0; i < 5; i++ {
var key string
for j := 0; j < i; j++ {
key += fmt.Sprintf("/dir%d", j)
}
key += "/:var"
n.Add(key, "var")
// b.Logf("Adding %s", key)
}
n.Add("/public/*filepath", "static")
// b.Logf("Adding /public/*filepath")
queries := map[string]string{
"/": "root",
"/dir0/dir1/dir2/dir3/resource4": "literal",
"/dir0/dir1/resource97": "literal",
"/dir0/variable": "var",
"/dir0/dir1/dir2/dir3/variable": "var",
"/public/stylesheets/main.css": "static",
"/public/images/icons/an-image.png": "static",
}
for query, answer := range queries {
leaf, _ := n.Find(query)
if leaf == nil {
b.Errorf("Failed to find leaf for querY %s", query)
return
}
if leaf.Value.(string) != answer {
b.Errorf("Incorrect answer for querY %s: expected: %s, actual: %s",
query, answer, leaf.Value.(string))
return
}
}
b.ResetTimer()
for i := 0; i < b.N/len(queries); i++ {
for k, _ := range queries {
n.Find(k)
}
}
}
func notfound(t *testing.T, n *Node, p string) {
if leaf, _ := n.Find(p); leaf != nil {
t.Errorf("Should not have found: %s", p)
}
}
func found(t *testing.T, n *Node, p string, expectedExpansions []string, val interface{}) {
leaf, expansions := n.Find(p)
if leaf == nil {
t.Errorf("Didn't find: %s", p)
return
}
if !reflect.DeepEqual(expansions, expectedExpansions) {
t.Errorf("%s: Wildcard expansions (actual) %v != %v (expected)", p, expansions, expectedExpansions)
}
if leaf.Value != val {
t.Errorf("%s: Value (actual) %v != %v (expected)", p, leaf.Value, val)
}
}
func fails(t *testing.T, err error, msg string) {
if err == nil {
t.Errorf("expected an error. %s", msg)
}
}