mirror of
https://github.com/minio/minio.git
synced 2024-12-24 22:25:54 -05:00
Removing old server
Removing storage drivers
This commit is contained in:
parent
7c861eec12
commit
f550e84cf4
19
Godeps/Godeps.json
generated
19
Godeps/Godeps.json
generated
@ -5,11 +5,6 @@
|
||||
"./..."
|
||||
],
|
||||
"Deps": [
|
||||
{
|
||||
"ImportPath": "github.com/codegangsta/cli",
|
||||
"Comment": "1.2.0-42-gfbda1ce",
|
||||
"Rev": "fbda1ce02d5dabcee952040e5f4025753b6c9ce0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gorilla/context",
|
||||
"Rev": "50c25fb3b2b3b3cc724e9b6ac75fb44b3bccd0da"
|
||||
@ -18,20 +13,6 @@
|
||||
"ImportPath": "github.com/gorilla/mux",
|
||||
"Rev": "e444e69cbd2e2e3e0749a2f3c717cec491552bbf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/minio-io/go-patricia/patricia",
|
||||
"Comment": "v1.0.1-2-g9469ac2",
|
||||
"Rev": "9469ac299b073cc3adb646559a176ba4041fefd5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/spaolacci/murmur3",
|
||||
"Rev": "9d87265e0948d305ffe0383d54e59c3ac3482454"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/tchap/go-patricia/patricia",
|
||||
"Comment": "v1.0.1",
|
||||
"Rev": "f64d0a63cd3363481c898faa9339de04d12213f9"
|
||||
},
|
||||
{
|
||||
"ImportPath": "gopkg.in/check.v1",
|
||||
"Rev": "64131543e7896d5bcc6bd5a76287eb75ea96c673"
|
||||
|
6
Godeps/_workspace/src/github.com/codegangsta/cli/.travis.yml
generated
vendored
6
Godeps/_workspace/src/github.com/codegangsta/cli/.travis.yml
generated
vendored
@ -1,6 +0,0 @@
|
||||
language: go
|
||||
go: 1.1
|
||||
|
||||
script:
|
||||
- go vet ./...
|
||||
- go test -v ./...
|
21
Godeps/_workspace/src/github.com/codegangsta/cli/LICENSE
generated
vendored
21
Godeps/_workspace/src/github.com/codegangsta/cli/LICENSE
generated
vendored
@ -1,21 +0,0 @@
|
||||
Copyright (C) 2013 Jeremy Saenz
|
||||
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.
|
298
Godeps/_workspace/src/github.com/codegangsta/cli/README.md
generated
vendored
298
Godeps/_workspace/src/github.com/codegangsta/cli/README.md
generated
vendored
@ -1,298 +0,0 @@
|
||||
[![Build Status](https://travis-ci.org/codegangsta/cli.png?branch=master)](https://travis-ci.org/codegangsta/cli)
|
||||
|
||||
# cli.go
|
||||
cli.go is simple, fast, and fun package for building command line apps in Go. The goal is to enable developers to write fast and distributable command line applications in an expressive way.
|
||||
|
||||
You can view the API docs here:
|
||||
http://godoc.org/github.com/codegangsta/cli
|
||||
|
||||
## Overview
|
||||
Command line apps are usually so tiny that there is absolutely no reason why your code should *not* be self-documenting. Things like generating help text and parsing command flags/options should not hinder productivity when writing a command line app.
|
||||
|
||||
**This is where cli.go comes into play.** cli.go makes command line programming fun, organized, and expressive!
|
||||
|
||||
## Installation
|
||||
Make sure you have a working Go environment (go 1.1 is *required*). [See the install instructions](http://golang.org/doc/install.html).
|
||||
|
||||
To install `cli.go`, simply run:
|
||||
```
|
||||
$ go get github.com/codegangsta/cli
|
||||
```
|
||||
|
||||
Make sure your `PATH` includes to the `$GOPATH/bin` directory so your commands can be easily used:
|
||||
```
|
||||
export PATH=$PATH:$GOPATH/bin
|
||||
```
|
||||
|
||||
## Getting Started
|
||||
One of the philosophies behind cli.go is that an API should be playful and full of discovery. So a cli.go app can be as little as one line of code in `main()`.
|
||||
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cli.NewApp().Run(os.Args)
|
||||
}
|
||||
```
|
||||
|
||||
This app will run and show help text, but is not very useful. Let's give an action to execute and some help documentation:
|
||||
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "boom"
|
||||
app.Usage = "make an explosive entrance"
|
||||
app.Action = func(c *cli.Context) {
|
||||
println("boom! I say!")
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
}
|
||||
```
|
||||
|
||||
Running this already gives you a ton of functionality, plus support for things like subcommands and flags, which are covered below.
|
||||
|
||||
## Example
|
||||
|
||||
Being a programmer can be a lonely job. Thankfully by the power of automation that is not the case! Let's create a greeter app to fend off our demons of loneliness!
|
||||
|
||||
Start by creating a directory named `greet`, and within it, add a file, `greet.go` with the following code in it:
|
||||
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "greet"
|
||||
app.Usage = "fight the loneliness!"
|
||||
app.Action = func(c *cli.Context) {
|
||||
println("Hello friend!")
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
}
|
||||
```
|
||||
|
||||
Install our command to the `$GOPATH/bin` directory:
|
||||
|
||||
```
|
||||
$ go install
|
||||
```
|
||||
|
||||
Finally run our new command:
|
||||
|
||||
```
|
||||
$ greet
|
||||
Hello friend!
|
||||
```
|
||||
|
||||
cli.go also generates some bitchass help text:
|
||||
```
|
||||
$ greet help
|
||||
NAME:
|
||||
greet - fight the loneliness!
|
||||
|
||||
USAGE:
|
||||
greet [global options] command [command options] [arguments...]
|
||||
|
||||
VERSION:
|
||||
0.0.0
|
||||
|
||||
COMMANDS:
|
||||
help, h Shows a list of commands or help for one command
|
||||
|
||||
GLOBAL OPTIONS
|
||||
--version Shows version information
|
||||
```
|
||||
|
||||
### Arguments
|
||||
You can lookup arguments by calling the `Args` function on `cli.Context`.
|
||||
|
||||
``` go
|
||||
...
|
||||
app.Action = func(c *cli.Context) {
|
||||
println("Hello", c.Args()[0])
|
||||
}
|
||||
...
|
||||
```
|
||||
|
||||
### Flags
|
||||
Setting and querying flags is simple.
|
||||
``` go
|
||||
...
|
||||
app.Flags = []cli.Flag {
|
||||
cli.StringFlag{
|
||||
Name: "lang",
|
||||
Value: "english",
|
||||
Usage: "language for the greeting",
|
||||
},
|
||||
}
|
||||
app.Action = func(c *cli.Context) {
|
||||
name := "someone"
|
||||
if len(c.Args()) > 0 {
|
||||
name = c.Args()[0]
|
||||
}
|
||||
if c.String("lang") == "spanish" {
|
||||
println("Hola", name)
|
||||
} else {
|
||||
println("Hello", name)
|
||||
}
|
||||
}
|
||||
...
|
||||
```
|
||||
|
||||
#### Alternate Names
|
||||
|
||||
You can set alternate (or short) names for flags by providing a comma-delimited list for the `Name`. e.g.
|
||||
|
||||
``` go
|
||||
app.Flags = []cli.Flag {
|
||||
cli.StringFlag{
|
||||
Name: "lang, l",
|
||||
Value: "english",
|
||||
Usage: "language for the greeting",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
That flag can then be set with `--lang spanish` or `-l spanish`. Note that giving two different forms of the same flag in the same command invocation is an error.
|
||||
|
||||
#### Values from the Environment
|
||||
|
||||
You can also have the default value set from the environment via `EnvVar`. e.g.
|
||||
|
||||
``` go
|
||||
app.Flags = []cli.Flag {
|
||||
cli.StringFlag{
|
||||
Name: "lang, l",
|
||||
Value: "english",
|
||||
Usage: "language for the greeting",
|
||||
EnvVar: "APP_LANG",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
The `EnvVar` may also be given as a comma-delimited "cascade", where the first environment variable that resolves is used as the default.
|
||||
|
||||
``` go
|
||||
app.Flags = []cli.Flag {
|
||||
cli.StringFlag{
|
||||
Name: "lang, l",
|
||||
Value: "english",
|
||||
Usage: "language for the greeting",
|
||||
EnvVar: "LEGACY_COMPAT_LANG,APP_LANG,LANG",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### Subcommands
|
||||
|
||||
Subcommands can be defined for a more git-like command line app.
|
||||
```go
|
||||
...
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
Name: "add",
|
||||
ShortName: "a",
|
||||
Usage: "add a task to the list",
|
||||
Action: func(c *cli.Context) {
|
||||
println("added task: ", c.Args().First())
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "complete",
|
||||
ShortName: "c",
|
||||
Usage: "complete a task on the list",
|
||||
Action: func(c *cli.Context) {
|
||||
println("completed task: ", c.Args().First())
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "template",
|
||||
ShortName: "r",
|
||||
Usage: "options for task templates",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "add",
|
||||
Usage: "add a new template",
|
||||
Action: func(c *cli.Context) {
|
||||
println("new task template: ", c.Args().First())
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "remove",
|
||||
Usage: "remove an existing template",
|
||||
Action: func(c *cli.Context) {
|
||||
println("removed task template: ", c.Args().First())
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
...
|
||||
```
|
||||
|
||||
### Bash Completion
|
||||
|
||||
You can enable completion commands by setting the `EnableBashCompletion`
|
||||
flag on the `App` object. By default, this setting will only auto-complete to
|
||||
show an app's subcommands, but you can write your own completion methods for
|
||||
the App or its subcommands.
|
||||
```go
|
||||
...
|
||||
var tasks = []string{"cook", "clean", "laundry", "eat", "sleep", "code"}
|
||||
app := cli.NewApp()
|
||||
app.EnableBashCompletion = true
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
Name: "complete",
|
||||
ShortName: "c",
|
||||
Usage: "complete a task on the list",
|
||||
Action: func(c *cli.Context) {
|
||||
println("completed task: ", c.Args().First())
|
||||
},
|
||||
BashComplete: func(c *cli.Context) {
|
||||
// This will complete if no args are passed
|
||||
if len(c.Args()) > 0 {
|
||||
return
|
||||
}
|
||||
for _, t := range tasks {
|
||||
fmt.Println(t)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
...
|
||||
```
|
||||
|
||||
#### To Enable
|
||||
|
||||
Source the `autocomplete/bash_autocomplete` file in your `.bashrc` file while
|
||||
setting the `PROG` variable to the name of your program:
|
||||
|
||||
`PROG=myprogram source /.../cli/autocomplete/bash_autocomplete`
|
||||
|
||||
|
||||
## Contribution Guidelines
|
||||
Feel free to put up a pull request to fix a bug or maybe add a feature. I will give it a code review and make sure that it does not break backwards compatibility. If I or any other collaborators agree that it is in line with the vision of the project, we will work with you to get the code into a mergeable state and merge it into the master branch.
|
||||
|
||||
If you are have contributed something significant to the project, I will most likely add you as a collaborator. As a collaborator you are given the ability to merge others pull requests. It is very important that new code does not break existing code, so be careful about what code you do choose to merge. If you have any questions feel free to link @codegangsta to the issue in question and we can review it together.
|
||||
|
||||
If you feel like you have contributed to the project but have not yet been added as a collaborator, I probably forgot to add you. Hit @codegangsta up over email and we will get it figured out.
|
251
Godeps/_workspace/src/github.com/codegangsta/cli/app.go
generated
vendored
251
Godeps/_workspace/src/github.com/codegangsta/cli/app.go
generated
vendored
@ -1,251 +0,0 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
// App is the main structure of a cli application. It is recomended that
|
||||
// and app be created with the cli.NewApp() function
|
||||
type App struct {
|
||||
// The name of the program. Defaults to os.Args[0]
|
||||
Name string
|
||||
// Description of the program.
|
||||
Usage string
|
||||
// Version of the program
|
||||
Version string
|
||||
// List of commands to execute
|
||||
Commands []Command
|
||||
// List of flags to parse
|
||||
Flags []Flag
|
||||
// Boolean to enable bash completion commands
|
||||
EnableBashCompletion bool
|
||||
// Boolean to hide built-in help command
|
||||
HideHelp bool
|
||||
// Boolean to hide built-in version flag
|
||||
HideVersion bool
|
||||
// An action to execute when the bash-completion flag is set
|
||||
BashComplete func(context *Context)
|
||||
// An action to execute before any subcommands are run, but after the context is ready
|
||||
// If a non-nil error is returned, no subcommands are run
|
||||
Before func(context *Context) error
|
||||
// The action to execute when no subcommands are specified
|
||||
Action func(context *Context)
|
||||
// Execute this function if the proper command cannot be found
|
||||
CommandNotFound func(context *Context, command string)
|
||||
// Compilation date
|
||||
Compiled time.Time
|
||||
// Author
|
||||
Author string
|
||||
// Author e-mail
|
||||
Email string
|
||||
}
|
||||
|
||||
// Tries to find out when this binary was compiled.
|
||||
// Returns the current time if it fails to find it.
|
||||
func compileTime() time.Time {
|
||||
info, err := os.Stat(os.Args[0])
|
||||
if err != nil {
|
||||
return time.Now()
|
||||
}
|
||||
return info.ModTime()
|
||||
}
|
||||
|
||||
// Creates a new cli Application with some reasonable defaults for Name, Usage, Version and Action.
|
||||
func NewApp() *App {
|
||||
return &App{
|
||||
Name: os.Args[0],
|
||||
Usage: "A new cli application",
|
||||
Version: "0.0.0",
|
||||
BashComplete: DefaultAppComplete,
|
||||
Action: helpCommand.Action,
|
||||
Compiled: compileTime(),
|
||||
}
|
||||
}
|
||||
|
||||
// Entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination
|
||||
func (a *App) Run(arguments []string) error {
|
||||
// append help to commands
|
||||
if a.Command(helpCommand.Name) == nil && !a.HideHelp {
|
||||
a.Commands = append(a.Commands, helpCommand)
|
||||
a.appendFlag(HelpFlag)
|
||||
}
|
||||
|
||||
//append version/help flags
|
||||
if a.EnableBashCompletion {
|
||||
a.appendFlag(BashCompletionFlag)
|
||||
}
|
||||
|
||||
if !a.HideVersion {
|
||||
a.appendFlag(VersionFlag)
|
||||
}
|
||||
|
||||
// parse flags
|
||||
set := flagSet(a.Name, a.Flags)
|
||||
set.SetOutput(ioutil.Discard)
|
||||
err := set.Parse(arguments[1:])
|
||||
nerr := normalizeFlags(a.Flags, set)
|
||||
if nerr != nil {
|
||||
fmt.Println(nerr)
|
||||
context := NewContext(a, set, set)
|
||||
ShowAppHelp(context)
|
||||
fmt.Println("")
|
||||
return nerr
|
||||
}
|
||||
context := NewContext(a, set, set)
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("Incorrect Usage.\n\n")
|
||||
ShowAppHelp(context)
|
||||
fmt.Println("")
|
||||
return err
|
||||
}
|
||||
|
||||
if checkCompletions(context) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if checkHelp(context) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if checkVersion(context) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if a.Before != nil {
|
||||
err := a.Before(context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
args := context.Args()
|
||||
if args.Present() {
|
||||
name := args.First()
|
||||
c := a.Command(name)
|
||||
if c != nil {
|
||||
return c.Run(context)
|
||||
}
|
||||
}
|
||||
|
||||
// Run default Action
|
||||
a.Action(context)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Another entry point to the cli app, takes care of passing arguments and error handling
|
||||
func (a *App) RunAndExitOnError() {
|
||||
if err := a.Run(os.Args); err != nil {
|
||||
os.Stderr.WriteString(fmt.Sprintln(err))
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Invokes the subcommand given the context, parses ctx.Args() to generate command-specific flags
|
||||
func (a *App) RunAsSubcommand(ctx *Context) error {
|
||||
// append help to commands
|
||||
if len(a.Commands) > 0 {
|
||||
if a.Command(helpCommand.Name) == nil && !a.HideHelp {
|
||||
a.Commands = append(a.Commands, helpCommand)
|
||||
a.appendFlag(HelpFlag)
|
||||
}
|
||||
}
|
||||
|
||||
// append flags
|
||||
if a.EnableBashCompletion {
|
||||
a.appendFlag(BashCompletionFlag)
|
||||
}
|
||||
|
||||
// parse flags
|
||||
set := flagSet(a.Name, a.Flags)
|
||||
set.SetOutput(ioutil.Discard)
|
||||
err := set.Parse(ctx.Args().Tail())
|
||||
nerr := normalizeFlags(a.Flags, set)
|
||||
context := NewContext(a, set, ctx.globalSet)
|
||||
|
||||
if nerr != nil {
|
||||
fmt.Println(nerr)
|
||||
if len(a.Commands) > 0 {
|
||||
ShowSubcommandHelp(context)
|
||||
} else {
|
||||
ShowCommandHelp(ctx, context.Args().First())
|
||||
}
|
||||
fmt.Println("")
|
||||
return nerr
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("Incorrect Usage.\n\n")
|
||||
ShowSubcommandHelp(context)
|
||||
return err
|
||||
}
|
||||
|
||||
if checkCompletions(context) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(a.Commands) > 0 {
|
||||
if checkSubcommandHelp(context) {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
if checkCommandHelp(ctx, context.Args().First()) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if a.Before != nil {
|
||||
err := a.Before(context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
args := context.Args()
|
||||
if args.Present() {
|
||||
name := args.First()
|
||||
c := a.Command(name)
|
||||
if c != nil {
|
||||
return c.Run(context)
|
||||
}
|
||||
}
|
||||
|
||||
// Run default Action
|
||||
if len(a.Commands) > 0 {
|
||||
a.Action(context)
|
||||
} else {
|
||||
a.Action(ctx)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns the named command on App. Returns nil if the command does not exist
|
||||
func (a *App) Command(name string) *Command {
|
||||
for _, c := range a.Commands {
|
||||
if c.HasName(name) {
|
||||
return &c
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) hasFlag(flag Flag) bool {
|
||||
for _, f := range a.Flags {
|
||||
if flag == f {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *App) appendFlag(flag Flag) {
|
||||
if !a.hasFlag(flag) {
|
||||
a.Flags = append(a.Flags, flag)
|
||||
}
|
||||
}
|
423
Godeps/_workspace/src/github.com/codegangsta/cli/app_test.go
generated
vendored
423
Godeps/_workspace/src/github.com/codegangsta/cli/app_test.go
generated
vendored
@ -1,423 +0,0 @@
|
||||
package cli_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
func ExampleApp() {
|
||||
// set args for examples sake
|
||||
os.Args = []string{"greet", "--name", "Jeremy"}
|
||||
|
||||
app := cli.NewApp()
|
||||
app.Name = "greet"
|
||||
app.Flags = []cli.Flag{
|
||||
cli.StringFlag{Name: "name", Value: "bob", Usage: "a name to say"},
|
||||
}
|
||||
app.Action = func(c *cli.Context) {
|
||||
fmt.Printf("Hello %v\n", c.String("name"))
|
||||
}
|
||||
app.Run(os.Args)
|
||||
// Output:
|
||||
// Hello Jeremy
|
||||
}
|
||||
|
||||
func ExampleAppSubcommand() {
|
||||
// set args for examples sake
|
||||
os.Args = []string{"say", "hi", "english", "--name", "Jeremy"}
|
||||
app := cli.NewApp()
|
||||
app.Name = "say"
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
Name: "hello",
|
||||
ShortName: "hi",
|
||||
Usage: "use it to see a description",
|
||||
Description: "This is how we describe hello the function",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "english",
|
||||
ShortName: "en",
|
||||
Usage: "sends a greeting in english",
|
||||
Description: "greets someone in english",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "name",
|
||||
Value: "Bob",
|
||||
Usage: "Name of the person to greet",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) {
|
||||
fmt.Println("Hello,", c.String("name"))
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
// Output:
|
||||
// Hello, Jeremy
|
||||
}
|
||||
|
||||
func ExampleAppHelp() {
|
||||
// set args for examples sake
|
||||
os.Args = []string{"greet", "h", "describeit"}
|
||||
|
||||
app := cli.NewApp()
|
||||
app.Name = "greet"
|
||||
app.Flags = []cli.Flag{
|
||||
cli.StringFlag{Name: "name", Value: "bob", Usage: "a name to say"},
|
||||
}
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
Name: "describeit",
|
||||
ShortName: "d",
|
||||
Usage: "use it to see a description",
|
||||
Description: "This is how we describe describeit the function",
|
||||
Action: func(c *cli.Context) {
|
||||
fmt.Printf("i like to describe things")
|
||||
},
|
||||
},
|
||||
}
|
||||
app.Run(os.Args)
|
||||
// Output:
|
||||
// NAME:
|
||||
// describeit - use it to see a description
|
||||
//
|
||||
// USAGE:
|
||||
// command describeit [arguments...]
|
||||
//
|
||||
// DESCRIPTION:
|
||||
// This is how we describe describeit the function
|
||||
}
|
||||
|
||||
func ExampleAppBashComplete() {
|
||||
// set args for examples sake
|
||||
os.Args = []string{"greet", "--generate-bash-completion"}
|
||||
|
||||
app := cli.NewApp()
|
||||
app.Name = "greet"
|
||||
app.EnableBashCompletion = true
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
Name: "describeit",
|
||||
ShortName: "d",
|
||||
Usage: "use it to see a description",
|
||||
Description: "This is how we describe describeit the function",
|
||||
Action: func(c *cli.Context) {
|
||||
fmt.Printf("i like to describe things")
|
||||
},
|
||||
}, {
|
||||
Name: "next",
|
||||
Usage: "next example",
|
||||
Description: "more stuff to see when generating bash completion",
|
||||
Action: func(c *cli.Context) {
|
||||
fmt.Printf("the next example")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
// Output:
|
||||
// describeit
|
||||
// d
|
||||
// next
|
||||
// help
|
||||
// h
|
||||
}
|
||||
|
||||
func TestApp_Run(t *testing.T) {
|
||||
s := ""
|
||||
|
||||
app := cli.NewApp()
|
||||
app.Action = func(c *cli.Context) {
|
||||
s = s + c.Args().First()
|
||||
}
|
||||
|
||||
err := app.Run([]string{"command", "foo"})
|
||||
expect(t, err, nil)
|
||||
err = app.Run([]string{"command", "bar"})
|
||||
expect(t, err, nil)
|
||||
expect(t, s, "foobar")
|
||||
}
|
||||
|
||||
var commandAppTests = []struct {
|
||||
name string
|
||||
expected bool
|
||||
}{
|
||||
{"foobar", true},
|
||||
{"batbaz", true},
|
||||
{"b", true},
|
||||
{"f", true},
|
||||
{"bat", false},
|
||||
{"nothing", false},
|
||||
}
|
||||
|
||||
func TestApp_Command(t *testing.T) {
|
||||
app := cli.NewApp()
|
||||
fooCommand := cli.Command{Name: "foobar", ShortName: "f"}
|
||||
batCommand := cli.Command{Name: "batbaz", ShortName: "b"}
|
||||
app.Commands = []cli.Command{
|
||||
fooCommand,
|
||||
batCommand,
|
||||
}
|
||||
|
||||
for _, test := range commandAppTests {
|
||||
expect(t, app.Command(test.name) != nil, test.expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApp_CommandWithArgBeforeFlags(t *testing.T) {
|
||||
var parsedOption, firstArg string
|
||||
|
||||
app := cli.NewApp()
|
||||
command := cli.Command{
|
||||
Name: "cmd",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{Name: "option", Value: "", Usage: "some option"},
|
||||
},
|
||||
Action: func(c *cli.Context) {
|
||||
parsedOption = c.String("option")
|
||||
firstArg = c.Args().First()
|
||||
},
|
||||
}
|
||||
app.Commands = []cli.Command{command}
|
||||
|
||||
app.Run([]string{"", "cmd", "my-arg", "--option", "my-option"})
|
||||
|
||||
expect(t, parsedOption, "my-option")
|
||||
expect(t, firstArg, "my-arg")
|
||||
}
|
||||
|
||||
func TestApp_Float64Flag(t *testing.T) {
|
||||
var meters float64
|
||||
|
||||
app := cli.NewApp()
|
||||
app.Flags = []cli.Flag{
|
||||
cli.Float64Flag{Name: "height", Value: 1.5, Usage: "Set the height, in meters"},
|
||||
}
|
||||
app.Action = func(c *cli.Context) {
|
||||
meters = c.Float64("height")
|
||||
}
|
||||
|
||||
app.Run([]string{"", "--height", "1.93"})
|
||||
expect(t, meters, 1.93)
|
||||
}
|
||||
|
||||
func TestApp_ParseSliceFlags(t *testing.T) {
|
||||
var parsedOption, firstArg string
|
||||
var parsedIntSlice []int
|
||||
var parsedStringSlice []string
|
||||
|
||||
app := cli.NewApp()
|
||||
command := cli.Command{
|
||||
Name: "cmd",
|
||||
Flags: []cli.Flag{
|
||||
cli.IntSliceFlag{Name: "p", Value: &cli.IntSlice{}, Usage: "set one or more ip addr"},
|
||||
cli.StringSliceFlag{Name: "ip", Value: &cli.StringSlice{}, Usage: "set one or more ports to open"},
|
||||
},
|
||||
Action: func(c *cli.Context) {
|
||||
parsedIntSlice = c.IntSlice("p")
|
||||
parsedStringSlice = c.StringSlice("ip")
|
||||
parsedOption = c.String("option")
|
||||
firstArg = c.Args().First()
|
||||
},
|
||||
}
|
||||
app.Commands = []cli.Command{command}
|
||||
|
||||
app.Run([]string{"", "cmd", "my-arg", "-p", "22", "-p", "80", "-ip", "8.8.8.8", "-ip", "8.8.4.4"})
|
||||
|
||||
IntsEquals := func(a, b []int) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i, v := range a {
|
||||
if v != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
StrsEquals := func(a, b []string) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i, v := range a {
|
||||
if v != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
var expectedIntSlice = []int{22, 80}
|
||||
var expectedStringSlice = []string{"8.8.8.8", "8.8.4.4"}
|
||||
|
||||
if !IntsEquals(parsedIntSlice, expectedIntSlice) {
|
||||
t.Errorf("%v does not match %v", parsedIntSlice, expectedIntSlice)
|
||||
}
|
||||
|
||||
if !StrsEquals(parsedStringSlice, expectedStringSlice) {
|
||||
t.Errorf("%v does not match %v", parsedStringSlice, expectedStringSlice)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApp_BeforeFunc(t *testing.T) {
|
||||
beforeRun, subcommandRun := false, false
|
||||
beforeError := fmt.Errorf("fail")
|
||||
var err error
|
||||
|
||||
app := cli.NewApp()
|
||||
|
||||
app.Before = func(c *cli.Context) error {
|
||||
beforeRun = true
|
||||
s := c.String("opt")
|
||||
if s == "fail" {
|
||||
return beforeError
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
app.Commands = []cli.Command{
|
||||
cli.Command{
|
||||
Name: "sub",
|
||||
Action: func(c *cli.Context) {
|
||||
subcommandRun = true
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
app.Flags = []cli.Flag{
|
||||
cli.StringFlag{Name: "opt"},
|
||||
}
|
||||
|
||||
// run with the Before() func succeeding
|
||||
err = app.Run([]string{"command", "--opt", "succeed", "sub"})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Run error: %s", err)
|
||||
}
|
||||
|
||||
if beforeRun == false {
|
||||
t.Errorf("Before() not executed when expected")
|
||||
}
|
||||
|
||||
if subcommandRun == false {
|
||||
t.Errorf("Subcommand not executed when expected")
|
||||
}
|
||||
|
||||
// reset
|
||||
beforeRun, subcommandRun = false, false
|
||||
|
||||
// run with the Before() func failing
|
||||
err = app.Run([]string{"command", "--opt", "fail", "sub"})
|
||||
|
||||
// should be the same error produced by the Before func
|
||||
if err != beforeError {
|
||||
t.Errorf("Run error expected, but not received")
|
||||
}
|
||||
|
||||
if beforeRun == false {
|
||||
t.Errorf("Before() not executed when expected")
|
||||
}
|
||||
|
||||
if subcommandRun == true {
|
||||
t.Errorf("Subcommand executed when NOT expected")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestAppHelpPrinter(t *testing.T) {
|
||||
oldPrinter := cli.HelpPrinter
|
||||
defer func() {
|
||||
cli.HelpPrinter = oldPrinter
|
||||
}()
|
||||
|
||||
var wasCalled = false
|
||||
cli.HelpPrinter = func(template string, data interface{}) {
|
||||
wasCalled = true
|
||||
}
|
||||
|
||||
app := cli.NewApp()
|
||||
app.Run([]string{"-h"})
|
||||
|
||||
if wasCalled == false {
|
||||
t.Errorf("Help printer expected to be called, but was not")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppVersionPrinter(t *testing.T) {
|
||||
oldPrinter := cli.VersionPrinter
|
||||
defer func() {
|
||||
cli.VersionPrinter = oldPrinter
|
||||
}()
|
||||
|
||||
var wasCalled = false
|
||||
cli.VersionPrinter = func(c *cli.Context) {
|
||||
wasCalled = true
|
||||
}
|
||||
|
||||
app := cli.NewApp()
|
||||
ctx := cli.NewContext(app, nil, nil)
|
||||
cli.ShowVersion(ctx)
|
||||
|
||||
if wasCalled == false {
|
||||
t.Errorf("Version printer expected to be called, but was not")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppCommandNotFound(t *testing.T) {
|
||||
beforeRun, subcommandRun := false, false
|
||||
app := cli.NewApp()
|
||||
|
||||
app.CommandNotFound = func(c *cli.Context, command string) {
|
||||
beforeRun = true
|
||||
}
|
||||
|
||||
app.Commands = []cli.Command{
|
||||
cli.Command{
|
||||
Name: "bar",
|
||||
Action: func(c *cli.Context) {
|
||||
subcommandRun = true
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
app.Run([]string{"command", "foo"})
|
||||
|
||||
expect(t, beforeRun, true)
|
||||
expect(t, subcommandRun, false)
|
||||
}
|
||||
|
||||
func TestGlobalFlagsInSubcommands(t *testing.T) {
|
||||
subcommandRun := false
|
||||
app := cli.NewApp()
|
||||
|
||||
app.Flags = []cli.Flag{
|
||||
cli.BoolFlag{Name: "debug, d", Usage: "Enable debugging"},
|
||||
}
|
||||
|
||||
app.Commands = []cli.Command{
|
||||
cli.Command{
|
||||
Name: "foo",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "bar",
|
||||
Action: func(c *cli.Context) {
|
||||
if c.GlobalBool("debug") {
|
||||
subcommandRun = true
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
app.Run([]string{"command", "-d", "foo", "bar"})
|
||||
|
||||
expect(t, subcommandRun, true)
|
||||
}
|
13
Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/bash_autocomplete
generated
vendored
13
Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/bash_autocomplete
generated
vendored
@ -1,13 +0,0 @@
|
||||
#! /bin/bash
|
||||
|
||||
_cli_bash_autocomplete() {
|
||||
local cur prev opts base
|
||||
COMPREPLY=()
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||
opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion )
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
}
|
||||
|
||||
complete -F _cli_bash_autocomplete $PROG
|
5
Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/zsh_autocomplete
generated
vendored
5
Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/zsh_autocomplete
generated
vendored
@ -1,5 +0,0 @@
|
||||
autoload -U compinit && compinit
|
||||
autoload -U bashcompinit && bashcompinit
|
||||
|
||||
script_dir=$(dirname $0)
|
||||
source ${script_dir}/bash_autocomplete
|
19
Godeps/_workspace/src/github.com/codegangsta/cli/cli.go
generated
vendored
19
Godeps/_workspace/src/github.com/codegangsta/cli/cli.go
generated
vendored
@ -1,19 +0,0 @@
|
||||
// Package cli provides a minimal framework for creating and organizing command line
|
||||
// Go applications. cli is designed to be easy to understand and write, the most simple
|
||||
// cli application can be written as follows:
|
||||
// func main() {
|
||||
// cli.NewApp().Run(os.Args)
|
||||
// }
|
||||
//
|
||||
// Of course this application does not do much, so let's make this an actual application:
|
||||
// func main() {
|
||||
// app := cli.NewApp()
|
||||
// app.Name = "greet"
|
||||
// app.Usage = "say a greeting"
|
||||
// app.Action = func(c *cli.Context) {
|
||||
// println("Greetings")
|
||||
// }
|
||||
//
|
||||
// app.Run(os.Args)
|
||||
// }
|
||||
package cli
|
100
Godeps/_workspace/src/github.com/codegangsta/cli/cli_test.go
generated
vendored
100
Godeps/_workspace/src/github.com/codegangsta/cli/cli_test.go
generated
vendored
@ -1,100 +0,0 @@
|
||||
package cli_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
func Example() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "todo"
|
||||
app.Usage = "task list on the command line"
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
Name: "add",
|
||||
ShortName: "a",
|
||||
Usage: "add a task to the list",
|
||||
Action: func(c *cli.Context) {
|
||||
println("added task: ", c.Args().First())
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "complete",
|
||||
ShortName: "c",
|
||||
Usage: "complete a task on the list",
|
||||
Action: func(c *cli.Context) {
|
||||
println("completed task: ", c.Args().First())
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
}
|
||||
|
||||
func ExampleSubcommand() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "say"
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
Name: "hello",
|
||||
ShortName: "hi",
|
||||
Usage: "use it to see a description",
|
||||
Description: "This is how we describe hello the function",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "english",
|
||||
ShortName: "en",
|
||||
Usage: "sends a greeting in english",
|
||||
Description: "greets someone in english",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "name",
|
||||
Value: "Bob",
|
||||
Usage: "Name of the person to greet",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) {
|
||||
println("Hello, ", c.String("name"))
|
||||
},
|
||||
}, {
|
||||
Name: "spanish",
|
||||
ShortName: "sp",
|
||||
Usage: "sends a greeting in spanish",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "surname",
|
||||
Value: "Jones",
|
||||
Usage: "Surname of the person to greet",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) {
|
||||
println("Hola, ", c.String("surname"))
|
||||
},
|
||||
}, {
|
||||
Name: "french",
|
||||
ShortName: "fr",
|
||||
Usage: "sends a greeting in french",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "nickname",
|
||||
Value: "Stevie",
|
||||
Usage: "Nickname of the person to greet",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) {
|
||||
println("Bonjour, ", c.String("nickname"))
|
||||
},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Name: "bye",
|
||||
Usage: "says goodbye",
|
||||
Action: func(c *cli.Context) {
|
||||
println("bye")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
}
|
144
Godeps/_workspace/src/github.com/codegangsta/cli/command.go
generated
vendored
144
Godeps/_workspace/src/github.com/codegangsta/cli/command.go
generated
vendored
@ -1,144 +0,0 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Command is a subcommand for a cli.App.
|
||||
type Command struct {
|
||||
// The name of the command
|
||||
Name string
|
||||
// short name of the command. Typically one character
|
||||
ShortName string
|
||||
// A short description of the usage of this command
|
||||
Usage string
|
||||
// A longer explanation of how the command works
|
||||
Description string
|
||||
// The function to call when checking for bash command completions
|
||||
BashComplete func(context *Context)
|
||||
// An action to execute before any sub-subcommands are run, but after the context is ready
|
||||
// If a non-nil error is returned, no sub-subcommands are run
|
||||
Before func(context *Context) error
|
||||
// The function to call when this command is invoked
|
||||
Action func(context *Context)
|
||||
// List of child commands
|
||||
Subcommands []Command
|
||||
// List of flags to parse
|
||||
Flags []Flag
|
||||
// Treat all flags as normal arguments if true
|
||||
SkipFlagParsing bool
|
||||
// Boolean to hide built-in help command
|
||||
HideHelp bool
|
||||
}
|
||||
|
||||
// Invokes the command given the context, parses ctx.Args() to generate command-specific flags
|
||||
func (c Command) Run(ctx *Context) error {
|
||||
|
||||
if len(c.Subcommands) > 0 || c.Before != nil {
|
||||
return c.startApp(ctx)
|
||||
}
|
||||
|
||||
if !c.HideHelp {
|
||||
// append help to flags
|
||||
c.Flags = append(
|
||||
c.Flags,
|
||||
HelpFlag,
|
||||
)
|
||||
}
|
||||
|
||||
if ctx.App.EnableBashCompletion {
|
||||
c.Flags = append(c.Flags, BashCompletionFlag)
|
||||
}
|
||||
|
||||
set := flagSet(c.Name, c.Flags)
|
||||
set.SetOutput(ioutil.Discard)
|
||||
|
||||
firstFlagIndex := -1
|
||||
for index, arg := range ctx.Args() {
|
||||
if strings.HasPrefix(arg, "-") {
|
||||
firstFlagIndex = index
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
if firstFlagIndex > -1 && !c.SkipFlagParsing {
|
||||
args := ctx.Args()
|
||||
regularArgs := args[1:firstFlagIndex]
|
||||
flagArgs := args[firstFlagIndex:]
|
||||
err = set.Parse(append(flagArgs, regularArgs...))
|
||||
} else {
|
||||
err = set.Parse(ctx.Args().Tail())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("Incorrect Usage.\n\n")
|
||||
ShowCommandHelp(ctx, c.Name)
|
||||
fmt.Println("")
|
||||
return err
|
||||
}
|
||||
|
||||
nerr := normalizeFlags(c.Flags, set)
|
||||
if nerr != nil {
|
||||
fmt.Println(nerr)
|
||||
fmt.Println("")
|
||||
ShowCommandHelp(ctx, c.Name)
|
||||
fmt.Println("")
|
||||
return nerr
|
||||
}
|
||||
context := NewContext(ctx.App, set, ctx.globalSet)
|
||||
|
||||
if checkCommandCompletions(context, c.Name) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if checkCommandHelp(context, c.Name) {
|
||||
return nil
|
||||
}
|
||||
context.Command = c
|
||||
c.Action(context)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns true if Command.Name or Command.ShortName matches given name
|
||||
func (c Command) HasName(name string) bool {
|
||||
return c.Name == name || c.ShortName == name
|
||||
}
|
||||
|
||||
func (c Command) startApp(ctx *Context) error {
|
||||
app := NewApp()
|
||||
|
||||
// set the name and usage
|
||||
app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name)
|
||||
if c.Description != "" {
|
||||
app.Usage = c.Description
|
||||
} else {
|
||||
app.Usage = c.Usage
|
||||
}
|
||||
|
||||
// set CommandNotFound
|
||||
app.CommandNotFound = ctx.App.CommandNotFound
|
||||
|
||||
// set the flags and commands
|
||||
app.Commands = c.Subcommands
|
||||
app.Flags = c.Flags
|
||||
app.HideHelp = c.HideHelp
|
||||
|
||||
// bash completion
|
||||
app.EnableBashCompletion = ctx.App.EnableBashCompletion
|
||||
if c.BashComplete != nil {
|
||||
app.BashComplete = c.BashComplete
|
||||
}
|
||||
|
||||
// set the actions
|
||||
app.Before = c.Before
|
||||
if c.Action != nil {
|
||||
app.Action = c.Action
|
||||
} else {
|
||||
app.Action = helpSubcommand.Action
|
||||
}
|
||||
|
||||
return app.RunAsSubcommand(ctx)
|
||||
}
|
49
Godeps/_workspace/src/github.com/codegangsta/cli/command_test.go
generated
vendored
49
Godeps/_workspace/src/github.com/codegangsta/cli/command_test.go
generated
vendored
@ -1,49 +0,0 @@
|
||||
package cli_test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"testing"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
func TestCommandDoNotIgnoreFlags(t *testing.T) {
|
||||
app := cli.NewApp()
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
test := []string{"blah", "blah", "-break"}
|
||||
set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, set)
|
||||
|
||||
command := cli.Command{
|
||||
Name: "test-cmd",
|
||||
ShortName: "tc",
|
||||
Usage: "this is for testing",
|
||||
Description: "testing",
|
||||
Action: func(_ *cli.Context) {},
|
||||
}
|
||||
err := command.Run(c)
|
||||
|
||||
expect(t, err.Error(), "flag provided but not defined: -break")
|
||||
}
|
||||
|
||||
func TestCommandIgnoreFlags(t *testing.T) {
|
||||
app := cli.NewApp()
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
test := []string{"blah", "blah"}
|
||||
set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, set)
|
||||
|
||||
command := cli.Command{
|
||||
Name: "test-cmd",
|
||||
ShortName: "tc",
|
||||
Usage: "this is for testing",
|
||||
Description: "testing",
|
||||
Action: func(_ *cli.Context) {},
|
||||
SkipFlagParsing: true,
|
||||
}
|
||||
err := command.Run(c)
|
||||
|
||||
expect(t, err, nil)
|
||||
}
|
339
Godeps/_workspace/src/github.com/codegangsta/cli/context.go
generated
vendored
339
Godeps/_workspace/src/github.com/codegangsta/cli/context.go
generated
vendored
@ -1,339 +0,0 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Context is a type that is passed through to
|
||||
// each Handler action in a cli application. Context
|
||||
// can be used to retrieve context-specific Args and
|
||||
// parsed command-line options.
|
||||
type Context struct {
|
||||
App *App
|
||||
Command Command
|
||||
flagSet *flag.FlagSet
|
||||
globalSet *flag.FlagSet
|
||||
setFlags map[string]bool
|
||||
globalSetFlags map[string]bool
|
||||
}
|
||||
|
||||
// Creates a new context. For use in when invoking an App or Command action.
|
||||
func NewContext(app *App, set *flag.FlagSet, globalSet *flag.FlagSet) *Context {
|
||||
return &Context{App: app, flagSet: set, globalSet: globalSet}
|
||||
}
|
||||
|
||||
// Looks up the value of a local int flag, returns 0 if no int flag exists
|
||||
func (c *Context) Int(name string) int {
|
||||
return lookupInt(name, c.flagSet)
|
||||
}
|
||||
|
||||
// Looks up the value of a local time.Duration flag, returns 0 if no time.Duration flag exists
|
||||
func (c *Context) Duration(name string) time.Duration {
|
||||
return lookupDuration(name, c.flagSet)
|
||||
}
|
||||
|
||||
// Looks up the value of a local float64 flag, returns 0 if no float64 flag exists
|
||||
func (c *Context) Float64(name string) float64 {
|
||||
return lookupFloat64(name, c.flagSet)
|
||||
}
|
||||
|
||||
// Looks up the value of a local bool flag, returns false if no bool flag exists
|
||||
func (c *Context) Bool(name string) bool {
|
||||
return lookupBool(name, c.flagSet)
|
||||
}
|
||||
|
||||
// Looks up the value of a local boolT flag, returns false if no bool flag exists
|
||||
func (c *Context) BoolT(name string) bool {
|
||||
return lookupBoolT(name, c.flagSet)
|
||||
}
|
||||
|
||||
// Looks up the value of a local string flag, returns "" if no string flag exists
|
||||
func (c *Context) String(name string) string {
|
||||
return lookupString(name, c.flagSet)
|
||||
}
|
||||
|
||||
// Looks up the value of a local string slice flag, returns nil if no string slice flag exists
|
||||
func (c *Context) StringSlice(name string) []string {
|
||||
return lookupStringSlice(name, c.flagSet)
|
||||
}
|
||||
|
||||
// Looks up the value of a local int slice flag, returns nil if no int slice flag exists
|
||||
func (c *Context) IntSlice(name string) []int {
|
||||
return lookupIntSlice(name, c.flagSet)
|
||||
}
|
||||
|
||||
// Looks up the value of a local generic flag, returns nil if no generic flag exists
|
||||
func (c *Context) Generic(name string) interface{} {
|
||||
return lookupGeneric(name, c.flagSet)
|
||||
}
|
||||
|
||||
// Looks up the value of a global int flag, returns 0 if no int flag exists
|
||||
func (c *Context) GlobalInt(name string) int {
|
||||
return lookupInt(name, c.globalSet)
|
||||
}
|
||||
|
||||
// Looks up the value of a global time.Duration flag, returns 0 if no time.Duration flag exists
|
||||
func (c *Context) GlobalDuration(name string) time.Duration {
|
||||
return lookupDuration(name, c.globalSet)
|
||||
}
|
||||
|
||||
// Looks up the value of a global bool flag, returns false if no bool flag exists
|
||||
func (c *Context) GlobalBool(name string) bool {
|
||||
return lookupBool(name, c.globalSet)
|
||||
}
|
||||
|
||||
// Looks up the value of a global string flag, returns "" if no string flag exists
|
||||
func (c *Context) GlobalString(name string) string {
|
||||
return lookupString(name, c.globalSet)
|
||||
}
|
||||
|
||||
// Looks up the value of a global string slice flag, returns nil if no string slice flag exists
|
||||
func (c *Context) GlobalStringSlice(name string) []string {
|
||||
return lookupStringSlice(name, c.globalSet)
|
||||
}
|
||||
|
||||
// Looks up the value of a global int slice flag, returns nil if no int slice flag exists
|
||||
func (c *Context) GlobalIntSlice(name string) []int {
|
||||
return lookupIntSlice(name, c.globalSet)
|
||||
}
|
||||
|
||||
// Looks up the value of a global generic flag, returns nil if no generic flag exists
|
||||
func (c *Context) GlobalGeneric(name string) interface{} {
|
||||
return lookupGeneric(name, c.globalSet)
|
||||
}
|
||||
|
||||
// Determines if the flag was actually set
|
||||
func (c *Context) IsSet(name string) bool {
|
||||
if c.setFlags == nil {
|
||||
c.setFlags = make(map[string]bool)
|
||||
c.flagSet.Visit(func(f *flag.Flag) {
|
||||
c.setFlags[f.Name] = true
|
||||
})
|
||||
}
|
||||
return c.setFlags[name] == true
|
||||
}
|
||||
|
||||
// Determines if the global flag was actually set
|
||||
func (c *Context) GlobalIsSet(name string) bool {
|
||||
if c.globalSetFlags == nil {
|
||||
c.globalSetFlags = make(map[string]bool)
|
||||
c.globalSet.Visit(func(f *flag.Flag) {
|
||||
c.globalSetFlags[f.Name] = true
|
||||
})
|
||||
}
|
||||
return c.globalSetFlags[name] == true
|
||||
}
|
||||
|
||||
// Returns a slice of flag names used in this context.
|
||||
func (c *Context) FlagNames() (names []string) {
|
||||
for _, flag := range c.Command.Flags {
|
||||
name := strings.Split(flag.getName(), ",")[0]
|
||||
if name == "help" {
|
||||
continue
|
||||
}
|
||||
names = append(names, name)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Returns a slice of global flag names used by the app.
|
||||
func (c *Context) GlobalFlagNames() (names []string) {
|
||||
for _, flag := range c.App.Flags {
|
||||
name := strings.Split(flag.getName(), ",")[0]
|
||||
if name == "help" || name == "version" {
|
||||
continue
|
||||
}
|
||||
names = append(names, name)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type Args []string
|
||||
|
||||
// Returns the command line arguments associated with the context.
|
||||
func (c *Context) Args() Args {
|
||||
args := Args(c.flagSet.Args())
|
||||
return args
|
||||
}
|
||||
|
||||
// Returns the nth argument, or else a blank string
|
||||
func (a Args) Get(n int) string {
|
||||
if len(a) > n {
|
||||
return a[n]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Returns the first argument, or else a blank string
|
||||
func (a Args) First() string {
|
||||
return a.Get(0)
|
||||
}
|
||||
|
||||
// Return the rest of the arguments (not the first one)
|
||||
// or else an empty string slice
|
||||
func (a Args) Tail() []string {
|
||||
if len(a) >= 2 {
|
||||
return []string(a)[1:]
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// Checks if there are any arguments present
|
||||
func (a Args) Present() bool {
|
||||
return len(a) != 0
|
||||
}
|
||||
|
||||
// Swaps arguments at the given indexes
|
||||
func (a Args) Swap(from, to int) error {
|
||||
if from >= len(a) || to >= len(a) {
|
||||
return errors.New("index out of range")
|
||||
}
|
||||
a[from], a[to] = a[to], a[from]
|
||||
return nil
|
||||
}
|
||||
|
||||
func lookupInt(name string, set *flag.FlagSet) int {
|
||||
f := set.Lookup(name)
|
||||
if f != nil {
|
||||
val, err := strconv.Atoi(f.Value.String())
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func lookupDuration(name string, set *flag.FlagSet) time.Duration {
|
||||
f := set.Lookup(name)
|
||||
if f != nil {
|
||||
val, err := time.ParseDuration(f.Value.String())
|
||||
if err == nil {
|
||||
return val
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func lookupFloat64(name string, set *flag.FlagSet) float64 {
|
||||
f := set.Lookup(name)
|
||||
if f != nil {
|
||||
val, err := strconv.ParseFloat(f.Value.String(), 64)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func lookupString(name string, set *flag.FlagSet) string {
|
||||
f := set.Lookup(name)
|
||||
if f != nil {
|
||||
return f.Value.String()
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func lookupStringSlice(name string, set *flag.FlagSet) []string {
|
||||
f := set.Lookup(name)
|
||||
if f != nil {
|
||||
return (f.Value.(*StringSlice)).Value()
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func lookupIntSlice(name string, set *flag.FlagSet) []int {
|
||||
f := set.Lookup(name)
|
||||
if f != nil {
|
||||
return (f.Value.(*IntSlice)).Value()
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func lookupGeneric(name string, set *flag.FlagSet) interface{} {
|
||||
f := set.Lookup(name)
|
||||
if f != nil {
|
||||
return f.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func lookupBool(name string, set *flag.FlagSet) bool {
|
||||
f := set.Lookup(name)
|
||||
if f != nil {
|
||||
val, err := strconv.ParseBool(f.Value.String())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func lookupBoolT(name string, set *flag.FlagSet) bool {
|
||||
f := set.Lookup(name)
|
||||
if f != nil {
|
||||
val, err := strconv.ParseBool(f.Value.String())
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) {
|
||||
switch ff.Value.(type) {
|
||||
case *StringSlice:
|
||||
default:
|
||||
set.Set(name, ff.Value.String())
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeFlags(flags []Flag, set *flag.FlagSet) error {
|
||||
visited := make(map[string]bool)
|
||||
set.Visit(func(f *flag.Flag) {
|
||||
visited[f.Name] = true
|
||||
})
|
||||
for _, f := range flags {
|
||||
parts := strings.Split(f.getName(), ",")
|
||||
if len(parts) == 1 {
|
||||
continue
|
||||
}
|
||||
var ff *flag.Flag
|
||||
for _, name := range parts {
|
||||
name = strings.Trim(name, " ")
|
||||
if visited[name] {
|
||||
if ff != nil {
|
||||
return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name)
|
||||
}
|
||||
ff = set.Lookup(name)
|
||||
}
|
||||
}
|
||||
if ff == nil {
|
||||
continue
|
||||
}
|
||||
for _, name := range parts {
|
||||
name = strings.Trim(name, " ")
|
||||
if !visited[name] {
|
||||
copyFlag(name, ff, set)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
99
Godeps/_workspace/src/github.com/codegangsta/cli/context_test.go
generated
vendored
99
Godeps/_workspace/src/github.com/codegangsta/cli/context_test.go
generated
vendored
@ -1,99 +0,0 @@
|
||||
package cli_test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
func TestNewContext(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Int("myflag", 12, "doc")
|
||||
globalSet := flag.NewFlagSet("test", 0)
|
||||
globalSet.Int("myflag", 42, "doc")
|
||||
command := cli.Command{Name: "mycommand"}
|
||||
c := cli.NewContext(nil, set, globalSet)
|
||||
c.Command = command
|
||||
expect(t, c.Int("myflag"), 12)
|
||||
expect(t, c.GlobalInt("myflag"), 42)
|
||||
expect(t, c.Command.Name, "mycommand")
|
||||
}
|
||||
|
||||
func TestContext_Int(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Int("myflag", 12, "doc")
|
||||
c := cli.NewContext(nil, set, set)
|
||||
expect(t, c.Int("myflag"), 12)
|
||||
}
|
||||
|
||||
func TestContext_Duration(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Duration("myflag", time.Duration(12*time.Second), "doc")
|
||||
c := cli.NewContext(nil, set, set)
|
||||
expect(t, c.Duration("myflag"), time.Duration(12*time.Second))
|
||||
}
|
||||
|
||||
func TestContext_String(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.String("myflag", "hello world", "doc")
|
||||
c := cli.NewContext(nil, set, set)
|
||||
expect(t, c.String("myflag"), "hello world")
|
||||
}
|
||||
|
||||
func TestContext_Bool(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Bool("myflag", false, "doc")
|
||||
c := cli.NewContext(nil, set, set)
|
||||
expect(t, c.Bool("myflag"), false)
|
||||
}
|
||||
|
||||
func TestContext_BoolT(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Bool("myflag", true, "doc")
|
||||
c := cli.NewContext(nil, set, set)
|
||||
expect(t, c.BoolT("myflag"), true)
|
||||
}
|
||||
|
||||
func TestContext_Args(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Bool("myflag", false, "doc")
|
||||
c := cli.NewContext(nil, set, set)
|
||||
set.Parse([]string{"--myflag", "bat", "baz"})
|
||||
expect(t, len(c.Args()), 2)
|
||||
expect(t, c.Bool("myflag"), true)
|
||||
}
|
||||
|
||||
func TestContext_IsSet(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Bool("myflag", false, "doc")
|
||||
set.String("otherflag", "hello world", "doc")
|
||||
globalSet := flag.NewFlagSet("test", 0)
|
||||
globalSet.Bool("myflagGlobal", true, "doc")
|
||||
c := cli.NewContext(nil, set, globalSet)
|
||||
set.Parse([]string{"--myflag", "bat", "baz"})
|
||||
globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"})
|
||||
expect(t, c.IsSet("myflag"), true)
|
||||
expect(t, c.IsSet("otherflag"), false)
|
||||
expect(t, c.IsSet("bogusflag"), false)
|
||||
expect(t, c.IsSet("myflagGlobal"), false)
|
||||
}
|
||||
|
||||
func TestContext_GlobalIsSet(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Bool("myflag", false, "doc")
|
||||
set.String("otherflag", "hello world", "doc")
|
||||
globalSet := flag.NewFlagSet("test", 0)
|
||||
globalSet.Bool("myflagGlobal", true, "doc")
|
||||
globalSet.Bool("myflagGlobalUnset", true, "doc")
|
||||
c := cli.NewContext(nil, set, globalSet)
|
||||
set.Parse([]string{"--myflag", "bat", "baz"})
|
||||
globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"})
|
||||
expect(t, c.GlobalIsSet("myflag"), false)
|
||||
expect(t, c.GlobalIsSet("otherflag"), false)
|
||||
expect(t, c.GlobalIsSet("bogusflag"), false)
|
||||
expect(t, c.GlobalIsSet("myflagGlobal"), true)
|
||||
expect(t, c.GlobalIsSet("myflagGlobalUnset"), false)
|
||||
expect(t, c.GlobalIsSet("bogusGlobal"), false)
|
||||
}
|
447
Godeps/_workspace/src/github.com/codegangsta/cli/flag.go
generated
vendored
447
Godeps/_workspace/src/github.com/codegangsta/cli/flag.go
generated
vendored
@ -1,447 +0,0 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// This flag enables bash-completion for all commands and subcommands
|
||||
var BashCompletionFlag = BoolFlag{
|
||||
Name: "generate-bash-completion",
|
||||
}
|
||||
|
||||
// This flag prints the version for the application
|
||||
var VersionFlag = BoolFlag{
|
||||
Name: "version, v",
|
||||
Usage: "print the version",
|
||||
}
|
||||
|
||||
// This flag prints the help for all commands and subcommands
|
||||
var HelpFlag = BoolFlag{
|
||||
Name: "help, h",
|
||||
Usage: "show help",
|
||||
}
|
||||
|
||||
// Flag is a common interface related to parsing flags in cli.
|
||||
// For more advanced flag parsing techniques, it is recomended that
|
||||
// this interface be implemented.
|
||||
type Flag interface {
|
||||
fmt.Stringer
|
||||
// Apply Flag settings to the given flag set
|
||||
Apply(*flag.FlagSet)
|
||||
getName() string
|
||||
}
|
||||
|
||||
func flagSet(name string, flags []Flag) *flag.FlagSet {
|
||||
set := flag.NewFlagSet(name, flag.ContinueOnError)
|
||||
|
||||
for _, f := range flags {
|
||||
f.Apply(set)
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
func eachName(longName string, fn func(string)) {
|
||||
parts := strings.Split(longName, ",")
|
||||
for _, name := range parts {
|
||||
name = strings.Trim(name, " ")
|
||||
fn(name)
|
||||
}
|
||||
}
|
||||
|
||||
// Generic is a generic parseable type identified by a specific flag
|
||||
type Generic interface {
|
||||
Set(value string) error
|
||||
String() string
|
||||
}
|
||||
|
||||
// GenericFlag is the flag type for types implementing Generic
|
||||
type GenericFlag struct {
|
||||
Name string
|
||||
Value Generic
|
||||
Usage string
|
||||
EnvVar string
|
||||
}
|
||||
|
||||
func (f GenericFlag) String() string {
|
||||
return withEnvHint(f.EnvVar, fmt.Sprintf("%s%s %v\t`%v` %s", prefixFor(f.Name), f.Name, f.Value, "-"+f.Name+" option -"+f.Name+" option", f.Usage))
|
||||
}
|
||||
|
||||
func (f GenericFlag) Apply(set *flag.FlagSet) {
|
||||
val := f.Value
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
val.Set(envVal)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eachName(f.Name, func(name string) {
|
||||
set.Var(f.Value, name, f.Usage)
|
||||
})
|
||||
}
|
||||
|
||||
func (f GenericFlag) getName() string {
|
||||
return f.Name
|
||||
}
|
||||
|
||||
type StringSlice []string
|
||||
|
||||
func (f *StringSlice) Set(value string) error {
|
||||
*f = append(*f, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *StringSlice) String() string {
|
||||
return fmt.Sprintf("%s", *f)
|
||||
}
|
||||
|
||||
func (f *StringSlice) Value() []string {
|
||||
return *f
|
||||
}
|
||||
|
||||
type StringSliceFlag struct {
|
||||
Name string
|
||||
Value *StringSlice
|
||||
Usage string
|
||||
EnvVar string
|
||||
}
|
||||
|
||||
func (f StringSliceFlag) String() string {
|
||||
firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ")
|
||||
pref := prefixFor(firstName)
|
||||
return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage))
|
||||
}
|
||||
|
||||
func (f StringSliceFlag) Apply(set *flag.FlagSet) {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
newVal := &StringSlice{}
|
||||
for _, s := range strings.Split(envVal, ",") {
|
||||
s = strings.TrimSpace(s)
|
||||
newVal.Set(s)
|
||||
}
|
||||
f.Value = newVal
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eachName(f.Name, func(name string) {
|
||||
set.Var(f.Value, name, f.Usage)
|
||||
})
|
||||
}
|
||||
|
||||
func (f StringSliceFlag) getName() string {
|
||||
return f.Name
|
||||
}
|
||||
|
||||
type IntSlice []int
|
||||
|
||||
func (f *IntSlice) Set(value string) error {
|
||||
|
||||
tmp, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
*f = append(*f, tmp)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *IntSlice) String() string {
|
||||
return fmt.Sprintf("%d", *f)
|
||||
}
|
||||
|
||||
func (f *IntSlice) Value() []int {
|
||||
return *f
|
||||
}
|
||||
|
||||
type IntSliceFlag struct {
|
||||
Name string
|
||||
Value *IntSlice
|
||||
Usage string
|
||||
EnvVar string
|
||||
}
|
||||
|
||||
func (f IntSliceFlag) String() string {
|
||||
firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ")
|
||||
pref := prefixFor(firstName)
|
||||
return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage))
|
||||
}
|
||||
|
||||
func (f IntSliceFlag) Apply(set *flag.FlagSet) {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
newVal := &IntSlice{}
|
||||
for _, s := range strings.Split(envVal, ",") {
|
||||
s = strings.TrimSpace(s)
|
||||
err := newVal.Set(s)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, err.Error())
|
||||
}
|
||||
}
|
||||
f.Value = newVal
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eachName(f.Name, func(name string) {
|
||||
set.Var(f.Value, name, f.Usage)
|
||||
})
|
||||
}
|
||||
|
||||
func (f IntSliceFlag) getName() string {
|
||||
return f.Name
|
||||
}
|
||||
|
||||
type BoolFlag struct {
|
||||
Name string
|
||||
Usage string
|
||||
EnvVar string
|
||||
}
|
||||
|
||||
func (f BoolFlag) String() string {
|
||||
return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage))
|
||||
}
|
||||
|
||||
func (f BoolFlag) Apply(set *flag.FlagSet) {
|
||||
val := false
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
envValBool, err := strconv.ParseBool(envVal)
|
||||
if err == nil {
|
||||
val = envValBool
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eachName(f.Name, func(name string) {
|
||||
set.Bool(name, val, f.Usage)
|
||||
})
|
||||
}
|
||||
|
||||
func (f BoolFlag) getName() string {
|
||||
return f.Name
|
||||
}
|
||||
|
||||
type BoolTFlag struct {
|
||||
Name string
|
||||
Usage string
|
||||
EnvVar string
|
||||
}
|
||||
|
||||
func (f BoolTFlag) String() string {
|
||||
return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage))
|
||||
}
|
||||
|
||||
func (f BoolTFlag) Apply(set *flag.FlagSet) {
|
||||
val := true
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
envValBool, err := strconv.ParseBool(envVal)
|
||||
if err == nil {
|
||||
val = envValBool
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eachName(f.Name, func(name string) {
|
||||
set.Bool(name, val, f.Usage)
|
||||
})
|
||||
}
|
||||
|
||||
func (f BoolTFlag) getName() string {
|
||||
return f.Name
|
||||
}
|
||||
|
||||
type StringFlag struct {
|
||||
Name string
|
||||
Value string
|
||||
Usage string
|
||||
EnvVar string
|
||||
}
|
||||
|
||||
func (f StringFlag) String() string {
|
||||
var fmtString string
|
||||
fmtString = "%s %v\t%v"
|
||||
|
||||
if len(f.Value) > 0 {
|
||||
fmtString = "%s '%v'\t%v"
|
||||
} else {
|
||||
fmtString = "%s %v\t%v"
|
||||
}
|
||||
|
||||
return withEnvHint(f.EnvVar, fmt.Sprintf(fmtString, prefixedNames(f.Name), f.Value, f.Usage))
|
||||
}
|
||||
|
||||
func (f StringFlag) Apply(set *flag.FlagSet) {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
f.Value = envVal
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eachName(f.Name, func(name string) {
|
||||
set.String(name, f.Value, f.Usage)
|
||||
})
|
||||
}
|
||||
|
||||
func (f StringFlag) getName() string {
|
||||
return f.Name
|
||||
}
|
||||
|
||||
type IntFlag struct {
|
||||
Name string
|
||||
Value int
|
||||
Usage string
|
||||
EnvVar string
|
||||
}
|
||||
|
||||
func (f IntFlag) String() string {
|
||||
return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), f.Value, f.Usage))
|
||||
}
|
||||
|
||||
func (f IntFlag) Apply(set *flag.FlagSet) {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
envValInt, err := strconv.ParseUint(envVal, 10, 64)
|
||||
if err == nil {
|
||||
f.Value = int(envValInt)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eachName(f.Name, func(name string) {
|
||||
set.Int(name, f.Value, f.Usage)
|
||||
})
|
||||
}
|
||||
|
||||
func (f IntFlag) getName() string {
|
||||
return f.Name
|
||||
}
|
||||
|
||||
type DurationFlag struct {
|
||||
Name string
|
||||
Value time.Duration
|
||||
Usage string
|
||||
EnvVar string
|
||||
}
|
||||
|
||||
func (f DurationFlag) String() string {
|
||||
return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), f.Value, f.Usage))
|
||||
}
|
||||
|
||||
func (f DurationFlag) Apply(set *flag.FlagSet) {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
envValDuration, err := time.ParseDuration(envVal)
|
||||
if err == nil {
|
||||
f.Value = envValDuration
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eachName(f.Name, func(name string) {
|
||||
set.Duration(name, f.Value, f.Usage)
|
||||
})
|
||||
}
|
||||
|
||||
func (f DurationFlag) getName() string {
|
||||
return f.Name
|
||||
}
|
||||
|
||||
type Float64Flag struct {
|
||||
Name string
|
||||
Value float64
|
||||
Usage string
|
||||
EnvVar string
|
||||
}
|
||||
|
||||
func (f Float64Flag) String() string {
|
||||
return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), f.Value, f.Usage))
|
||||
}
|
||||
|
||||
func (f Float64Flag) Apply(set *flag.FlagSet) {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
envValFloat, err := strconv.ParseFloat(envVal, 10)
|
||||
if err == nil {
|
||||
f.Value = float64(envValFloat)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eachName(f.Name, func(name string) {
|
||||
set.Float64(name, f.Value, f.Usage)
|
||||
})
|
||||
}
|
||||
|
||||
func (f Float64Flag) getName() string {
|
||||
return f.Name
|
||||
}
|
||||
|
||||
func prefixFor(name string) (prefix string) {
|
||||
if len(name) == 1 {
|
||||
prefix = "-"
|
||||
} else {
|
||||
prefix = "--"
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func prefixedNames(fullName string) (prefixed string) {
|
||||
parts := strings.Split(fullName, ",")
|
||||
for i, name := range parts {
|
||||
name = strings.Trim(name, " ")
|
||||
prefixed += prefixFor(name) + name
|
||||
if i < len(parts)-1 {
|
||||
prefixed += ", "
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func withEnvHint(envVar, str string) string {
|
||||
envText := ""
|
||||
if envVar != "" {
|
||||
envText = fmt.Sprintf(" [$%s]", strings.Join(strings.Split(envVar, ","), ", $"))
|
||||
}
|
||||
return str + envText
|
||||
}
|
743
Godeps/_workspace/src/github.com/codegangsta/cli/flag_test.go
generated
vendored
743
Godeps/_workspace/src/github.com/codegangsta/cli/flag_test.go
generated
vendored
@ -1,743 +0,0 @@
|
||||
package cli_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
var boolFlagTests = []struct {
|
||||
name string
|
||||
expected string
|
||||
}{
|
||||
{"help", "--help\t"},
|
||||
{"h", "-h\t"},
|
||||
}
|
||||
|
||||
func TestBoolFlagHelpOutput(t *testing.T) {
|
||||
|
||||
for _, test := range boolFlagTests {
|
||||
flag := cli.BoolFlag{Name: test.name}
|
||||
output := flag.String()
|
||||
|
||||
if output != test.expected {
|
||||
t.Errorf("%s does not match %s", output, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var stringFlagTests = []struct {
|
||||
name string
|
||||
value string
|
||||
expected string
|
||||
}{
|
||||
{"help", "", "--help \t"},
|
||||
{"h", "", "-h \t"},
|
||||
{"h", "", "-h \t"},
|
||||
{"test", "Something", "--test 'Something'\t"},
|
||||
}
|
||||
|
||||
func TestStringFlagHelpOutput(t *testing.T) {
|
||||
|
||||
for _, test := range stringFlagTests {
|
||||
flag := cli.StringFlag{Name: test.name, Value: test.value}
|
||||
output := flag.String()
|
||||
|
||||
if output != test.expected {
|
||||
t.Errorf("%s does not match %s", output, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringFlagWithEnvVarHelpOutput(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_FOO", "derp")
|
||||
for _, test := range stringFlagTests {
|
||||
flag := cli.StringFlag{Name: test.name, Value: test.value, EnvVar: "APP_FOO"}
|
||||
output := flag.String()
|
||||
|
||||
if !strings.HasSuffix(output, " [$APP_FOO]") {
|
||||
t.Errorf("%s does not end with [$APP_FOO]", output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var stringSliceFlagTests = []struct {
|
||||
name string
|
||||
value *cli.StringSlice
|
||||
expected string
|
||||
}{
|
||||
{"help", func() *cli.StringSlice {
|
||||
s := &cli.StringSlice{}
|
||||
s.Set("")
|
||||
return s
|
||||
}(), "--help '--help option --help option'\t"},
|
||||
{"h", func() *cli.StringSlice {
|
||||
s := &cli.StringSlice{}
|
||||
s.Set("")
|
||||
return s
|
||||
}(), "-h '-h option -h option'\t"},
|
||||
{"h", func() *cli.StringSlice {
|
||||
s := &cli.StringSlice{}
|
||||
s.Set("")
|
||||
return s
|
||||
}(), "-h '-h option -h option'\t"},
|
||||
{"test", func() *cli.StringSlice {
|
||||
s := &cli.StringSlice{}
|
||||
s.Set("Something")
|
||||
return s
|
||||
}(), "--test '--test option --test option'\t"},
|
||||
}
|
||||
|
||||
func TestStringSliceFlagHelpOutput(t *testing.T) {
|
||||
|
||||
for _, test := range stringSliceFlagTests {
|
||||
flag := cli.StringSliceFlag{Name: test.name, Value: test.value}
|
||||
output := flag.String()
|
||||
|
||||
if output != test.expected {
|
||||
t.Errorf("%q does not match %q", output, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringSliceFlagWithEnvVarHelpOutput(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_QWWX", "11,4")
|
||||
for _, test := range stringSliceFlagTests {
|
||||
flag := cli.StringSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_QWWX"}
|
||||
output := flag.String()
|
||||
|
||||
if !strings.HasSuffix(output, " [$APP_QWWX]") {
|
||||
t.Errorf("%q does not end with [$APP_QWWX]", output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var intFlagTests = []struct {
|
||||
name string
|
||||
expected string
|
||||
}{
|
||||
{"help", "--help '0'\t"},
|
||||
{"h", "-h '0'\t"},
|
||||
}
|
||||
|
||||
func TestIntFlagHelpOutput(t *testing.T) {
|
||||
|
||||
for _, test := range intFlagTests {
|
||||
flag := cli.IntFlag{Name: test.name}
|
||||
output := flag.String()
|
||||
|
||||
if output != test.expected {
|
||||
t.Errorf("%s does not match %s", output, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntFlagWithEnvVarHelpOutput(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_BAR", "2")
|
||||
for _, test := range intFlagTests {
|
||||
flag := cli.IntFlag{Name: test.name, EnvVar: "APP_BAR"}
|
||||
output := flag.String()
|
||||
|
||||
if !strings.HasSuffix(output, " [$APP_BAR]") {
|
||||
t.Errorf("%s does not end with [$APP_BAR]", output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var durationFlagTests = []struct {
|
||||
name string
|
||||
expected string
|
||||
}{
|
||||
{"help", "--help '0'\t"},
|
||||
{"h", "-h '0'\t"},
|
||||
}
|
||||
|
||||
func TestDurationFlagHelpOutput(t *testing.T) {
|
||||
|
||||
for _, test := range durationFlagTests {
|
||||
flag := cli.DurationFlag{Name: test.name}
|
||||
output := flag.String()
|
||||
|
||||
if output != test.expected {
|
||||
t.Errorf("%s does not match %s", output, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDurationFlagWithEnvVarHelpOutput(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_BAR", "2h3m6s")
|
||||
for _, test := range durationFlagTests {
|
||||
flag := cli.DurationFlag{Name: test.name, EnvVar: "APP_BAR"}
|
||||
output := flag.String()
|
||||
|
||||
if !strings.HasSuffix(output, " [$APP_BAR]") {
|
||||
t.Errorf("%s does not end with [$APP_BAR]", output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var intSliceFlagTests = []struct {
|
||||
name string
|
||||
value *cli.IntSlice
|
||||
expected string
|
||||
}{
|
||||
{"help", &cli.IntSlice{}, "--help '--help option --help option'\t"},
|
||||
{"h", &cli.IntSlice{}, "-h '-h option -h option'\t"},
|
||||
{"h", &cli.IntSlice{}, "-h '-h option -h option'\t"},
|
||||
{"test", func() *cli.IntSlice {
|
||||
i := &cli.IntSlice{}
|
||||
i.Set("9")
|
||||
return i
|
||||
}(), "--test '--test option --test option'\t"},
|
||||
}
|
||||
|
||||
func TestIntSliceFlagHelpOutput(t *testing.T) {
|
||||
|
||||
for _, test := range intSliceFlagTests {
|
||||
flag := cli.IntSliceFlag{Name: test.name, Value: test.value}
|
||||
output := flag.String()
|
||||
|
||||
if output != test.expected {
|
||||
t.Errorf("%q does not match %q", output, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntSliceFlagWithEnvVarHelpOutput(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_SMURF", "42,3")
|
||||
for _, test := range intSliceFlagTests {
|
||||
flag := cli.IntSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_SMURF"}
|
||||
output := flag.String()
|
||||
|
||||
if !strings.HasSuffix(output, " [$APP_SMURF]") {
|
||||
t.Errorf("%q does not end with [$APP_SMURF]", output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var float64FlagTests = []struct {
|
||||
name string
|
||||
expected string
|
||||
}{
|
||||
{"help", "--help '0'\t"},
|
||||
{"h", "-h '0'\t"},
|
||||
}
|
||||
|
||||
func TestFloat64FlagHelpOutput(t *testing.T) {
|
||||
|
||||
for _, test := range float64FlagTests {
|
||||
flag := cli.Float64Flag{Name: test.name}
|
||||
output := flag.String()
|
||||
|
||||
if output != test.expected {
|
||||
t.Errorf("%s does not match %s", output, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFloat64FlagWithEnvVarHelpOutput(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_BAZ", "99.4")
|
||||
for _, test := range float64FlagTests {
|
||||
flag := cli.Float64Flag{Name: test.name, EnvVar: "APP_BAZ"}
|
||||
output := flag.String()
|
||||
|
||||
if !strings.HasSuffix(output, " [$APP_BAZ]") {
|
||||
t.Errorf("%s does not end with [$APP_BAZ]", output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var genericFlagTests = []struct {
|
||||
name string
|
||||
value cli.Generic
|
||||
expected string
|
||||
}{
|
||||
{"help", &Parser{}, "--help <nil>\t`-help option -help option` "},
|
||||
{"h", &Parser{}, "-h <nil>\t`-h option -h option` "},
|
||||
{"test", &Parser{}, "--test <nil>\t`-test option -test option` "},
|
||||
}
|
||||
|
||||
func TestGenericFlagHelpOutput(t *testing.T) {
|
||||
|
||||
for _, test := range genericFlagTests {
|
||||
flag := cli.GenericFlag{Name: test.name}
|
||||
output := flag.String()
|
||||
|
||||
if output != test.expected {
|
||||
t.Errorf("%q does not match %q", output, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenericFlagWithEnvVarHelpOutput(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_ZAP", "3")
|
||||
for _, test := range genericFlagTests {
|
||||
flag := cli.GenericFlag{Name: test.name, EnvVar: "APP_ZAP"}
|
||||
output := flag.String()
|
||||
|
||||
if !strings.HasSuffix(output, " [$APP_ZAP]") {
|
||||
t.Errorf("%s does not end with [$APP_ZAP]", output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseMultiString(t *testing.T) {
|
||||
(&cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{Name: "serve, s"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.String("serve") != "10" {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.String("s") != "10" {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}).Run([]string{"run", "-s", "10"})
|
||||
}
|
||||
|
||||
func TestParseMultiStringFromEnv(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_COUNT", "20")
|
||||
(&cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{Name: "count, c", EnvVar: "APP_COUNT"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.String("count") != "20" {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.String("c") != "20" {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}).Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiStringFromEnvCascade(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_COUNT", "20")
|
||||
(&cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{Name: "count, c", EnvVar: "COMPAT_COUNT,APP_COUNT"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.String("count") != "20" {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.String("c") != "20" {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}).Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiStringSlice(t *testing.T) {
|
||||
(&cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.StringSliceFlag{Name: "serve, s", Value: &cli.StringSlice{}},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if !reflect.DeepEqual(ctx.StringSlice("serve"), []string{"10", "20"}) {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if !reflect.DeepEqual(ctx.StringSlice("s"), []string{"10", "20"}) {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}).Run([]string{"run", "-s", "10", "-s", "20"})
|
||||
}
|
||||
|
||||
func TestParseMultiStringSliceFromEnv(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_INTERVALS", "20,30,40")
|
||||
|
||||
(&cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.StringSliceFlag{Name: "intervals, i", Value: &cli.StringSlice{}, EnvVar: "APP_INTERVALS"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) {
|
||||
t.Errorf("main name not set from env")
|
||||
}
|
||||
if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) {
|
||||
t.Errorf("short name not set from env")
|
||||
}
|
||||
},
|
||||
}).Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiStringSliceFromEnvCascade(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_INTERVALS", "20,30,40")
|
||||
|
||||
(&cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.StringSliceFlag{Name: "intervals, i", Value: &cli.StringSlice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) {
|
||||
t.Errorf("main name not set from env")
|
||||
}
|
||||
if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) {
|
||||
t.Errorf("short name not set from env")
|
||||
}
|
||||
},
|
||||
}).Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiInt(t *testing.T) {
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.IntFlag{Name: "serve, s"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.Int("serve") != 10 {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.Int("s") != 10 {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run", "-s", "10"})
|
||||
}
|
||||
|
||||
func TestParseMultiIntFromEnv(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_TIMEOUT_SECONDS", "10")
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.IntFlag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.Int("timeout") != 10 {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.Int("t") != 10 {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiIntFromEnvCascade(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_TIMEOUT_SECONDS", "10")
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.IntFlag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.Int("timeout") != 10 {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.Int("t") != 10 {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiIntSlice(t *testing.T) {
|
||||
(&cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.IntSliceFlag{Name: "serve, s", Value: &cli.IntSlice{}},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{10, 20}) {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if !reflect.DeepEqual(ctx.IntSlice("s"), []int{10, 20}) {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}).Run([]string{"run", "-s", "10", "-s", "20"})
|
||||
}
|
||||
|
||||
func TestParseMultiIntSliceFromEnv(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_INTERVALS", "20,30,40")
|
||||
|
||||
(&cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.IntSliceFlag{Name: "intervals, i", Value: &cli.IntSlice{}, EnvVar: "APP_INTERVALS"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) {
|
||||
t.Errorf("main name not set from env")
|
||||
}
|
||||
if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) {
|
||||
t.Errorf("short name not set from env")
|
||||
}
|
||||
},
|
||||
}).Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiIntSliceFromEnvCascade(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_INTERVALS", "20,30,40")
|
||||
|
||||
(&cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.IntSliceFlag{Name: "intervals, i", Value: &cli.IntSlice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) {
|
||||
t.Errorf("main name not set from env")
|
||||
}
|
||||
if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) {
|
||||
t.Errorf("short name not set from env")
|
||||
}
|
||||
},
|
||||
}).Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiFloat64(t *testing.T) {
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.Float64Flag{Name: "serve, s"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.Float64("serve") != 10.2 {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.Float64("s") != 10.2 {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run", "-s", "10.2"})
|
||||
}
|
||||
|
||||
func TestParseMultiFloat64FromEnv(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_TIMEOUT_SECONDS", "15.5")
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.Float64("timeout") != 15.5 {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.Float64("t") != 15.5 {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiFloat64FromEnvCascade(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_TIMEOUT_SECONDS", "15.5")
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.Float64Flag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.Float64("timeout") != 15.5 {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.Float64("t") != 15.5 {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiBool(t *testing.T) {
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{Name: "serve, s"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.Bool("serve") != true {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.Bool("s") != true {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run", "--serve"})
|
||||
}
|
||||
|
||||
func TestParseMultiBoolFromEnv(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_DEBUG", "1")
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{Name: "debug, d", EnvVar: "APP_DEBUG"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.Bool("debug") != true {
|
||||
t.Errorf("main name not set from env")
|
||||
}
|
||||
if ctx.Bool("d") != true {
|
||||
t.Errorf("short name not set from env")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiBoolFromEnvCascade(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_DEBUG", "1")
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.Bool("debug") != true {
|
||||
t.Errorf("main name not set from env")
|
||||
}
|
||||
if ctx.Bool("d") != true {
|
||||
t.Errorf("short name not set from env")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiBoolT(t *testing.T) {
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolTFlag{Name: "serve, s"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.BoolT("serve") != true {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.BoolT("s") != true {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run", "--serve"})
|
||||
}
|
||||
|
||||
func TestParseMultiBoolTFromEnv(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_DEBUG", "0")
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolTFlag{Name: "debug, d", EnvVar: "APP_DEBUG"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.BoolT("debug") != false {
|
||||
t.Errorf("main name not set from env")
|
||||
}
|
||||
if ctx.BoolT("d") != false {
|
||||
t.Errorf("short name not set from env")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseMultiBoolTFromEnvCascade(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_DEBUG", "0")
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolTFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if ctx.BoolT("debug") != false {
|
||||
t.Errorf("main name not set from env")
|
||||
}
|
||||
if ctx.BoolT("d") != false {
|
||||
t.Errorf("short name not set from env")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
|
||||
type Parser [2]string
|
||||
|
||||
func (p *Parser) Set(value string) error {
|
||||
parts := strings.Split(value, ",")
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("invalid format")
|
||||
}
|
||||
|
||||
(*p)[0] = parts[0]
|
||||
(*p)[1] = parts[1]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Parser) String() string {
|
||||
return fmt.Sprintf("%s,%s", p[0], p[1])
|
||||
}
|
||||
|
||||
func TestParseGeneric(t *testing.T) {
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.GenericFlag{Name: "serve, s", Value: &Parser{}},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"10", "20"}) {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"10", "20"}) {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run", "-s", "10,20"})
|
||||
}
|
||||
|
||||
func TestParseGenericFromEnv(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_SERVE", "20,30")
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.GenericFlag{Name: "serve, s", Value: &Parser{}, EnvVar: "APP_SERVE"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"20", "30"}) {
|
||||
t.Errorf("main name not set from env")
|
||||
}
|
||||
if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"20", "30"}) {
|
||||
t.Errorf("short name not set from env")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
||||
|
||||
func TestParseGenericFromEnvCascade(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("APP_FOO", "99,2000")
|
||||
a := cli.App{
|
||||
Flags: []cli.Flag{
|
||||
cli.GenericFlag{Name: "foos", Value: &Parser{}, EnvVar: "COMPAT_FOO,APP_FOO"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) {
|
||||
if !reflect.DeepEqual(ctx.Generic("foos"), &Parser{"99", "2000"}) {
|
||||
t.Errorf("value not set from env")
|
||||
}
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run"})
|
||||
}
|
224
Godeps/_workspace/src/github.com/codegangsta/cli/help.go
generated
vendored
224
Godeps/_workspace/src/github.com/codegangsta/cli/help.go
generated
vendored
@ -1,224 +0,0 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"text/tabwriter"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
// The text template for the Default help topic.
|
||||
// cli.go uses text/template to render templates. You can
|
||||
// render custom help text by setting this variable.
|
||||
var AppHelpTemplate = `NAME:
|
||||
{{.Name}} - {{.Usage}}
|
||||
|
||||
USAGE:
|
||||
{{.Name}} {{if .Flags}}[global options] {{end}}command{{if .Flags}} [command options]{{end}} [arguments...]
|
||||
|
||||
VERSION:
|
||||
{{.Version}}{{if or .Author .Email}}
|
||||
|
||||
AUTHOR:{{if .Author}}
|
||||
{{.Author}}{{if .Email}} - <{{.Email}}>{{end}}{{else}}
|
||||
{{.Email}}{{end}}{{end}}
|
||||
|
||||
COMMANDS:
|
||||
{{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
|
||||
{{end}}{{if .Flags}}
|
||||
GLOBAL OPTIONS:
|
||||
{{range .Flags}}{{.}}
|
||||
{{end}}{{end}}
|
||||
`
|
||||
|
||||
// The text template for the command help topic.
|
||||
// cli.go uses text/template to render templates. You can
|
||||
// render custom help text by setting this variable.
|
||||
var CommandHelpTemplate = `NAME:
|
||||
{{.Name}} - {{.Usage}}
|
||||
|
||||
USAGE:
|
||||
command {{.Name}}{{if .Flags}} [command options]{{end}} [arguments...]{{if .Description}}
|
||||
|
||||
DESCRIPTION:
|
||||
{{.Description}}{{end}}{{if .Flags}}
|
||||
|
||||
OPTIONS:
|
||||
{{range .Flags}}{{.}}
|
||||
{{end}}{{ end }}
|
||||
`
|
||||
|
||||
// The text template for the subcommand help topic.
|
||||
// cli.go uses text/template to render templates. You can
|
||||
// render custom help text by setting this variable.
|
||||
var SubcommandHelpTemplate = `NAME:
|
||||
{{.Name}} - {{.Usage}}
|
||||
|
||||
USAGE:
|
||||
{{.Name}} command{{if .Flags}} [command options]{{end}} [arguments...]
|
||||
|
||||
COMMANDS:
|
||||
{{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
|
||||
{{end}}{{if .Flags}}
|
||||
OPTIONS:
|
||||
{{range .Flags}}{{.}}
|
||||
{{end}}{{end}}
|
||||
`
|
||||
|
||||
var helpCommand = Command{
|
||||
Name: "help",
|
||||
ShortName: "h",
|
||||
Usage: "Shows a list of commands or help for one command",
|
||||
Action: func(c *Context) {
|
||||
args := c.Args()
|
||||
if args.Present() {
|
||||
ShowCommandHelp(c, args.First())
|
||||
} else {
|
||||
ShowAppHelp(c)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
var helpSubcommand = Command{
|
||||
Name: "help",
|
||||
ShortName: "h",
|
||||
Usage: "Shows a list of commands or help for one command",
|
||||
Action: func(c *Context) {
|
||||
args := c.Args()
|
||||
if args.Present() {
|
||||
ShowCommandHelp(c, args.First())
|
||||
} else {
|
||||
ShowSubcommandHelp(c)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// Prints help for the App
|
||||
var HelpPrinter = printHelp
|
||||
|
||||
// Prints version for the App
|
||||
var VersionPrinter = printVersion
|
||||
|
||||
func ShowAppHelp(c *Context) {
|
||||
HelpPrinter(AppHelpTemplate, c.App)
|
||||
}
|
||||
|
||||
// Prints the list of subcommands as the default app completion method
|
||||
func DefaultAppComplete(c *Context) {
|
||||
for _, command := range c.App.Commands {
|
||||
fmt.Println(command.Name)
|
||||
if command.ShortName != "" {
|
||||
fmt.Println(command.ShortName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prints help for the given command
|
||||
func ShowCommandHelp(c *Context, command string) {
|
||||
for _, c := range c.App.Commands {
|
||||
if c.HasName(command) {
|
||||
HelpPrinter(CommandHelpTemplate, c)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if c.App.CommandNotFound != nil {
|
||||
c.App.CommandNotFound(c, command)
|
||||
} else {
|
||||
fmt.Printf("No help topic for '%v'\n", command)
|
||||
}
|
||||
}
|
||||
|
||||
// Prints help for the given subcommand
|
||||
func ShowSubcommandHelp(c *Context) {
|
||||
HelpPrinter(SubcommandHelpTemplate, c.App)
|
||||
}
|
||||
|
||||
// Prints the version number of the App
|
||||
func ShowVersion(c *Context) {
|
||||
VersionPrinter(c)
|
||||
}
|
||||
|
||||
func printVersion(c *Context) {
|
||||
fmt.Printf("%v version %v\n", c.App.Name, c.App.Version)
|
||||
}
|
||||
|
||||
// Prints the lists of commands within a given context
|
||||
func ShowCompletions(c *Context) {
|
||||
a := c.App
|
||||
if a != nil && a.BashComplete != nil {
|
||||
a.BashComplete(c)
|
||||
}
|
||||
}
|
||||
|
||||
// Prints the custom completions for a given command
|
||||
func ShowCommandCompletions(ctx *Context, command string) {
|
||||
c := ctx.App.Command(command)
|
||||
if c != nil && c.BashComplete != nil {
|
||||
c.BashComplete(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func printHelp(templ string, data interface{}) {
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
|
||||
t := template.Must(template.New("help").Parse(templ))
|
||||
err := t.Execute(w, data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
w.Flush()
|
||||
}
|
||||
|
||||
func checkVersion(c *Context) bool {
|
||||
if c.GlobalBool("version") {
|
||||
ShowVersion(c)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func checkHelp(c *Context) bool {
|
||||
if c.GlobalBool("h") || c.GlobalBool("help") {
|
||||
ShowAppHelp(c)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func checkCommandHelp(c *Context, name string) bool {
|
||||
if c.Bool("h") || c.Bool("help") {
|
||||
ShowCommandHelp(c, name)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func checkSubcommandHelp(c *Context) bool {
|
||||
if c.GlobalBool("h") || c.GlobalBool("help") {
|
||||
ShowSubcommandHelp(c)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func checkCompletions(c *Context) bool {
|
||||
if (c.GlobalBool(BashCompletionFlag.Name) || c.Bool(BashCompletionFlag.Name)) && c.App.EnableBashCompletion {
|
||||
ShowCompletions(c)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func checkCommandCompletions(c *Context, name string) bool {
|
||||
if c.Bool(BashCompletionFlag.Name) && c.App.EnableBashCompletion {
|
||||
ShowCommandCompletions(c, name)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
19
Godeps/_workspace/src/github.com/codegangsta/cli/helpers_test.go
generated
vendored
19
Godeps/_workspace/src/github.com/codegangsta/cli/helpers_test.go
generated
vendored
@ -1,19 +0,0 @@
|
||||
package cli_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
/* Test Helpers */
|
||||
func expect(t *testing.T, a interface{}, b interface{}) {
|
||||
if a != b {
|
||||
t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
|
||||
}
|
||||
}
|
||||
|
||||
func refute(t *testing.T, a interface{}, b interface{}) {
|
||||
if a == b {
|
||||
t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
|
||||
}
|
||||
}
|
230
Godeps/_workspace/src/github.com/minio-io/go-patricia/patricia/children.go
generated
vendored
230
Godeps/_workspace/src/github.com/minio-io/go-patricia/patricia/children.go
generated
vendored
@ -1,230 +0,0 @@
|
||||
// Copyright (c) 2014 The go-patricia AUTHORS
|
||||
//
|
||||
// Use of this source code is governed by The MIT License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package patricia
|
||||
|
||||
// Max prefix length that is kept in a single trie node.
|
||||
var MaxPrefixPerNode = 10
|
||||
|
||||
// Max children to keep in a node in the sparse mode.
|
||||
const MaxChildrenPerSparseNode = 8
|
||||
|
||||
type ChildList interface {
|
||||
length() int
|
||||
head() *Trie
|
||||
add(child *Trie) ChildList
|
||||
replace(b byte, child *Trie)
|
||||
remove(child *Trie)
|
||||
next(b byte) *Trie
|
||||
walk(prefix *Prefix, visitor VisitorFunc) error
|
||||
}
|
||||
|
||||
type SparseChildList struct {
|
||||
Children []*Trie
|
||||
}
|
||||
|
||||
func newSparseChildList() ChildList {
|
||||
return &SparseChildList{
|
||||
Children: make([]*Trie, 0, MaxChildrenPerSparseNode),
|
||||
}
|
||||
}
|
||||
|
||||
func (list *SparseChildList) length() int {
|
||||
return len(list.Children)
|
||||
}
|
||||
|
||||
func (list *SparseChildList) head() *Trie {
|
||||
return list.Children[0]
|
||||
}
|
||||
|
||||
func (list *SparseChildList) add(child *Trie) ChildList {
|
||||
// Search for an empty spot and insert the child if possible.
|
||||
if len(list.Children) != cap(list.Children) {
|
||||
list.Children = append(list.Children, child)
|
||||
return list
|
||||
}
|
||||
|
||||
// Otherwise we have to transform to the dense list type.
|
||||
return newDenseChildList(list, child)
|
||||
}
|
||||
|
||||
func (list *SparseChildList) replace(b byte, child *Trie) {
|
||||
// Seek the child and replace it.
|
||||
for i, node := range list.Children {
|
||||
if node.InternalPrefix[0] == b {
|
||||
list.Children[i] = child
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (list *SparseChildList) remove(child *Trie) {
|
||||
for i, node := range list.Children {
|
||||
if node.InternalPrefix[0] == child.InternalPrefix[0] {
|
||||
list.Children = append(list.Children[:i], list.Children[i+1:]...)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// This is not supposed to be reached.
|
||||
panic("removing non-existent child")
|
||||
}
|
||||
|
||||
func (list *SparseChildList) next(b byte) *Trie {
|
||||
for _, child := range list.Children {
|
||||
if child.InternalPrefix[0] == b {
|
||||
return child
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (list *SparseChildList) walk(prefix *Prefix, visitor VisitorFunc) error {
|
||||
for _, child := range list.Children {
|
||||
*prefix = append(*prefix, child.InternalPrefix...)
|
||||
if child.InternalItem != nil {
|
||||
err := visitor(*prefix, child.InternalItem)
|
||||
if err != nil {
|
||||
if err == SkipSubtree {
|
||||
*prefix = (*prefix)[:len(*prefix)-len(child.InternalPrefix)]
|
||||
continue
|
||||
}
|
||||
*prefix = (*prefix)[:len(*prefix)-len(child.InternalPrefix)]
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err := child.InternalChildren.walk(prefix, visitor)
|
||||
*prefix = (*prefix)[:len(*prefix)-len(child.InternalPrefix)]
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type DenseChildList struct {
|
||||
Min int
|
||||
Max int
|
||||
Children []*Trie
|
||||
}
|
||||
|
||||
func newDenseChildList(list *SparseChildList, child *Trie) ChildList {
|
||||
var (
|
||||
min int = 255
|
||||
max int = 0
|
||||
)
|
||||
for _, child := range list.Children {
|
||||
b := int(child.InternalPrefix[0])
|
||||
if b < min {
|
||||
min = b
|
||||
}
|
||||
if b > max {
|
||||
max = b
|
||||
}
|
||||
}
|
||||
|
||||
b := int(child.InternalPrefix[0])
|
||||
if b < min {
|
||||
min = b
|
||||
}
|
||||
if b > max {
|
||||
max = b
|
||||
}
|
||||
|
||||
children := make([]*Trie, max-min+1)
|
||||
for _, child := range list.Children {
|
||||
children[int(child.InternalPrefix[0])-min] = child
|
||||
}
|
||||
children[int(child.InternalPrefix[0])-min] = child
|
||||
|
||||
return &DenseChildList{min, max, children}
|
||||
}
|
||||
|
||||
func (list *DenseChildList) length() int {
|
||||
return list.Max - list.Min + 1
|
||||
}
|
||||
|
||||
func (list *DenseChildList) head() *Trie {
|
||||
return list.Children[0]
|
||||
}
|
||||
|
||||
func (list *DenseChildList) add(child *Trie) ChildList {
|
||||
b := int(child.InternalPrefix[0])
|
||||
|
||||
switch {
|
||||
case list.Min <= b && b <= list.Max:
|
||||
if list.Children[b-list.Min] != nil {
|
||||
panic("dense child list collision detected")
|
||||
}
|
||||
list.Children[b-list.Min] = child
|
||||
|
||||
case b < list.Min:
|
||||
children := make([]*Trie, list.Max-b+1)
|
||||
children[0] = child
|
||||
copy(children[list.Min-b:], list.Children)
|
||||
list.Children = children
|
||||
list.Min = b
|
||||
|
||||
default: // b > list.max
|
||||
children := make([]*Trie, b-list.Min+1)
|
||||
children[b-list.Min] = child
|
||||
copy(children, list.Children)
|
||||
list.Children = children
|
||||
list.Max = b
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
func (list *DenseChildList) replace(b byte, child *Trie) {
|
||||
list.Children[int(b)-list.Min] = nil
|
||||
list.Children[int(child.InternalPrefix[0])-list.Min] = child
|
||||
}
|
||||
|
||||
func (list *DenseChildList) remove(child *Trie) {
|
||||
i := int(child.InternalPrefix[0]) - list.Min
|
||||
if list.Children[i] == nil {
|
||||
// This is not supposed to be reached.
|
||||
panic("removing non-existent child")
|
||||
}
|
||||
list.Children[i] = nil
|
||||
}
|
||||
|
||||
func (list *DenseChildList) next(b byte) *Trie {
|
||||
i := int(b)
|
||||
if i < list.Min || list.Max < i {
|
||||
return nil
|
||||
}
|
||||
return list.Children[i-list.Min]
|
||||
}
|
||||
|
||||
func (list *DenseChildList) walk(prefix *Prefix, visitor VisitorFunc) error {
|
||||
for _, child := range list.Children {
|
||||
if child == nil {
|
||||
continue
|
||||
}
|
||||
*prefix = append(*prefix, child.InternalPrefix...)
|
||||
if child.InternalItem != nil {
|
||||
if err := visitor(*prefix, child.InternalItem); err != nil {
|
||||
if err == SkipSubtree {
|
||||
*prefix = (*prefix)[:len(*prefix)-len(child.InternalPrefix)]
|
||||
continue
|
||||
}
|
||||
*prefix = (*prefix)[:len(*prefix)-len(child.InternalPrefix)]
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err := child.InternalChildren.walk(prefix, visitor)
|
||||
*prefix = (*prefix)[:len(*prefix)-len(child.InternalPrefix)]
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
432
Godeps/_workspace/src/github.com/minio-io/go-patricia/patricia/patricia.go
generated
vendored
432
Godeps/_workspace/src/github.com/minio-io/go-patricia/patricia/patricia.go
generated
vendored
@ -1,432 +0,0 @@
|
||||
// Copyright (c) 2014 The go-patricia AUTHORS
|
||||
//
|
||||
// Use of this source code is governed by The MIT License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package patricia
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Trie
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type (
|
||||
Prefix []byte
|
||||
Item interface{}
|
||||
VisitorFunc func(prefix Prefix, item Item) error
|
||||
)
|
||||
|
||||
// Trie is a generic patricia trie that allows fast retrieval of items by prefix.
|
||||
// and other funky stuff.
|
||||
//
|
||||
// Trie is not thread-safe.
|
||||
type Trie struct {
|
||||
InternalPrefix Prefix
|
||||
InternalItem Item
|
||||
|
||||
InternalChildren ChildList
|
||||
}
|
||||
|
||||
// Public API ------------------------------------------------------------------
|
||||
|
||||
// Trie constructor.
|
||||
func NewTrie() *Trie {
|
||||
return &Trie{
|
||||
InternalChildren: newSparseChildList(),
|
||||
}
|
||||
}
|
||||
|
||||
// Item returns the item stored in the root of this trie.
|
||||
func (trie *Trie) Item() Item {
|
||||
return trie.InternalItem
|
||||
}
|
||||
|
||||
// Insert inserts a new item into the trie using the given prefix. Insert does
|
||||
// not replace existing items. It returns false if an item was already in place.
|
||||
func (trie *Trie) Insert(key Prefix, item Item) (inserted bool) {
|
||||
return trie.put(key, item, false)
|
||||
}
|
||||
|
||||
// Set works much like Insert, but it always sets the item, possibly replacing
|
||||
// the item previously inserted.
|
||||
func (trie *Trie) Set(key Prefix, item Item) {
|
||||
trie.put(key, item, true)
|
||||
}
|
||||
|
||||
// Get returns the item located at key.
|
||||
//
|
||||
// This method is a bit dangerous, because Get can as well end up in an internal
|
||||
// node that is not really representing any user-defined value. So when nil is
|
||||
// a valid value being used, it is not possible to tell if the value was inserted
|
||||
// into the tree by the user or not. A possible workaround for this is not to use
|
||||
// nil interface as a valid value, even using zero value of any type is enough
|
||||
// to prevent this bad behaviour.
|
||||
func (trie *Trie) Get(key Prefix) (item Item) {
|
||||
_, node, found, leftover := trie.findSubtree(key)
|
||||
if !found || len(leftover) != 0 {
|
||||
return nil
|
||||
}
|
||||
return node.InternalItem
|
||||
}
|
||||
|
||||
// Match returns what Get(prefix) != nil would return. The same warning as for
|
||||
// Get applies here as well.
|
||||
func (trie *Trie) Match(prefix Prefix) (matchedExactly bool) {
|
||||
return trie.Get(prefix) != nil
|
||||
}
|
||||
|
||||
// MatchSubtree returns true when there is a subtree representing extensions
|
||||
// to key, that is if there are any keys in the tree which have key as prefix.
|
||||
func (trie *Trie) MatchSubtree(key Prefix) (matched bool) {
|
||||
_, _, matched, _ = trie.findSubtree(key)
|
||||
return
|
||||
}
|
||||
|
||||
// Visit calls visitor on every node containing a non-nil item.
|
||||
//
|
||||
// If an error is returned from visitor, the function stops visiting the tree
|
||||
// and returns that error, unless it is a special error - SkipSubtree. In that
|
||||
// case Visit skips the subtree represented by the current node and continues
|
||||
// elsewhere.
|
||||
func (trie *Trie) Visit(visitor VisitorFunc) error {
|
||||
return trie.walk(nil, visitor)
|
||||
}
|
||||
|
||||
// VisitSubtree works much like Visit, but it only visits nodes matching prefix.
|
||||
func (trie *Trie) VisitSubtree(prefix Prefix, visitor VisitorFunc) error {
|
||||
// Nil prefix not allowed.
|
||||
if prefix == nil {
|
||||
panic(ErrNilPrefix)
|
||||
}
|
||||
|
||||
// Empty trie must be handled explicitly.
|
||||
if trie.InternalPrefix == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Locate the relevant subtree.
|
||||
_, root, found, leftover := trie.findSubtree(prefix)
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
prefix = append(prefix, leftover...)
|
||||
|
||||
// Visit it.
|
||||
return root.walk(prefix, visitor)
|
||||
}
|
||||
|
||||
// VisitPrefixes visits only nodes that represent prefixes of key.
|
||||
// To say the obvious, returning SkipSubtree from visitor makes no sense here.
|
||||
func (trie *Trie) VisitPrefixes(key Prefix, visitor VisitorFunc) error {
|
||||
// Nil key not allowed.
|
||||
if key == nil {
|
||||
panic(ErrNilPrefix)
|
||||
}
|
||||
|
||||
// Empty trie must be handled explicitly.
|
||||
if trie.InternalPrefix == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Walk the path matching key prefixes.
|
||||
node := trie
|
||||
prefix := key
|
||||
offset := 0
|
||||
for {
|
||||
// Compute what part of prefix matches.
|
||||
common := node.longestCommonPrefixLength(key)
|
||||
key = key[common:]
|
||||
offset += common
|
||||
|
||||
// Partial match means that there is no subtree matching prefix.
|
||||
if common < len(node.InternalPrefix) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Call the visitor.
|
||||
if item := node.InternalItem; item != nil {
|
||||
if err := visitor(prefix[:offset], item); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(key) == 0 {
|
||||
// This node represents key, we are finished.
|
||||
return nil
|
||||
}
|
||||
|
||||
// There is some key suffix left, move to the children.
|
||||
child := node.InternalChildren.next(key[0])
|
||||
if child == nil {
|
||||
// There is nowhere to continue, return.
|
||||
return nil
|
||||
}
|
||||
|
||||
node = child
|
||||
}
|
||||
}
|
||||
|
||||
// Delete deletes the item represented by the given prefix.
|
||||
//
|
||||
// True is returned if the matching node was found and deleted.
|
||||
func (trie *Trie) Delete(key Prefix) (deleted bool) {
|
||||
// Nil prefix not allowed.
|
||||
if key == nil {
|
||||
panic(ErrNilPrefix)
|
||||
}
|
||||
|
||||
// Empty trie must be handled explicitly.
|
||||
if trie.InternalPrefix == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Find the relevant node.
|
||||
parent, node, _, leftover := trie.findSubtree(key)
|
||||
if len(leftover) != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// If the item is already set to nil, there is nothing to do.
|
||||
if node.InternalItem == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Delete the item.
|
||||
node.InternalItem = nil
|
||||
|
||||
// Compact since that might be possible now.
|
||||
if compacted := node.compact(); compacted != node {
|
||||
if parent == nil {
|
||||
*node = *compacted
|
||||
} else {
|
||||
parent.InternalChildren.replace(node.InternalPrefix[0], compacted)
|
||||
*parent = *parent.compact()
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// DeleteSubtree finds the subtree exactly matching prefix and deletes it.
|
||||
//
|
||||
// True is returned if the subtree was found and deleted.
|
||||
func (trie *Trie) DeleteSubtree(prefix Prefix) (deleted bool) {
|
||||
// Nil prefix not allowed.
|
||||
if prefix == nil {
|
||||
panic(ErrNilPrefix)
|
||||
}
|
||||
|
||||
// Empty trie must be handled explicitly.
|
||||
if trie.InternalPrefix == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Locate the relevant subtree.
|
||||
parent, root, found, _ := trie.findSubtree(prefix)
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
|
||||
// If we are in the root of the trie, reset the trie.
|
||||
if parent == nil {
|
||||
root.InternalPrefix = nil
|
||||
root.InternalChildren = newSparseChildList()
|
||||
return true
|
||||
}
|
||||
|
||||
// Otherwise remove the root node from its parent.
|
||||
parent.InternalChildren.remove(root)
|
||||
return true
|
||||
}
|
||||
|
||||
// Internal helper methods -----------------------------------------------------
|
||||
|
||||
func (trie *Trie) put(key Prefix, item Item, replace bool) (inserted bool) {
|
||||
// Nil prefix not allowed.
|
||||
if key == nil {
|
||||
panic(ErrNilPrefix)
|
||||
}
|
||||
|
||||
var (
|
||||
common int
|
||||
node *Trie = trie
|
||||
child *Trie
|
||||
)
|
||||
|
||||
if node.InternalPrefix == nil {
|
||||
if len(key) <= MaxPrefixPerNode {
|
||||
node.InternalPrefix = key
|
||||
goto InsertItem
|
||||
}
|
||||
node.InternalPrefix = key[:MaxPrefixPerNode]
|
||||
key = key[MaxPrefixPerNode:]
|
||||
goto AppendChild
|
||||
}
|
||||
|
||||
for {
|
||||
// Compute the longest common prefix length.
|
||||
common = node.longestCommonPrefixLength(key)
|
||||
key = key[common:]
|
||||
|
||||
// Only a part matches, split.
|
||||
if common < len(node.InternalPrefix) {
|
||||
goto SplitPrefix
|
||||
}
|
||||
|
||||
// common == len(node.prefix) since never (common > len(node.prefix))
|
||||
// common == len(former key) <-> 0 == len(key)
|
||||
// -> former key == node.prefix
|
||||
if len(key) == 0 {
|
||||
goto InsertItem
|
||||
}
|
||||
|
||||
// Check children for matching prefix.
|
||||
child = node.InternalChildren.next(key[0])
|
||||
if child == nil {
|
||||
goto AppendChild
|
||||
}
|
||||
node = child
|
||||
}
|
||||
|
||||
SplitPrefix:
|
||||
// Split the prefix if necessary.
|
||||
child = new(Trie)
|
||||
*child = *node
|
||||
*node = *NewTrie()
|
||||
node.InternalPrefix = child.InternalPrefix[:common]
|
||||
child.InternalPrefix = child.InternalPrefix[common:]
|
||||
child = child.compact()
|
||||
node.InternalChildren = node.InternalChildren.add(child)
|
||||
|
||||
AppendChild:
|
||||
// Keep appending children until whole prefix is inserted.
|
||||
// This loop starts with empty node.prefix that needs to be filled.
|
||||
for len(key) != 0 {
|
||||
child := NewTrie()
|
||||
if len(key) <= MaxPrefixPerNode {
|
||||
child.InternalPrefix = key
|
||||
node.InternalChildren = node.InternalChildren.add(child)
|
||||
node = child
|
||||
goto InsertItem
|
||||
} else {
|
||||
child.InternalPrefix = key[:MaxPrefixPerNode]
|
||||
key = key[MaxPrefixPerNode:]
|
||||
node.InternalChildren = node.InternalChildren.add(child)
|
||||
node = child
|
||||
}
|
||||
}
|
||||
|
||||
InsertItem:
|
||||
// Try to insert the item if possible.
|
||||
if replace || node.InternalItem == nil {
|
||||
node.InternalItem = item
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (trie *Trie) compact() *Trie {
|
||||
// Only a node with a single child can be compacted.
|
||||
if trie.InternalChildren.length() != 1 {
|
||||
return trie
|
||||
}
|
||||
|
||||
child := trie.InternalChildren.head()
|
||||
|
||||
// If any item is set, we cannot compact since we want to retain
|
||||
// the ability to do searching by key. This makes compaction less usable,
|
||||
// but that simply cannot be avoided.
|
||||
if trie.InternalItem != nil || child.InternalItem != nil {
|
||||
return trie
|
||||
}
|
||||
|
||||
// Make sure the combined prefixes fit into a single node.
|
||||
if len(trie.InternalPrefix)+len(child.InternalPrefix) > MaxPrefixPerNode {
|
||||
return trie
|
||||
}
|
||||
|
||||
// Concatenate the prefixes, move the items.
|
||||
child.InternalPrefix = append(trie.InternalPrefix, child.InternalPrefix...)
|
||||
if trie.InternalItem != nil {
|
||||
child.InternalItem = trie.InternalItem
|
||||
}
|
||||
|
||||
return child
|
||||
}
|
||||
|
||||
func (trie *Trie) findSubtree(prefix Prefix) (parent *Trie, root *Trie, found bool, leftover Prefix) {
|
||||
// Find the subtree matching prefix.
|
||||
root = trie
|
||||
for {
|
||||
// Compute what part of prefix matches.
|
||||
common := root.longestCommonPrefixLength(prefix)
|
||||
prefix = prefix[common:]
|
||||
|
||||
// We used up the whole prefix, subtree found.
|
||||
if len(prefix) == 0 {
|
||||
found = true
|
||||
leftover = root.InternalPrefix[common:]
|
||||
return
|
||||
}
|
||||
|
||||
// Partial match means that there is no subtree matching prefix.
|
||||
if common < len(root.InternalPrefix) {
|
||||
leftover = root.InternalPrefix[common:]
|
||||
return
|
||||
}
|
||||
|
||||
// There is some prefix left, move to the children.
|
||||
child := root.InternalChildren.next(prefix[0])
|
||||
if child == nil {
|
||||
// There is nowhere to continue, there is no subtree matching prefix.
|
||||
return
|
||||
}
|
||||
|
||||
parent = root
|
||||
root = child
|
||||
}
|
||||
}
|
||||
|
||||
func (trie *Trie) walk(actualRootPrefix Prefix, visitor VisitorFunc) error {
|
||||
var prefix Prefix
|
||||
// Allocate a bit more space for prefix at the beginning.
|
||||
if actualRootPrefix == nil {
|
||||
prefix = make(Prefix, 32+len(trie.InternalPrefix))
|
||||
copy(prefix, trie.InternalPrefix)
|
||||
prefix = prefix[:len(trie.InternalPrefix)]
|
||||
} else {
|
||||
prefix = make(Prefix, 32+len(actualRootPrefix))
|
||||
copy(prefix, actualRootPrefix)
|
||||
prefix = prefix[:len(actualRootPrefix)]
|
||||
}
|
||||
|
||||
// Visit the root first. Not that this works for empty trie as well since
|
||||
// in that case item == nil && len(children) == 0.
|
||||
if trie.InternalItem != nil {
|
||||
if err := visitor(prefix, trie.InternalItem); err != nil {
|
||||
if err == SkipSubtree {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Then continue to the children.
|
||||
return trie.InternalChildren.walk(&prefix, visitor)
|
||||
}
|
||||
|
||||
func (trie *Trie) longestCommonPrefixLength(prefix Prefix) (i int) {
|
||||
for ; i < len(prefix) && i < len(trie.InternalPrefix) && prefix[i] == trie.InternalPrefix[i]; i++ {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Errors ----------------------------------------------------------------------
|
||||
|
||||
var (
|
||||
SkipSubtree = errors.New("Skip this subtree")
|
||||
ErrNilPrefix = errors.New("Nil prefix passed into a method call")
|
||||
)
|
161
Godeps/_workspace/src/github.com/minio-io/go-patricia/patricia/patricia_dense_test.go
generated
vendored
161
Godeps/_workspace/src/github.com/minio-io/go-patricia/patricia/patricia_dense_test.go
generated
vendored
@ -1,161 +0,0 @@
|
||||
// Copyright (c) 2014 The go-patricia AUTHORS
|
||||
//
|
||||
// Use of this source code is governed by The MIT License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package patricia
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Tests -----------------------------------------------------------------------
|
||||
|
||||
func TestTrie_InsertDense(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"aba", 0, success},
|
||||
{"abb", 1, success},
|
||||
{"abc", 2, success},
|
||||
{"abd", 3, success},
|
||||
{"abe", 4, success},
|
||||
{"abf", 5, success},
|
||||
{"abg", 6, success},
|
||||
{"abh", 7, success},
|
||||
{"abi", 8, success},
|
||||
{"abj", 9, success},
|
||||
{"abk", 0, success},
|
||||
{"abl", 1, success},
|
||||
{"abm", 2, success},
|
||||
{"abn", 3, success},
|
||||
{"abo", 4, success},
|
||||
{"abp", 5, success},
|
||||
{"abq", 6, success},
|
||||
{"abr", 7, success},
|
||||
{"abs", 8, success},
|
||||
{"abt", 9, success},
|
||||
{"abu", 0, success},
|
||||
{"abv", 1, success},
|
||||
{"abw", 2, success},
|
||||
{"abx", 3, success},
|
||||
{"aby", 4, success},
|
||||
{"abz", 5, success},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_InsertDensePreceeding(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
start := byte(70)
|
||||
// create a dense node
|
||||
for i := byte(0); i <= MaxChildrenPerSparseNode; i++ {
|
||||
if !trie.Insert(Prefix([]byte{start + i}), true) {
|
||||
t.Errorf("insert failed, prefix=%v", start+i)
|
||||
}
|
||||
}
|
||||
// insert some preceeding keys
|
||||
for i := byte(1); i < start; i *= i + 1 {
|
||||
if !trie.Insert(Prefix([]byte{start - i}), true) {
|
||||
t.Errorf("insert failed, prefix=%v", start-i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_InsertDenseDuplicatePrefixes(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"aba", 0, success},
|
||||
{"abb", 1, success},
|
||||
{"abc", 2, success},
|
||||
{"abd", 3, success},
|
||||
{"abe", 4, success},
|
||||
{"abf", 5, success},
|
||||
{"abg", 6, success},
|
||||
{"abh", 7, success},
|
||||
{"abi", 8, success},
|
||||
{"abj", 9, success},
|
||||
{"abk", 0, success},
|
||||
{"abl", 1, success},
|
||||
{"abm", 2, success},
|
||||
{"abn", 3, success},
|
||||
{"abo", 4, success},
|
||||
{"abp", 5, success},
|
||||
{"abq", 6, success},
|
||||
{"abr", 7, success},
|
||||
{"abs", 8, success},
|
||||
{"abt", 9, success},
|
||||
{"abu", 0, success},
|
||||
{"abv", 1, success},
|
||||
{"abw", 2, success},
|
||||
{"abx", 3, success},
|
||||
{"aby", 4, success},
|
||||
{"abz", 5, success},
|
||||
{"aba", 0, failure},
|
||||
{"abb", 1, failure},
|
||||
{"abc", 2, failure},
|
||||
{"abd", 3, failure},
|
||||
{"abe", 4, failure},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_DeleteDense(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"aba", 0, success},
|
||||
{"abb", 1, success},
|
||||
{"abc", 2, success},
|
||||
{"abd", 3, success},
|
||||
{"abe", 4, success},
|
||||
{"abf", 5, success},
|
||||
{"abg", 6, success},
|
||||
{"abh", 7, success},
|
||||
{"abi", 8, success},
|
||||
{"abj", 9, success},
|
||||
{"abk", 0, success},
|
||||
{"abl", 1, success},
|
||||
{"abm", 2, success},
|
||||
{"abn", 3, success},
|
||||
{"abo", 4, success},
|
||||
{"abp", 5, success},
|
||||
{"abq", 6, success},
|
||||
{"abr", 7, success},
|
||||
{"abs", 8, success},
|
||||
{"abt", 9, success},
|
||||
{"abu", 0, success},
|
||||
{"abv", 1, success},
|
||||
{"abw", 2, success},
|
||||
{"abx", 3, success},
|
||||
{"aby", 4, success},
|
||||
{"abz", 5, success},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("DELETE word=%v, success=%v", v.key, v.retVal)
|
||||
if ok := trie.Delete([]byte(v.key)); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
}
|
659
Godeps/_workspace/src/github.com/minio-io/go-patricia/patricia/patricia_sparse_test.go
generated
vendored
659
Godeps/_workspace/src/github.com/minio-io/go-patricia/patricia/patricia_sparse_test.go
generated
vendored
@ -1,659 +0,0 @@
|
||||
// Copyright (c) 2014 The go-patricia AUTHORS
|
||||
//
|
||||
// Use of this source code is governed by The MIT License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package patricia
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
success = true
|
||||
failure = false
|
||||
)
|
||||
|
||||
type testData struct {
|
||||
key string
|
||||
value interface{}
|
||||
retVal bool
|
||||
}
|
||||
|
||||
// Tests -----------------------------------------------------------------------
|
||||
|
||||
func TestTrie_InsertDifferentPrefixes(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"Pepaneeeeeeeeeeeeee", "Pepan Zdepan", success},
|
||||
{"Honzooooooooooooooo", "Honza Novak", success},
|
||||
{"Jenikuuuuuuuuuuuuuu", "Jenik Poustevnicek", success},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_InsertDuplicatePrefixes(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"Pepan", "Pepan Zdepan", success},
|
||||
{"Pepan", "Pepan Zdepan", failure},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_InsertVariousPrefixes(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"Pepan", "Pepan Zdepan", success},
|
||||
{"Pepin", "Pepin Omacka", success},
|
||||
{"Honza", "Honza Novak", success},
|
||||
{"Jenik", "Jenik Poustevnicek", success},
|
||||
{"Pepan", "Pepan Dupan", failure},
|
||||
{"Karel", "Karel Pekar", success},
|
||||
{"Jenik", "Jenik Poustevnicek", failure},
|
||||
{"Pepanek", "Pepanek Zemlicka", success},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_InsertAndMatchPrefix(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
t.Log("INSERT prefix=by week")
|
||||
trie.Insert(Prefix("by week"), 2)
|
||||
t.Log("INSERT prefix=by")
|
||||
trie.Insert(Prefix("by"), 1)
|
||||
|
||||
if !trie.Match(Prefix("by")) {
|
||||
t.Error("MATCH prefix=by, expected=true, got=false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_SetGet(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"Pepan", "Pepan Zdepan", success},
|
||||
{"Pepin", "Pepin Omacka", success},
|
||||
{"Honza", "Honza Novak", success},
|
||||
{"Jenik", "Jenik Poustevnicek", success},
|
||||
{"Pepan", "Pepan Dupan", failure},
|
||||
{"Karel", "Karel Pekar", success},
|
||||
{"Jenik", "Jenik Poustevnicek", failure},
|
||||
{"Pepanek", "Pepanek Zemlicka", success},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("SET %q to 10", v.key)
|
||||
trie.Set(Prefix(v.key), 10)
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
value := trie.Get(Prefix(v.key))
|
||||
t.Logf("GET %q => %v", v.key, value)
|
||||
if value.(int) != 10 {
|
||||
t.Errorf("Unexpected return value, != 10", value)
|
||||
}
|
||||
}
|
||||
|
||||
if value := trie.Get(Prefix("random crap")); value != nil {
|
||||
t.Errorf("Unexpected return value, %v != <nil>", value)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_Match(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"Pepan", "Pepan Zdepan", success},
|
||||
{"Pepin", "Pepin Omacka", success},
|
||||
{"Honza", "Honza Novak", success},
|
||||
{"Jenik", "Jenik Poustevnicek", success},
|
||||
{"Pepan", "Pepan Dupan", failure},
|
||||
{"Karel", "Karel Pekar", success},
|
||||
{"Jenik", "Jenik Poustevnicek", failure},
|
||||
{"Pepanek", "Pepanek Zemlicka", success},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
matched := trie.Match(Prefix(v.key))
|
||||
t.Logf("MATCH %q => %v", v.key, matched)
|
||||
if !matched {
|
||||
t.Errorf("Inserted key %q was not matched", v.key)
|
||||
}
|
||||
}
|
||||
|
||||
if trie.Match(Prefix("random crap")) {
|
||||
t.Errorf("Key that was not inserted matched: %q", "random crap")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_MatchFalsePositive(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
if ok := trie.Insert(Prefix("A"), 1); !ok {
|
||||
t.Fatal("INSERT prefix=A, item=1 not ok")
|
||||
}
|
||||
|
||||
resultMatchSubtree := trie.MatchSubtree(Prefix("A extra"))
|
||||
resultMatch := trie.Match(Prefix("A extra"))
|
||||
|
||||
if resultMatchSubtree != false {
|
||||
t.Error("MatchSubtree returned false positive")
|
||||
}
|
||||
|
||||
if resultMatch != false {
|
||||
t.Error("Match returned false positive")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_MatchSubtree(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"Pepan", "Pepan Zdepan", success},
|
||||
{"Pepin", "Pepin Omacka", success},
|
||||
{"Honza", "Honza Novak", success},
|
||||
{"Jenik", "Jenik Poustevnicek", success},
|
||||
{"Pepan", "Pepan Dupan", failure},
|
||||
{"Karel", "Karel Pekar", success},
|
||||
{"Jenik", "Jenik Poustevnicek", failure},
|
||||
{"Pepanek", "Pepanek Zemlicka", success},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
key := Prefix(v.key[:3])
|
||||
matched := trie.MatchSubtree(key)
|
||||
t.Logf("MATCH_SUBTREE %q => %v", key, matched)
|
||||
if !matched {
|
||||
t.Errorf("Subtree %q was not matched", v.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_Visit(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"Pepa", 0, success},
|
||||
{"Pepa Zdepa", 1, success},
|
||||
{"Pepa Kuchar", 2, success},
|
||||
{"Honza", 3, success},
|
||||
{"Jenik", 4, success},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
|
||||
t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
|
||||
if err := trie.Visit(func(prefix Prefix, item Item) error {
|
||||
name := data[item.(int)].key
|
||||
t.Logf("VISITING prefix=%q, item=%v", prefix, item)
|
||||
if !strings.HasPrefix(string(prefix), name) {
|
||||
t.Errorf("Unexpected prefix encountered, %q not a prefix of %q", prefix, name)
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_VisitSkipSubtree(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"Pepa", 0, success},
|
||||
{"Pepa Zdepa", 1, success},
|
||||
{"Pepa Kuchar", 2, success},
|
||||
{"Honza", 3, success},
|
||||
{"Jenik", 4, success},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
|
||||
t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
|
||||
if err := trie.Visit(func(prefix Prefix, item Item) error {
|
||||
t.Logf("VISITING prefix=%q, item=%v", prefix, item)
|
||||
if item.(int) == 0 {
|
||||
t.Logf("SKIP %q", prefix)
|
||||
return SkipSubtree
|
||||
}
|
||||
if strings.HasPrefix(string(prefix), "Pepa") {
|
||||
t.Errorf("Unexpected prefix encountered, %q", prefix)
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_VisitReturnError(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"Pepa", 0, success},
|
||||
{"Pepa Zdepa", 1, success},
|
||||
{"Pepa Kuchar", 2, success},
|
||||
{"Honza", 3, success},
|
||||
{"Jenik", 4, success},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
|
||||
t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
|
||||
someErr := errors.New("Something exploded")
|
||||
if err := trie.Visit(func(prefix Prefix, item Item) error {
|
||||
t.Logf("VISITING prefix=%q, item=%v", prefix, item)
|
||||
if item.(int) == 0 {
|
||||
return someErr
|
||||
}
|
||||
if item.(int) != 0 {
|
||||
t.Errorf("Unexpected prefix encountered, %q", prefix)
|
||||
}
|
||||
return nil
|
||||
}); err != nil && err != someErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_VisitSubtree(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"Pepa", 0, success},
|
||||
{"Pepa Zdepa", 1, success},
|
||||
{"Pepa Kuchar", 2, success},
|
||||
{"Honza", 3, success},
|
||||
{"Jenik", 4, success},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
|
||||
t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
|
||||
var counter int
|
||||
subtreePrefix := []byte("Pep")
|
||||
t.Log("VISIT Pep")
|
||||
if err := trie.VisitSubtree(subtreePrefix, func(prefix Prefix, item Item) error {
|
||||
t.Logf("VISITING prefix=%q, item=%v", prefix, item)
|
||||
if !bytes.HasPrefix(prefix, subtreePrefix) {
|
||||
t.Errorf("Unexpected prefix encountered, %q does not extend %q",
|
||||
prefix, subtreePrefix)
|
||||
}
|
||||
if len(prefix) > len(data[item.(int)].key) {
|
||||
t.Fatalf("Something is rather fishy here, prefix=%q", prefix)
|
||||
}
|
||||
counter++
|
||||
return nil
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if counter != 3 {
|
||||
t.Error("Unexpected number of nodes visited")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_VisitPrefixes(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"P", 0, success},
|
||||
{"Pe", 1, success},
|
||||
{"Pep", 2, success},
|
||||
{"Pepa", 3, success},
|
||||
{"Pepa Zdepa", 4, success},
|
||||
{"Pepa Kuchar", 5, success},
|
||||
{"Honza", 6, success},
|
||||
{"Jenik", 7, success},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
|
||||
t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
|
||||
var counter int
|
||||
word := []byte("Pepa")
|
||||
if err := trie.VisitPrefixes(word, func(prefix Prefix, item Item) error {
|
||||
t.Logf("VISITING prefix=%q, item=%v", prefix, item)
|
||||
if !bytes.HasPrefix(word, prefix) {
|
||||
t.Errorf("Unexpected prefix encountered, %q is not a prefix of %q",
|
||||
prefix, word)
|
||||
}
|
||||
counter++
|
||||
return nil
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if counter != 4 {
|
||||
t.Error("Unexpected number of nodes visited")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParticiaTrie_Delete(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"Pepan", "Pepan Zdepan", success},
|
||||
{"Honza", "Honza Novak", success},
|
||||
{"Jenik", "Jenik Poustevnicek", success},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
|
||||
t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("DELETE word=%v, success=%v", v.key, v.retVal)
|
||||
if ok := trie.Delete([]byte(v.key)); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParticiaTrie_DeleteNonExistent(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
insertData := []testData{
|
||||
{"Pepan", "Pepan Zdepan", success},
|
||||
{"Honza", "Honza Novak", success},
|
||||
{"Jenik", "Jenik Poustevnicek", success},
|
||||
}
|
||||
deleteData := []testData{
|
||||
{"Pepan", "Pepan Zdepan", success},
|
||||
{"Honza", "Honza Novak", success},
|
||||
{"Pepan", "Pepan Zdepan", failure},
|
||||
{"Jenik", "Jenik Poustevnicek", success},
|
||||
{"Honza", "Honza Novak", failure},
|
||||
}
|
||||
|
||||
for _, v := range insertData {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
|
||||
t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range deleteData {
|
||||
t.Logf("DELETE word=%v, success=%v", v.key, v.retVal)
|
||||
if ok := trie.Delete([]byte(v.key)); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParticiaTrie_DeleteSubtree(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
insertData := []testData{
|
||||
{"P", 0, success},
|
||||
{"Pe", 1, success},
|
||||
{"Pep", 2, success},
|
||||
{"Pepa", 3, success},
|
||||
{"Pepa Zdepa", 4, success},
|
||||
{"Pepa Kuchar", 5, success},
|
||||
{"Honza", 6, success},
|
||||
{"Jenik", 7, success},
|
||||
}
|
||||
deleteData := []testData{
|
||||
{"Pe", -1, success},
|
||||
{"Pe", -1, failure},
|
||||
{"Honzik", -1, failure},
|
||||
{"Honza", -1, success},
|
||||
{"Honza", -1, failure},
|
||||
{"Pep", -1, failure},
|
||||
{"P", -1, success},
|
||||
{"Nobody", -1, failure},
|
||||
{"", -1, success},
|
||||
}
|
||||
|
||||
for _, v := range insertData {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
|
||||
t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range deleteData {
|
||||
t.Logf("DELETE_SUBTREE prefix=%v, success=%v", v.key, v.retVal)
|
||||
if ok := trie.DeleteSubtree([]byte(v.key)); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func TestTrie_Dump(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"Honda", nil, success},
|
||||
{"Honza", nil, success},
|
||||
{"Jenik", nil, success},
|
||||
{"Pepan", nil, success},
|
||||
{"Pepin", nil, success},
|
||||
}
|
||||
|
||||
for i, v := range data {
|
||||
if _, ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
|
||||
t.Logf("INSERT %v %v", v.key, v.value)
|
||||
t.Fatalf("Unexpected return value, expected=%v, got=%v", i, ok)
|
||||
}
|
||||
}
|
||||
|
||||
dump := `
|
||||
+--+--+ Hon +--+--+ da
|
||||
| |
|
||||
| +--+ za
|
||||
|
|
||||
+--+ Jenik
|
||||
|
|
||||
+--+ Pep +--+--+ an
|
||||
|
|
||||
+--+ in
|
||||
`
|
||||
|
||||
var buf bytes.Buffer
|
||||
trie.Dump(buf)
|
||||
|
||||
if !bytes.Equal(buf.Bytes(), dump) {
|
||||
t.Logf("DUMP")
|
||||
t.Fatalf("Unexpected dump generated, expected\n\n%v\ngot\n\n%v", dump, buf.String())
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func TestTrie_compact(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
trie.Insert(Prefix("a"), 0)
|
||||
trie.Insert(Prefix("ab"), 0)
|
||||
trie.Insert(Prefix("abc"), 0)
|
||||
trie.Insert(Prefix("abcd"), 0)
|
||||
trie.Insert(Prefix("abcde"), 0)
|
||||
trie.Insert(Prefix("abcdef"), 0)
|
||||
trie.Insert(Prefix("abcdefg"), 0)
|
||||
trie.Insert(Prefix("abcdefgi"), 0)
|
||||
trie.Insert(Prefix("abcdefgij"), 0)
|
||||
trie.Insert(Prefix("abcdefgijk"), 0)
|
||||
|
||||
trie.Delete(Prefix("abcdef"))
|
||||
trie.Delete(Prefix("abcde"))
|
||||
trie.Delete(Prefix("abcdefg"))
|
||||
|
||||
trie.Delete(Prefix("a"))
|
||||
trie.Delete(Prefix("abc"))
|
||||
trie.Delete(Prefix("ab"))
|
||||
|
||||
trie.Visit(func(prefix Prefix, item Item) error {
|
||||
// 97 ~~ 'a',
|
||||
for ch := byte(97); ch <= 107; ch++ {
|
||||
if c := bytes.Count(prefix, []byte{ch}); c > 1 {
|
||||
t.Errorf("%q appeared in %q %v times", ch, prefix, c)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func TestTrie_longestCommonPrefixLenght(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
trie.InternalPrefix = []byte("1234567890")
|
||||
|
||||
switch {
|
||||
case trie.longestCommonPrefixLength([]byte("")) != 0:
|
||||
t.Fail()
|
||||
case trie.longestCommonPrefixLength([]byte("12345")) != 5:
|
||||
t.Fail()
|
||||
case trie.longestCommonPrefixLength([]byte("123789")) != 3:
|
||||
t.Fail()
|
||||
case trie.longestCommonPrefixLength([]byte("12345678901")) != 10:
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
// Examples --------------------------------------------------------------------
|
||||
|
||||
func ExampleTrie() {
|
||||
// Create a new tree.
|
||||
trie := NewTrie()
|
||||
|
||||
// Insert some items.
|
||||
trie.Insert(Prefix("Pepa Novak"), 1)
|
||||
trie.Insert(Prefix("Pepa Sindelar"), 2)
|
||||
trie.Insert(Prefix("Karel Macha"), 3)
|
||||
trie.Insert(Prefix("Karel Hynek Macha"), 4)
|
||||
|
||||
// Just check if some things are present in the tree.
|
||||
key := Prefix("Pepa Novak")
|
||||
fmt.Printf("%q present? %v\n", key, trie.Match(key))
|
||||
key = Prefix("Karel")
|
||||
fmt.Printf("Anybody called %q here? %v\n", key, trie.MatchSubtree(key))
|
||||
|
||||
// Walk the tree.
|
||||
trie.Visit(printItem)
|
||||
// "Pepa Novak": 1
|
||||
// "Pepa Sindelar": 2
|
||||
// "Karel Macha": 3
|
||||
// "Karel Hynek Macha": 4
|
||||
|
||||
// Walk a subtree.
|
||||
trie.VisitSubtree(Prefix("Pepa"), printItem)
|
||||
// "Pepa Novak": 1
|
||||
// "Pepa Sindelar": 2
|
||||
|
||||
// Modify an item, then fetch it from the tree.
|
||||
trie.Set(Prefix("Karel Hynek Macha"), 10)
|
||||
key = Prefix("Karel Hynek Macha")
|
||||
fmt.Printf("%q: %v\n", key, trie.Get(key))
|
||||
// "Karel Hynek Macha": 10
|
||||
|
||||
// Walk prefixes.
|
||||
prefix := Prefix("Karel Hynek Macha je kouzelnik")
|
||||
trie.VisitPrefixes(prefix, printItem)
|
||||
// "Karel Hynek Macha": 10
|
||||
|
||||
// Delete some items.
|
||||
trie.Delete(Prefix("Pepa Novak"))
|
||||
trie.Delete(Prefix("Karel Macha"))
|
||||
|
||||
// Walk again.
|
||||
trie.Visit(printItem)
|
||||
// "Pepa Sindelar": 2
|
||||
// "Karel Hynek Macha": 10
|
||||
|
||||
// Delete a subtree.
|
||||
trie.DeleteSubtree(Prefix("Pepa"))
|
||||
|
||||
// Print what is left.
|
||||
trie.Visit(printItem)
|
||||
// "Karel Hynek Macha": 10
|
||||
|
||||
// Output:
|
||||
// "Pepa Novak" present? true
|
||||
// Anybody called "Karel" here? true
|
||||
// "Pepa Novak": 1
|
||||
// "Pepa Sindelar": 2
|
||||
// "Karel Macha": 3
|
||||
// "Karel Hynek Macha": 4
|
||||
// "Pepa Novak": 1
|
||||
// "Pepa Sindelar": 2
|
||||
// "Karel Hynek Macha": 10
|
||||
// "Karel Hynek Macha": 10
|
||||
// "Pepa Sindelar": 2
|
||||
// "Karel Hynek Macha": 10
|
||||
// "Karel Hynek Macha": 10
|
||||
}
|
||||
|
||||
// Helpers ---------------------------------------------------------------------
|
||||
|
||||
func printItem(prefix Prefix, item Item) error {
|
||||
fmt.Printf("%q: %v\n", prefix, item)
|
||||
return nil
|
||||
}
|
105
Godeps/_workspace/src/github.com/minio-io/go-patricia/patricia/patricia_test.go
generated
vendored
105
Godeps/_workspace/src/github.com/minio-io/go-patricia/patricia/patricia_test.go
generated
vendored
@ -1,105 +0,0 @@
|
||||
// Copyright (c) 2014 The go-patricia AUTHORS
|
||||
//
|
||||
// Use of this source code is governed by The MIT License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package patricia
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/gob"
|
||||
"log"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Tests -----------------------------------------------------------------------
|
||||
|
||||
func TestTrie_GetNonexistentPrefix(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"aba", 0, success},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
|
||||
t.Logf("GET prefix=baa, expect item=nil")
|
||||
if item := trie.Get(Prefix("baa")); item != nil {
|
||||
t.Errorf("Unexpected return value, expected=<nil>, got=%v", item)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_RandomKitchenSink(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip()
|
||||
}
|
||||
const count, size = 750000, 16
|
||||
b := make([]byte, count+size+1)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
t.Fatal("error generating random bytes", err)
|
||||
}
|
||||
m := make(map[string]string)
|
||||
for i := 0; i < count; i++ {
|
||||
m[string(b[i:i+size])] = string(b[i+1 : i+size+1])
|
||||
}
|
||||
trie := NewTrie()
|
||||
getAndDelete := func(k, v string) {
|
||||
i := trie.Get(Prefix(k))
|
||||
if i == nil {
|
||||
t.Fatalf("item not found, prefix=%v", []byte(k))
|
||||
} else if s, ok := i.(string); !ok {
|
||||
t.Fatalf("unexpected item type, expecting=%v, got=%v", reflect.TypeOf(k), reflect.TypeOf(i))
|
||||
} else if s != v {
|
||||
t.Fatalf("unexpected item, expecting=%v, got=%v", []byte(k), []byte(s))
|
||||
} else if !trie.Delete(Prefix(k)) {
|
||||
t.Fatalf("delete failed, prefix=%v", []byte(k))
|
||||
} else if i = trie.Get(Prefix(k)); i != nil {
|
||||
t.Fatalf("unexpected item, expecting=<nil>, got=%v", i)
|
||||
} else if trie.Delete(Prefix(k)) {
|
||||
t.Fatalf("extra delete succeeded, prefix=%v", []byte(k))
|
||||
}
|
||||
}
|
||||
for k, v := range m {
|
||||
if !trie.Insert(Prefix(k), v) {
|
||||
t.Fatalf("insert failed, prefix=%v", []byte(k))
|
||||
}
|
||||
if byte(k[size/2]) < 128 {
|
||||
getAndDelete(k, v)
|
||||
delete(m, k)
|
||||
}
|
||||
}
|
||||
for k, v := range m {
|
||||
getAndDelete(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrieSerializes(t *testing.T) {
|
||||
gob.Register(&SparseChildList{})
|
||||
gob.Register(&DenseChildList{})
|
||||
trie := NewTrie()
|
||||
trie.Insert([]byte("hello1"), "world1")
|
||||
trie.Insert([]byte("hello2"), "world2")
|
||||
var bytesBuffer bytes.Buffer
|
||||
encoder := gob.NewEncoder(&bytesBuffer)
|
||||
if err := encoder.Encode(trie); err != nil {
|
||||
t.Fatalf("Serialization failed:", err)
|
||||
}
|
||||
encodedReader := bytes.NewReader(bytesBuffer.Bytes())
|
||||
decoder := gob.NewDecoder(encodedReader)
|
||||
trie2 := NewTrie()
|
||||
decoder.Decode(&trie2)
|
||||
log.Println(trie2)
|
||||
if value := trie2.Get([]byte("hello1")); value == nil {
|
||||
t.Fatalf("hello1 not serialized")
|
||||
}
|
||||
if value := trie2.Get([]byte("hello2")); value == nil {
|
||||
t.Fatalf("hello1 not serialized")
|
||||
}
|
||||
}
|
22
Godeps/_workspace/src/github.com/spaolacci/murmur3/.gitignore
generated
vendored
22
Godeps/_workspace/src/github.com/spaolacci/murmur3/.gitignore
generated
vendored
@ -1,22 +0,0 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
24
Godeps/_workspace/src/github.com/spaolacci/murmur3/LICENSE
generated
vendored
24
Godeps/_workspace/src/github.com/spaolacci/murmur3/LICENSE
generated
vendored
@ -1,24 +0,0 @@
|
||||
Copyright 2013, Sébastien Paolacci.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the library nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
84
Godeps/_workspace/src/github.com/spaolacci/murmur3/README.md
generated
vendored
84
Godeps/_workspace/src/github.com/spaolacci/murmur3/README.md
generated
vendored
@ -1,84 +0,0 @@
|
||||
murmur3
|
||||
=======
|
||||
|
||||
Native Go implementation of Austin Appleby's third MurmurHash revision (aka
|
||||
MurmurHash3).
|
||||
|
||||
Reference algorithm has been slightly hacked as to support the streaming mode
|
||||
required by Go's standard [Hash interface](http://golang.org/pkg/hash/#Hash).
|
||||
|
||||
|
||||
Benchmarks
|
||||
----------
|
||||
|
||||
Go tip as of 2014-06-12 (i.e almost go1.3), core i7 @ 3.4 Ghz. All runs
|
||||
include hasher instantiation and sequence finalization.
|
||||
|
||||
<pre>
|
||||
|
||||
Benchmark32_1 500000000 7.69 ns/op 130.00 MB/s
|
||||
Benchmark32_2 200000000 8.83 ns/op 226.42 MB/s
|
||||
Benchmark32_4 500000000 7.99 ns/op 500.39 MB/s
|
||||
Benchmark32_8 200000000 9.47 ns/op 844.69 MB/s
|
||||
Benchmark32_16 100000000 12.1 ns/op 1321.61 MB/s
|
||||
Benchmark32_32 100000000 18.3 ns/op 1743.93 MB/s
|
||||
Benchmark32_64 50000000 30.9 ns/op 2071.64 MB/s
|
||||
Benchmark32_128 50000000 57.6 ns/op 2222.96 MB/s
|
||||
Benchmark32_256 20000000 116 ns/op 2188.60 MB/s
|
||||
Benchmark32_512 10000000 226 ns/op 2260.59 MB/s
|
||||
Benchmark32_1024 5000000 452 ns/op 2263.73 MB/s
|
||||
Benchmark32_2048 2000000 891 ns/op 2296.02 MB/s
|
||||
Benchmark32_4096 1000000 1787 ns/op 2290.92 MB/s
|
||||
Benchmark32_8192 500000 3593 ns/op 2279.68 MB/s
|
||||
Benchmark128_1 100000000 26.1 ns/op 38.33 MB/s
|
||||
Benchmark128_2 100000000 29.0 ns/op 69.07 MB/s
|
||||
Benchmark128_4 50000000 29.8 ns/op 134.17 MB/s
|
||||
Benchmark128_8 50000000 31.6 ns/op 252.86 MB/s
|
||||
Benchmark128_16 100000000 26.5 ns/op 603.42 MB/s
|
||||
Benchmark128_32 100000000 28.6 ns/op 1117.15 MB/s
|
||||
Benchmark128_64 50000000 35.5 ns/op 1800.97 MB/s
|
||||
Benchmark128_128 50000000 50.9 ns/op 2515.50 MB/s
|
||||
Benchmark128_256 20000000 76.9 ns/op 3330.11 MB/s
|
||||
Benchmark128_512 20000000 135 ns/op 3769.09 MB/s
|
||||
Benchmark128_1024 10000000 250 ns/op 4094.38 MB/s
|
||||
Benchmark128_2048 5000000 477 ns/op 4290.75 MB/s
|
||||
Benchmark128_4096 2000000 940 ns/op 4353.29 MB/s
|
||||
Benchmark128_8192 1000000 1838 ns/op 4455.47 MB/s
|
||||
|
||||
</pre>
|
||||
|
||||
|
||||
<pre>
|
||||
|
||||
benchmark Go1.0 MB/s Go1.1 MB/s speedup Go1.2 MB/s speedup Go1.3 MB/s speedup
|
||||
Benchmark32_1 98.90 118.59 1.20x 114.79 0.97x 130.00 1.13x
|
||||
Benchmark32_2 168.04 213.31 1.27x 210.65 0.99x 226.42 1.07x
|
||||
Benchmark32_4 414.01 494.19 1.19x 490.29 0.99x 500.39 1.02x
|
||||
Benchmark32_8 662.19 836.09 1.26x 836.46 1.00x 844.69 1.01x
|
||||
Benchmark32_16 917.46 1304.62 1.42x 1297.63 0.99x 1321.61 1.02x
|
||||
Benchmark32_32 1141.93 1737.54 1.52x 1728.24 0.99x 1743.93 1.01x
|
||||
Benchmark32_64 1289.47 2039.51 1.58x 2038.20 1.00x 2071.64 1.02x
|
||||
Benchmark32_128 1299.23 2097.63 1.61x 2177.13 1.04x 2222.96 1.02x
|
||||
Benchmark32_256 1369.90 2202.34 1.61x 2213.15 1.00x 2188.60 0.99x
|
||||
Benchmark32_512 1399.56 2255.72 1.61x 2264.49 1.00x 2260.59 1.00x
|
||||
Benchmark32_1024 1410.90 2285.82 1.62x 2270.99 0.99x 2263.73 1.00x
|
||||
Benchmark32_2048 1422.14 2297.62 1.62x 2269.59 0.99x 2296.02 1.01x
|
||||
Benchmark32_4096 1420.53 2307.81 1.62x 2273.43 0.99x 2290.92 1.01x
|
||||
Benchmark32_8192 1424.79 2312.87 1.62x 2286.07 0.99x 2279.68 1.00x
|
||||
Benchmark128_1 8.32 30.15 3.62x 30.84 1.02x 38.33 1.24x
|
||||
Benchmark128_2 16.38 59.72 3.65x 59.37 0.99x 69.07 1.16x
|
||||
Benchmark128_4 32.26 112.96 3.50x 114.24 1.01x 134.17 1.17x
|
||||
Benchmark128_8 62.68 217.88 3.48x 218.18 1.00x 252.86 1.16x
|
||||
Benchmark128_16 128.47 451.57 3.51x 474.65 1.05x 603.42 1.27x
|
||||
Benchmark128_32 246.18 910.42 3.70x 871.06 0.96x 1117.15 1.28x
|
||||
Benchmark128_64 449.05 1477.64 3.29x 1449.24 0.98x 1800.97 1.24x
|
||||
Benchmark128_128 762.61 2222.42 2.91x 2217.30 1.00x 2515.50 1.13x
|
||||
Benchmark128_256 1179.92 3005.46 2.55x 2931.55 0.98x 3330.11 1.14x
|
||||
Benchmark128_512 1616.51 3590.75 2.22x 3592.08 1.00x 3769.09 1.05x
|
||||
Benchmark128_1024 1964.36 3979.67 2.03x 4034.01 1.01x 4094.38 1.01x
|
||||
Benchmark128_2048 2225.07 4156.93 1.87x 4244.17 1.02x 4290.75 1.01x
|
||||
Benchmark128_4096 2360.15 4299.09 1.82x 4392.35 1.02x 4353.29 0.99x
|
||||
Benchmark128_8192 2411.50 4356.84 1.81x 4480.68 1.03x 4455.47 0.99x
|
||||
|
||||
</pre>
|
||||
|
65
Godeps/_workspace/src/github.com/spaolacci/murmur3/murmur.go
generated
vendored
65
Godeps/_workspace/src/github.com/spaolacci/murmur3/murmur.go
generated
vendored
@ -1,65 +0,0 @@
|
||||
// Copyright 2013, Sébastien Paolacci. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Native (and fast) implementation of Austin Appleby's MurmurHash3.
|
||||
|
||||
Package murmur3 implements Austin Appleby's non-cryptographic MurmurHash3.
|
||||
|
||||
Reference implementation:
|
||||
http://code.google.com/p/smhasher/wiki/MurmurHash3
|
||||
|
||||
History, characteristics and (legacy) perfs:
|
||||
https://sites.google.com/site/murmurhash/
|
||||
https://sites.google.com/site/murmurhash/statistics
|
||||
*/
|
||||
package murmur3
|
||||
|
||||
type bmixer interface {
|
||||
bmix(p []byte) (tail []byte)
|
||||
Size() (n int)
|
||||
reset()
|
||||
}
|
||||
|
||||
type digest struct {
|
||||
clen int // Digested input cumulative length.
|
||||
tail []byte // 0 to Size()-1 bytes view of `buf'.
|
||||
buf [16]byte // Expected (but not required) to be Size() large.
|
||||
bmixer
|
||||
}
|
||||
|
||||
func (d *digest) BlockSize() int { return 1 }
|
||||
|
||||
func (d *digest) Write(p []byte) (n int, err error) {
|
||||
n = len(p)
|
||||
d.clen += n
|
||||
|
||||
if len(d.tail) > 0 {
|
||||
// Stick back pending bytes.
|
||||
nfree := d.Size() - len(d.tail) // nfree ∈ [1, d.Size()-1].
|
||||
if nfree < len(p) {
|
||||
// One full block can be formed.
|
||||
block := append(d.tail, p[:nfree]...)
|
||||
p = p[nfree:]
|
||||
_ = d.bmix(block) // No tail.
|
||||
} else {
|
||||
// Tail's buf is large enough to prevent reallocs.
|
||||
p = append(d.tail, p...)
|
||||
}
|
||||
}
|
||||
|
||||
d.tail = d.bmix(p)
|
||||
|
||||
// Keep own copy of the 0 to Size()-1 pending bytes.
|
||||
nn := copy(d.buf[:], d.tail)
|
||||
d.tail = d.buf[:nn]
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (d *digest) Reset() {
|
||||
d.clen = 0
|
||||
d.tail = nil
|
||||
d.bmixer.reset()
|
||||
}
|
189
Godeps/_workspace/src/github.com/spaolacci/murmur3/murmur128.go
generated
vendored
189
Godeps/_workspace/src/github.com/spaolacci/murmur3/murmur128.go
generated
vendored
@ -1,189 +0,0 @@
|
||||
package murmur3
|
||||
|
||||
import (
|
||||
//"encoding/binary"
|
||||
"hash"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
c1_128 = 0x87c37b91114253d5
|
||||
c2_128 = 0x4cf5ad432745937f
|
||||
)
|
||||
|
||||
// Make sure interfaces are correctly implemented.
|
||||
var (
|
||||
_ hash.Hash = new(digest128)
|
||||
_ Hash128 = new(digest128)
|
||||
_ bmixer = new(digest128)
|
||||
)
|
||||
|
||||
// Hack: the standard api doesn't define any Hash128 interface.
|
||||
type Hash128 interface {
|
||||
hash.Hash
|
||||
Sum128() (uint64, uint64)
|
||||
}
|
||||
|
||||
// digest128 represents a partial evaluation of a 128 bites hash.
|
||||
type digest128 struct {
|
||||
digest
|
||||
h1 uint64 // Unfinalized running hash part 1.
|
||||
h2 uint64 // Unfinalized running hash part 2.
|
||||
}
|
||||
|
||||
func New128() Hash128 {
|
||||
d := new(digest128)
|
||||
d.bmixer = d
|
||||
d.Reset()
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *digest128) Size() int { return 16 }
|
||||
|
||||
func (d *digest128) reset() { d.h1, d.h2 = 0, 0 }
|
||||
|
||||
func (d *digest128) Sum(b []byte) []byte {
|
||||
h1, h2 := d.h1, d.h2
|
||||
return append(b,
|
||||
byte(h1>>56), byte(h1>>48), byte(h1>>40), byte(h1>>32),
|
||||
byte(h1>>24), byte(h1>>16), byte(h1>>8), byte(h1),
|
||||
|
||||
byte(h2>>56), byte(h2>>48), byte(h2>>40), byte(h2>>32),
|
||||
byte(h2>>24), byte(h2>>16), byte(h2>>8), byte(h2),
|
||||
)
|
||||
}
|
||||
|
||||
func (d *digest128) bmix(p []byte) (tail []byte) {
|
||||
h1, h2 := d.h1, d.h2
|
||||
|
||||
nblocks := len(p) / 16
|
||||
for i := 0; i < nblocks; i++ {
|
||||
t := (*[2]uint64)(unsafe.Pointer(&p[i*16]))
|
||||
k1, k2 := t[0], t[1]
|
||||
|
||||
k1 *= c1_128
|
||||
k1 = (k1 << 31) | (k1 >> 33) // rotl64(k1, 31)
|
||||
k1 *= c2_128
|
||||
h1 ^= k1
|
||||
|
||||
h1 = (h1 << 27) | (h1 >> 37) // rotl64(h1, 27)
|
||||
h1 += h2
|
||||
h1 = h1*5 + 0x52dce729
|
||||
|
||||
k2 *= c2_128
|
||||
k2 = (k2 << 33) | (k2 >> 31) // rotl64(k2, 33)
|
||||
k2 *= c1_128
|
||||
h2 ^= k2
|
||||
|
||||
h2 = (h2 << 31) | (h2 >> 33) // rotl64(h2, 31)
|
||||
h2 += h1
|
||||
h2 = h2*5 + 0x38495ab5
|
||||
}
|
||||
d.h1, d.h2 = h1, h2
|
||||
return p[nblocks*d.Size():]
|
||||
}
|
||||
|
||||
func (d *digest128) Sum128() (h1, h2 uint64) {
|
||||
|
||||
h1, h2 = d.h1, d.h2
|
||||
|
||||
var k1, k2 uint64
|
||||
switch len(d.tail) & 15 {
|
||||
case 15:
|
||||
k2 ^= uint64(d.tail[14]) << 48
|
||||
fallthrough
|
||||
case 14:
|
||||
k2 ^= uint64(d.tail[13]) << 40
|
||||
fallthrough
|
||||
case 13:
|
||||
k2 ^= uint64(d.tail[12]) << 32
|
||||
fallthrough
|
||||
case 12:
|
||||
k2 ^= uint64(d.tail[11]) << 24
|
||||
fallthrough
|
||||
case 11:
|
||||
k2 ^= uint64(d.tail[10]) << 16
|
||||
fallthrough
|
||||
case 10:
|
||||
k2 ^= uint64(d.tail[9]) << 8
|
||||
fallthrough
|
||||
case 9:
|
||||
k2 ^= uint64(d.tail[8]) << 0
|
||||
|
||||
k2 *= c2_128
|
||||
k2 = (k2 << 33) | (k2 >> 31) // rotl64(k2, 33)
|
||||
k2 *= c1_128
|
||||
h2 ^= k2
|
||||
|
||||
fallthrough
|
||||
|
||||
case 8:
|
||||
k1 ^= uint64(d.tail[7]) << 56
|
||||
fallthrough
|
||||
case 7:
|
||||
k1 ^= uint64(d.tail[6]) << 48
|
||||
fallthrough
|
||||
case 6:
|
||||
k1 ^= uint64(d.tail[5]) << 40
|
||||
fallthrough
|
||||
case 5:
|
||||
k1 ^= uint64(d.tail[4]) << 32
|
||||
fallthrough
|
||||
case 4:
|
||||
k1 ^= uint64(d.tail[3]) << 24
|
||||
fallthrough
|
||||
case 3:
|
||||
k1 ^= uint64(d.tail[2]) << 16
|
||||
fallthrough
|
||||
case 2:
|
||||
k1 ^= uint64(d.tail[1]) << 8
|
||||
fallthrough
|
||||
case 1:
|
||||
k1 ^= uint64(d.tail[0]) << 0
|
||||
k1 *= c1_128
|
||||
k1 = (k1 << 31) | (k1 >> 33) // rotl64(k1, 31)
|
||||
k1 *= c2_128
|
||||
h1 ^= k1
|
||||
}
|
||||
|
||||
h1 ^= uint64(d.clen)
|
||||
h2 ^= uint64(d.clen)
|
||||
|
||||
h1 += h2
|
||||
h2 += h1
|
||||
|
||||
h1 = fmix64(h1)
|
||||
h2 = fmix64(h2)
|
||||
|
||||
h1 += h2
|
||||
h2 += h1
|
||||
|
||||
return h1, h2
|
||||
}
|
||||
|
||||
func fmix64(k uint64) uint64 {
|
||||
k ^= k >> 33
|
||||
k *= 0xff51afd7ed558ccd
|
||||
k ^= k >> 33
|
||||
k *= 0xc4ceb9fe1a85ec53
|
||||
k ^= k >> 33
|
||||
return k
|
||||
}
|
||||
|
||||
/*
|
||||
func rotl64(x uint64, r byte) uint64 {
|
||||
return (x << r) | (x >> (64 - r))
|
||||
}
|
||||
*/
|
||||
|
||||
// Sum128 returns the MurmurHash3 sum of data. It is equivalent to the
|
||||
// following sequence (without the extra burden and the extra allocation):
|
||||
// hasher := New128()
|
||||
// hasher.Write(data)
|
||||
// return hasher.Sum128()
|
||||
func Sum128(data []byte) (h1 uint64, h2 uint64) {
|
||||
d := &digest128{h1: 0, h2: 0}
|
||||
d.tail = d.bmix(data)
|
||||
d.clen = len(data)
|
||||
return d.Sum128()
|
||||
}
|
154
Godeps/_workspace/src/github.com/spaolacci/murmur3/murmur32.go
generated
vendored
154
Godeps/_workspace/src/github.com/spaolacci/murmur3/murmur32.go
generated
vendored
@ -1,154 +0,0 @@
|
||||
package murmur3
|
||||
|
||||
// http://code.google.com/p/guava-libraries/source/browse/guava/src/com/google/common/hash/Murmur3_32HashFunction.java
|
||||
|
||||
import (
|
||||
"hash"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Make sure interfaces are correctly implemented.
|
||||
var (
|
||||
_ hash.Hash = new(digest32)
|
||||
_ hash.Hash32 = new(digest32)
|
||||
)
|
||||
|
||||
const (
|
||||
c1_32 uint32 = 0xcc9e2d51
|
||||
c2_32 uint32 = 0x1b873593
|
||||
)
|
||||
|
||||
// digest32 represents a partial evaluation of a 32 bites hash.
|
||||
type digest32 struct {
|
||||
digest
|
||||
h1 uint32 // Unfinalized running hash.
|
||||
}
|
||||
|
||||
func New32() hash.Hash32 {
|
||||
d := new(digest32)
|
||||
d.bmixer = d
|
||||
d.Reset()
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *digest32) Size() int { return 4 }
|
||||
|
||||
func (d *digest32) reset() { d.h1 = 0 }
|
||||
|
||||
func (d *digest32) Sum(b []byte) []byte {
|
||||
h := d.h1
|
||||
return append(b, byte(h>>24), byte(h>>16), byte(h>>8), byte(h))
|
||||
}
|
||||
|
||||
// Digest as many blocks as possible.
|
||||
func (d *digest32) bmix(p []byte) (tail []byte) {
|
||||
h1 := d.h1
|
||||
|
||||
nblocks := len(p) / 4
|
||||
for i := 0; i < nblocks; i++ {
|
||||
k1 := *(*uint32)(unsafe.Pointer(&p[i*4]))
|
||||
|
||||
k1 *= c1_32
|
||||
k1 = (k1 << 15) | (k1 >> 17) // rotl32(k1, 15)
|
||||
k1 *= c2_32
|
||||
|
||||
h1 ^= k1
|
||||
h1 = (h1 << 13) | (h1 >> 19) // rotl32(h1, 13)
|
||||
h1 = h1*5 + 0xe6546b64
|
||||
}
|
||||
d.h1 = h1
|
||||
return p[nblocks*d.Size():]
|
||||
}
|
||||
|
||||
func (d *digest32) Sum32() (h1 uint32) {
|
||||
|
||||
h1 = d.h1
|
||||
|
||||
var k1 uint32
|
||||
switch len(d.tail) & 3 {
|
||||
case 3:
|
||||
k1 ^= uint32(d.tail[2]) << 16
|
||||
fallthrough
|
||||
case 2:
|
||||
k1 ^= uint32(d.tail[1]) << 8
|
||||
fallthrough
|
||||
case 1:
|
||||
k1 ^= uint32(d.tail[0])
|
||||
k1 *= c1_32
|
||||
k1 = (k1 << 15) | (k1 >> 17) // rotl32(k1, 15)
|
||||
k1 *= c2_32
|
||||
h1 ^= k1
|
||||
}
|
||||
|
||||
h1 ^= uint32(d.clen)
|
||||
|
||||
h1 ^= h1 >> 16
|
||||
h1 *= 0x85ebca6b
|
||||
h1 ^= h1 >> 13
|
||||
h1 *= 0xc2b2ae35
|
||||
h1 ^= h1 >> 16
|
||||
|
||||
return h1
|
||||
}
|
||||
|
||||
/*
|
||||
func rotl32(x uint32, r byte) uint32 {
|
||||
return (x << r) | (x >> (32 - r))
|
||||
}
|
||||
*/
|
||||
|
||||
// Sum32 returns the MurmurHash3 sum of data. It is equivalent to the
|
||||
// following sequence (without the extra burden and the extra allocation):
|
||||
// hasher := New32()
|
||||
// hasher.Write(data)
|
||||
// return hasher.Sum32()
|
||||
func Sum32(data []byte) uint32 {
|
||||
|
||||
var h1 uint32 = 0
|
||||
|
||||
nblocks := len(data) / 4
|
||||
var p uintptr
|
||||
if len(data) > 0 {
|
||||
p = uintptr(unsafe.Pointer(&data[0]))
|
||||
}
|
||||
p1 := p + uintptr(4*nblocks)
|
||||
for ; p < p1; p += 4 {
|
||||
k1 := *(*uint32)(unsafe.Pointer(p))
|
||||
|
||||
k1 *= c1_32
|
||||
k1 = (k1 << 15) | (k1 >> 17) // rotl32(k1, 15)
|
||||
k1 *= c2_32
|
||||
|
||||
h1 ^= k1
|
||||
h1 = (h1 << 13) | (h1 >> 19) // rotl32(h1, 13)
|
||||
h1 = h1*5 + 0xe6546b64
|
||||
}
|
||||
|
||||
tail := data[nblocks*4:]
|
||||
|
||||
var k1 uint32
|
||||
switch len(tail) & 3 {
|
||||
case 3:
|
||||
k1 ^= uint32(tail[2]) << 16
|
||||
fallthrough
|
||||
case 2:
|
||||
k1 ^= uint32(tail[1]) << 8
|
||||
fallthrough
|
||||
case 1:
|
||||
k1 ^= uint32(tail[0])
|
||||
k1 *= c1_32
|
||||
k1 = (k1 << 15) | (k1 >> 17) // rotl32(k1, 15)
|
||||
k1 *= c2_32
|
||||
h1 ^= k1
|
||||
}
|
||||
|
||||
h1 ^= uint32(len(data))
|
||||
|
||||
h1 ^= h1 >> 16
|
||||
h1 *= 0x85ebca6b
|
||||
h1 ^= h1 >> 13
|
||||
h1 *= 0xc2b2ae35
|
||||
h1 ^= h1 >> 16
|
||||
|
||||
return h1
|
||||
}
|
45
Godeps/_workspace/src/github.com/spaolacci/murmur3/murmur64.go
generated
vendored
45
Godeps/_workspace/src/github.com/spaolacci/murmur3/murmur64.go
generated
vendored
@ -1,45 +0,0 @@
|
||||
package murmur3
|
||||
|
||||
import (
|
||||
"hash"
|
||||
)
|
||||
|
||||
// Make sure interfaces are correctly implemented.
|
||||
var (
|
||||
_ hash.Hash = new(digest64)
|
||||
_ hash.Hash64 = new(digest64)
|
||||
_ bmixer = new(digest64)
|
||||
)
|
||||
|
||||
// digest64 is half a digest128.
|
||||
type digest64 digest128
|
||||
|
||||
func New64() hash.Hash64 {
|
||||
d := (*digest64)(New128().(*digest128))
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *digest64) Sum(b []byte) []byte {
|
||||
h1 := d.h1
|
||||
return append(b,
|
||||
byte(h1>>56), byte(h1>>48), byte(h1>>40), byte(h1>>32),
|
||||
byte(h1>>24), byte(h1>>16), byte(h1>>8), byte(h1))
|
||||
}
|
||||
|
||||
func (d *digest64) Sum64() uint64 {
|
||||
h1, _ := (*digest128)(d).Sum128()
|
||||
return h1
|
||||
}
|
||||
|
||||
// Sum64 returns the MurmurHash3 sum of data. It is equivalent to the
|
||||
// following sequence (without the extra burden and the extra allocation):
|
||||
// hasher := New64()
|
||||
// hasher.Write(data)
|
||||
// return hasher.Sum64()
|
||||
func Sum64(data []byte) uint64 {
|
||||
d := &digest128{h1: 0, h2: 0}
|
||||
d.tail = d.bmix(data)
|
||||
d.clen = len(data)
|
||||
h1, _ := d.Sum128()
|
||||
return h1
|
||||
}
|
229
Godeps/_workspace/src/github.com/spaolacci/murmur3/murmur_test.go
generated
vendored
229
Godeps/_workspace/src/github.com/spaolacci/murmur3/murmur_test.go
generated
vendored
@ -1,229 +0,0 @@
|
||||
package murmur3
|
||||
|
||||
import (
|
||||
"hash"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var data = []struct {
|
||||
h32 uint32
|
||||
h64_1 uint64
|
||||
h64_2 uint64
|
||||
s string
|
||||
}{
|
||||
{0x00000000, 0x0000000000000000, 0x0000000000000000, ""},
|
||||
{0x248bfa47, 0xcbd8a7b341bd9b02, 0x5b1e906a48ae1d19, "hello"},
|
||||
{0x149bbb7f, 0x342fac623a5ebc8e, 0x4cdcbc079642414d, "hello, world"},
|
||||
{0xe31e8a70, 0xb89e5988b737affc, 0x664fc2950231b2cb, "19 Jan 2038 at 3:14:07 AM"},
|
||||
{0xd5c48bfc, 0xcd99481f9ee902c9, 0x695da1a38987b6e7, "The quick brown fox jumps over the lazy dog."},
|
||||
}
|
||||
|
||||
func TestRef(t *testing.T) {
|
||||
for _, elem := range data {
|
||||
|
||||
var h32 hash.Hash32 = New32()
|
||||
h32.Write([]byte(elem.s))
|
||||
if v := h32.Sum32(); v != elem.h32 {
|
||||
t.Errorf("'%s': 0x%x (want 0x%x)", elem.s, v, elem.h32)
|
||||
}
|
||||
|
||||
if v := Sum32([]byte(elem.s)); v != elem.h32 {
|
||||
t.Errorf("'%s': 0x%x (want 0x%x)", elem.s, v, elem.h32)
|
||||
}
|
||||
|
||||
var h64 hash.Hash64 = New64()
|
||||
h64.Write([]byte(elem.s))
|
||||
if v := h64.Sum64(); v != elem.h64_1 {
|
||||
t.Errorf("'%s': 0x%x (want 0x%x)", elem.s, v, elem.h64_1)
|
||||
}
|
||||
|
||||
if v := Sum64([]byte(elem.s)); v != elem.h64_1 {
|
||||
t.Errorf("'%s': 0x%x (want 0x%x)", elem.s, v, elem.h64_1)
|
||||
}
|
||||
|
||||
var h128 Hash128 = New128()
|
||||
h128.Write([]byte(elem.s))
|
||||
if v1, v2 := h128.Sum128(); v1 != elem.h64_1 || v2 != elem.h64_2 {
|
||||
t.Errorf("'%s': 0x%x-0x%x (want 0x%x-0x%x)", elem.s, v1, v2, elem.h64_1, elem.h64_2)
|
||||
}
|
||||
|
||||
if v1, v2 := Sum128([]byte(elem.s)); v1 != elem.h64_1 || v2 != elem.h64_2 {
|
||||
t.Errorf("'%s': 0x%x-0x%x (want 0x%x-0x%x)", elem.s, v1, v2, elem.h64_1, elem.h64_2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIncremental(t *testing.T) {
|
||||
for _, elem := range data {
|
||||
h32 := New32()
|
||||
h128 := New128()
|
||||
for i, j, k := 0, 0, len(elem.s); i < k; i = j {
|
||||
j = 2*i + 3
|
||||
if j > k {
|
||||
j = k
|
||||
}
|
||||
s := elem.s[i:j]
|
||||
print(s + "|")
|
||||
h32.Write([]byte(s))
|
||||
h128.Write([]byte(s))
|
||||
}
|
||||
println()
|
||||
if v := h32.Sum32(); v != elem.h32 {
|
||||
t.Errorf("'%s': 0x%x (want 0x%x)", elem.s, v, elem.h32)
|
||||
}
|
||||
if v1, v2 := h128.Sum128(); v1 != elem.h64_1 || v2 != elem.h64_2 {
|
||||
t.Errorf("'%s': 0x%x-0x%x (want 0x%x-0x%x)", elem.s, v1, v2, elem.h64_1, elem.h64_2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---
|
||||
|
||||
func bench32(b *testing.B, length int) {
|
||||
buf := make([]byte, length)
|
||||
b.SetBytes(int64(length))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
Sum32(buf)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark32_1(b *testing.B) {
|
||||
bench32(b, 1)
|
||||
}
|
||||
func Benchmark32_2(b *testing.B) {
|
||||
bench32(b, 2)
|
||||
}
|
||||
func Benchmark32_4(b *testing.B) {
|
||||
bench32(b, 4)
|
||||
}
|
||||
func Benchmark32_8(b *testing.B) {
|
||||
bench32(b, 8)
|
||||
}
|
||||
func Benchmark32_16(b *testing.B) {
|
||||
bench32(b, 16)
|
||||
}
|
||||
func Benchmark32_32(b *testing.B) {
|
||||
bench32(b, 32)
|
||||
}
|
||||
func Benchmark32_64(b *testing.B) {
|
||||
bench32(b, 64)
|
||||
}
|
||||
func Benchmark32_128(b *testing.B) {
|
||||
bench32(b, 128)
|
||||
}
|
||||
func Benchmark32_256(b *testing.B) {
|
||||
bench32(b, 256)
|
||||
}
|
||||
func Benchmark32_512(b *testing.B) {
|
||||
bench32(b, 512)
|
||||
}
|
||||
func Benchmark32_1024(b *testing.B) {
|
||||
bench32(b, 1024)
|
||||
}
|
||||
func Benchmark32_2048(b *testing.B) {
|
||||
bench32(b, 2048)
|
||||
}
|
||||
func Benchmark32_4096(b *testing.B) {
|
||||
bench32(b, 4096)
|
||||
}
|
||||
func Benchmark32_8192(b *testing.B) {
|
||||
bench32(b, 8192)
|
||||
}
|
||||
|
||||
//---
|
||||
|
||||
func benchPartial32(b *testing.B, length int) {
|
||||
buf := make([]byte, length)
|
||||
b.SetBytes(int64(length))
|
||||
|
||||
start := (32 / 8) / 2
|
||||
chunks := 7
|
||||
k := length / chunks
|
||||
tail := (length - start) % k
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
hasher := New32()
|
||||
hasher.Write(buf[0:start])
|
||||
|
||||
for j := start; j+k <= length; j += k {
|
||||
hasher.Write(buf[j : j+k])
|
||||
}
|
||||
|
||||
hasher.Write(buf[length-tail:])
|
||||
hasher.Sum32()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPartial32_8(b *testing.B) {
|
||||
benchPartial32(b, 8)
|
||||
}
|
||||
func BenchmarkPartial32_16(b *testing.B) {
|
||||
benchPartial32(b, 16)
|
||||
}
|
||||
func BenchmarkPartial32_32(b *testing.B) {
|
||||
benchPartial32(b, 32)
|
||||
}
|
||||
func BenchmarkPartial32_64(b *testing.B) {
|
||||
benchPartial32(b, 64)
|
||||
}
|
||||
func BenchmarkPartial32_128(b *testing.B) {
|
||||
benchPartial32(b, 128)
|
||||
}
|
||||
|
||||
//---
|
||||
|
||||
func bench128(b *testing.B, length int) {
|
||||
buf := make([]byte, length)
|
||||
b.SetBytes(int64(length))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
Sum128(buf)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark128_1(b *testing.B) {
|
||||
bench128(b, 1)
|
||||
}
|
||||
func Benchmark128_2(b *testing.B) {
|
||||
bench128(b, 2)
|
||||
}
|
||||
func Benchmark128_4(b *testing.B) {
|
||||
bench128(b, 4)
|
||||
}
|
||||
func Benchmark128_8(b *testing.B) {
|
||||
bench128(b, 8)
|
||||
}
|
||||
func Benchmark128_16(b *testing.B) {
|
||||
bench128(b, 16)
|
||||
}
|
||||
func Benchmark128_32(b *testing.B) {
|
||||
bench128(b, 32)
|
||||
}
|
||||
func Benchmark128_64(b *testing.B) {
|
||||
bench128(b, 64)
|
||||
}
|
||||
func Benchmark128_128(b *testing.B) {
|
||||
bench128(b, 128)
|
||||
}
|
||||
func Benchmark128_256(b *testing.B) {
|
||||
bench128(b, 256)
|
||||
}
|
||||
func Benchmark128_512(b *testing.B) {
|
||||
bench128(b, 512)
|
||||
}
|
||||
func Benchmark128_1024(b *testing.B) {
|
||||
bench128(b, 1024)
|
||||
}
|
||||
func Benchmark128_2048(b *testing.B) {
|
||||
bench128(b, 2048)
|
||||
}
|
||||
func Benchmark128_4096(b *testing.B) {
|
||||
bench128(b, 4096)
|
||||
}
|
||||
func Benchmark128_8192(b *testing.B) {
|
||||
bench128(b, 8192)
|
||||
}
|
||||
|
||||
//---
|
230
Godeps/_workspace/src/github.com/tchap/go-patricia/patricia/children.go
generated
vendored
230
Godeps/_workspace/src/github.com/tchap/go-patricia/patricia/children.go
generated
vendored
@ -1,230 +0,0 @@
|
||||
// Copyright (c) 2014 The go-patricia AUTHORS
|
||||
//
|
||||
// Use of this source code is governed by The MIT License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package patricia
|
||||
|
||||
// Max prefix length that is kept in a single trie node.
|
||||
var MaxPrefixPerNode = 10
|
||||
|
||||
// Max children to keep in a node in the sparse mode.
|
||||
const MaxChildrenPerSparseNode = 8
|
||||
|
||||
type childList interface {
|
||||
length() int
|
||||
head() *Trie
|
||||
add(child *Trie) childList
|
||||
replace(b byte, child *Trie)
|
||||
remove(child *Trie)
|
||||
next(b byte) *Trie
|
||||
walk(prefix *Prefix, visitor VisitorFunc) error
|
||||
}
|
||||
|
||||
type sparseChildList struct {
|
||||
children []*Trie
|
||||
}
|
||||
|
||||
func newSparseChildList() childList {
|
||||
return &sparseChildList{
|
||||
children: make([]*Trie, 0, MaxChildrenPerSparseNode),
|
||||
}
|
||||
}
|
||||
|
||||
func (list *sparseChildList) length() int {
|
||||
return len(list.children)
|
||||
}
|
||||
|
||||
func (list *sparseChildList) head() *Trie {
|
||||
return list.children[0]
|
||||
}
|
||||
|
||||
func (list *sparseChildList) add(child *Trie) childList {
|
||||
// Search for an empty spot and insert the child if possible.
|
||||
if len(list.children) != cap(list.children) {
|
||||
list.children = append(list.children, child)
|
||||
return list
|
||||
}
|
||||
|
||||
// Otherwise we have to transform to the dense list type.
|
||||
return newDenseChildList(list, child)
|
||||
}
|
||||
|
||||
func (list *sparseChildList) replace(b byte, child *Trie) {
|
||||
// Seek the child and replace it.
|
||||
for i, node := range list.children {
|
||||
if node.prefix[0] == b {
|
||||
list.children[i] = child
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (list *sparseChildList) remove(child *Trie) {
|
||||
for i, node := range list.children {
|
||||
if node.prefix[0] == child.prefix[0] {
|
||||
list.children = append(list.children[:i], list.children[i+1:]...)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// This is not supposed to be reached.
|
||||
panic("removing non-existent child")
|
||||
}
|
||||
|
||||
func (list *sparseChildList) next(b byte) *Trie {
|
||||
for _, child := range list.children {
|
||||
if child.prefix[0] == b {
|
||||
return child
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (list *sparseChildList) walk(prefix *Prefix, visitor VisitorFunc) error {
|
||||
for _, child := range list.children {
|
||||
*prefix = append(*prefix, child.prefix...)
|
||||
if child.item != nil {
|
||||
err := visitor(*prefix, child.item)
|
||||
if err != nil {
|
||||
if err == SkipSubtree {
|
||||
*prefix = (*prefix)[:len(*prefix)-len(child.prefix)]
|
||||
continue
|
||||
}
|
||||
*prefix = (*prefix)[:len(*prefix)-len(child.prefix)]
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err := child.children.walk(prefix, visitor)
|
||||
*prefix = (*prefix)[:len(*prefix)-len(child.prefix)]
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type denseChildList struct {
|
||||
min int
|
||||
max int
|
||||
children []*Trie
|
||||
}
|
||||
|
||||
func newDenseChildList(list *sparseChildList, child *Trie) childList {
|
||||
var (
|
||||
min int = 255
|
||||
max int = 0
|
||||
)
|
||||
for _, child := range list.children {
|
||||
b := int(child.prefix[0])
|
||||
if b < min {
|
||||
min = b
|
||||
}
|
||||
if b > max {
|
||||
max = b
|
||||
}
|
||||
}
|
||||
|
||||
b := int(child.prefix[0])
|
||||
if b < min {
|
||||
min = b
|
||||
}
|
||||
if b > max {
|
||||
max = b
|
||||
}
|
||||
|
||||
children := make([]*Trie, max-min+1)
|
||||
for _, child := range list.children {
|
||||
children[int(child.prefix[0])-min] = child
|
||||
}
|
||||
children[int(child.prefix[0])-min] = child
|
||||
|
||||
return &denseChildList{min, max, children}
|
||||
}
|
||||
|
||||
func (list *denseChildList) length() int {
|
||||
return list.max - list.min + 1
|
||||
}
|
||||
|
||||
func (list *denseChildList) head() *Trie {
|
||||
return list.children[0]
|
||||
}
|
||||
|
||||
func (list *denseChildList) add(child *Trie) childList {
|
||||
b := int(child.prefix[0])
|
||||
|
||||
switch {
|
||||
case list.min <= b && b <= list.max:
|
||||
if list.children[b-list.min] != nil {
|
||||
panic("dense child list collision detected")
|
||||
}
|
||||
list.children[b-list.min] = child
|
||||
|
||||
case b < list.min:
|
||||
children := make([]*Trie, list.max-b+1)
|
||||
children[0] = child
|
||||
copy(children[list.min-b:], list.children)
|
||||
list.children = children
|
||||
list.min = b
|
||||
|
||||
default: // b > list.max
|
||||
children := make([]*Trie, b-list.min+1)
|
||||
children[b-list.min] = child
|
||||
copy(children, list.children)
|
||||
list.children = children
|
||||
list.max = b
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
func (list *denseChildList) replace(b byte, child *Trie) {
|
||||
list.children[int(b)-list.min] = nil
|
||||
list.children[int(child.prefix[0])-list.min] = child
|
||||
}
|
||||
|
||||
func (list *denseChildList) remove(child *Trie) {
|
||||
i := int(child.prefix[0]) - list.min
|
||||
if list.children[i] == nil {
|
||||
// This is not supposed to be reached.
|
||||
panic("removing non-existent child")
|
||||
}
|
||||
list.children[i] = nil
|
||||
}
|
||||
|
||||
func (list *denseChildList) next(b byte) *Trie {
|
||||
i := int(b)
|
||||
if i < list.min || list.max < i {
|
||||
return nil
|
||||
}
|
||||
return list.children[i-list.min]
|
||||
}
|
||||
|
||||
func (list *denseChildList) walk(prefix *Prefix, visitor VisitorFunc) error {
|
||||
for _, child := range list.children {
|
||||
if child == nil {
|
||||
continue
|
||||
}
|
||||
*prefix = append(*prefix, child.prefix...)
|
||||
if child.item != nil {
|
||||
if err := visitor(*prefix, child.item); err != nil {
|
||||
if err == SkipSubtree {
|
||||
*prefix = (*prefix)[:len(*prefix)-len(child.prefix)]
|
||||
continue
|
||||
}
|
||||
*prefix = (*prefix)[:len(*prefix)-len(child.prefix)]
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err := child.children.walk(prefix, visitor)
|
||||
*prefix = (*prefix)[:len(*prefix)-len(child.prefix)]
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
432
Godeps/_workspace/src/github.com/tchap/go-patricia/patricia/patricia.go
generated
vendored
432
Godeps/_workspace/src/github.com/tchap/go-patricia/patricia/patricia.go
generated
vendored
@ -1,432 +0,0 @@
|
||||
// Copyright (c) 2014 The go-patricia AUTHORS
|
||||
//
|
||||
// Use of this source code is governed by The MIT License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package patricia
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Trie
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type (
|
||||
Prefix []byte
|
||||
Item interface{}
|
||||
VisitorFunc func(prefix Prefix, item Item) error
|
||||
)
|
||||
|
||||
// Trie is a generic patricia trie that allows fast retrieval of items by prefix.
|
||||
// and other funky stuff.
|
||||
//
|
||||
// Trie is not thread-safe.
|
||||
type Trie struct {
|
||||
prefix Prefix
|
||||
item Item
|
||||
|
||||
children childList
|
||||
}
|
||||
|
||||
// Public API ------------------------------------------------------------------
|
||||
|
||||
// Trie constructor.
|
||||
func NewTrie() *Trie {
|
||||
return &Trie{
|
||||
children: newSparseChildList(),
|
||||
}
|
||||
}
|
||||
|
||||
// Item returns the item stored in the root of this trie.
|
||||
func (trie *Trie) Item() Item {
|
||||
return trie.item
|
||||
}
|
||||
|
||||
// Insert inserts a new item into the trie using the given prefix. Insert does
|
||||
// not replace existing items. It returns false if an item was already in place.
|
||||
func (trie *Trie) Insert(key Prefix, item Item) (inserted bool) {
|
||||
return trie.put(key, item, false)
|
||||
}
|
||||
|
||||
// Set works much like Insert, but it always sets the item, possibly replacing
|
||||
// the item previously inserted.
|
||||
func (trie *Trie) Set(key Prefix, item Item) {
|
||||
trie.put(key, item, true)
|
||||
}
|
||||
|
||||
// Get returns the item located at key.
|
||||
//
|
||||
// This method is a bit dangerous, because Get can as well end up in an internal
|
||||
// node that is not really representing any user-defined value. So when nil is
|
||||
// a valid value being used, it is not possible to tell if the value was inserted
|
||||
// into the tree by the user or not. A possible workaround for this is not to use
|
||||
// nil interface as a valid value, even using zero value of any type is enough
|
||||
// to prevent this bad behaviour.
|
||||
func (trie *Trie) Get(key Prefix) (item Item) {
|
||||
_, node, found, leftover := trie.findSubtree(key)
|
||||
if !found || len(leftover) != 0 {
|
||||
return nil
|
||||
}
|
||||
return node.item
|
||||
}
|
||||
|
||||
// Match returns what Get(prefix) != nil would return. The same warning as for
|
||||
// Get applies here as well.
|
||||
func (trie *Trie) Match(prefix Prefix) (matchedExactly bool) {
|
||||
return trie.Get(prefix) != nil
|
||||
}
|
||||
|
||||
// MatchSubtree returns true when there is a subtree representing extensions
|
||||
// to key, that is if there are any keys in the tree which have key as prefix.
|
||||
func (trie *Trie) MatchSubtree(key Prefix) (matched bool) {
|
||||
_, _, matched, _ = trie.findSubtree(key)
|
||||
return
|
||||
}
|
||||
|
||||
// Visit calls visitor on every node containing a non-nil item.
|
||||
//
|
||||
// If an error is returned from visitor, the function stops visiting the tree
|
||||
// and returns that error, unless it is a special error - SkipSubtree. In that
|
||||
// case Visit skips the subtree represented by the current node and continues
|
||||
// elsewhere.
|
||||
func (trie *Trie) Visit(visitor VisitorFunc) error {
|
||||
return trie.walk(nil, visitor)
|
||||
}
|
||||
|
||||
// VisitSubtree works much like Visit, but it only visits nodes matching prefix.
|
||||
func (trie *Trie) VisitSubtree(prefix Prefix, visitor VisitorFunc) error {
|
||||
// Nil prefix not allowed.
|
||||
if prefix == nil {
|
||||
panic(ErrNilPrefix)
|
||||
}
|
||||
|
||||
// Empty trie must be handled explicitly.
|
||||
if trie.prefix == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Locate the relevant subtree.
|
||||
_, root, found, leftover := trie.findSubtree(prefix)
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
prefix = append(prefix, leftover...)
|
||||
|
||||
// Visit it.
|
||||
return root.walk(prefix, visitor)
|
||||
}
|
||||
|
||||
// VisitPrefixes visits only nodes that represent prefixes of key.
|
||||
// To say the obvious, returning SkipSubtree from visitor makes no sense here.
|
||||
func (trie *Trie) VisitPrefixes(key Prefix, visitor VisitorFunc) error {
|
||||
// Nil key not allowed.
|
||||
if key == nil {
|
||||
panic(ErrNilPrefix)
|
||||
}
|
||||
|
||||
// Empty trie must be handled explicitly.
|
||||
if trie.prefix == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Walk the path matching key prefixes.
|
||||
node := trie
|
||||
prefix := key
|
||||
offset := 0
|
||||
for {
|
||||
// Compute what part of prefix matches.
|
||||
common := node.longestCommonPrefixLength(key)
|
||||
key = key[common:]
|
||||
offset += common
|
||||
|
||||
// Partial match means that there is no subtree matching prefix.
|
||||
if common < len(node.prefix) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Call the visitor.
|
||||
if item := node.item; item != nil {
|
||||
if err := visitor(prefix[:offset], item); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(key) == 0 {
|
||||
// This node represents key, we are finished.
|
||||
return nil
|
||||
}
|
||||
|
||||
// There is some key suffix left, move to the children.
|
||||
child := node.children.next(key[0])
|
||||
if child == nil {
|
||||
// There is nowhere to continue, return.
|
||||
return nil
|
||||
}
|
||||
|
||||
node = child
|
||||
}
|
||||
}
|
||||
|
||||
// Delete deletes the item represented by the given prefix.
|
||||
//
|
||||
// True is returned if the matching node was found and deleted.
|
||||
func (trie *Trie) Delete(key Prefix) (deleted bool) {
|
||||
// Nil prefix not allowed.
|
||||
if key == nil {
|
||||
panic(ErrNilPrefix)
|
||||
}
|
||||
|
||||
// Empty trie must be handled explicitly.
|
||||
if trie.prefix == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Find the relevant node.
|
||||
parent, node, _, leftover := trie.findSubtree(key)
|
||||
if len(leftover) != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// If the item is already set to nil, there is nothing to do.
|
||||
if node.item == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Delete the item.
|
||||
node.item = nil
|
||||
|
||||
// Compact since that might be possible now.
|
||||
if compacted := node.compact(); compacted != node {
|
||||
if parent == nil {
|
||||
*node = *compacted
|
||||
} else {
|
||||
parent.children.replace(node.prefix[0], compacted)
|
||||
*parent = *parent.compact()
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// DeleteSubtree finds the subtree exactly matching prefix and deletes it.
|
||||
//
|
||||
// True is returned if the subtree was found and deleted.
|
||||
func (trie *Trie) DeleteSubtree(prefix Prefix) (deleted bool) {
|
||||
// Nil prefix not allowed.
|
||||
if prefix == nil {
|
||||
panic(ErrNilPrefix)
|
||||
}
|
||||
|
||||
// Empty trie must be handled explicitly.
|
||||
if trie.prefix == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Locate the relevant subtree.
|
||||
parent, root, found, _ := trie.findSubtree(prefix)
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
|
||||
// If we are in the root of the trie, reset the trie.
|
||||
if parent == nil {
|
||||
root.prefix = nil
|
||||
root.children = newSparseChildList()
|
||||
return true
|
||||
}
|
||||
|
||||
// Otherwise remove the root node from its parent.
|
||||
parent.children.remove(root)
|
||||
return true
|
||||
}
|
||||
|
||||
// Internal helper methods -----------------------------------------------------
|
||||
|
||||
func (trie *Trie) put(key Prefix, item Item, replace bool) (inserted bool) {
|
||||
// Nil prefix not allowed.
|
||||
if key == nil {
|
||||
panic(ErrNilPrefix)
|
||||
}
|
||||
|
||||
var (
|
||||
common int
|
||||
node *Trie = trie
|
||||
child *Trie
|
||||
)
|
||||
|
||||
if node.prefix == nil {
|
||||
if len(key) <= MaxPrefixPerNode {
|
||||
node.prefix = key
|
||||
goto InsertItem
|
||||
}
|
||||
node.prefix = key[:MaxPrefixPerNode]
|
||||
key = key[MaxPrefixPerNode:]
|
||||
goto AppendChild
|
||||
}
|
||||
|
||||
for {
|
||||
// Compute the longest common prefix length.
|
||||
common = node.longestCommonPrefixLength(key)
|
||||
key = key[common:]
|
||||
|
||||
// Only a part matches, split.
|
||||
if common < len(node.prefix) {
|
||||
goto SplitPrefix
|
||||
}
|
||||
|
||||
// common == len(node.prefix) since never (common > len(node.prefix))
|
||||
// common == len(former key) <-> 0 == len(key)
|
||||
// -> former key == node.prefix
|
||||
if len(key) == 0 {
|
||||
goto InsertItem
|
||||
}
|
||||
|
||||
// Check children for matching prefix.
|
||||
child = node.children.next(key[0])
|
||||
if child == nil {
|
||||
goto AppendChild
|
||||
}
|
||||
node = child
|
||||
}
|
||||
|
||||
SplitPrefix:
|
||||
// Split the prefix if necessary.
|
||||
child = new(Trie)
|
||||
*child = *node
|
||||
*node = *NewTrie()
|
||||
node.prefix = child.prefix[:common]
|
||||
child.prefix = child.prefix[common:]
|
||||
child = child.compact()
|
||||
node.children = node.children.add(child)
|
||||
|
||||
AppendChild:
|
||||
// Keep appending children until whole prefix is inserted.
|
||||
// This loop starts with empty node.prefix that needs to be filled.
|
||||
for len(key) != 0 {
|
||||
child := NewTrie()
|
||||
if len(key) <= MaxPrefixPerNode {
|
||||
child.prefix = key
|
||||
node.children = node.children.add(child)
|
||||
node = child
|
||||
goto InsertItem
|
||||
} else {
|
||||
child.prefix = key[:MaxPrefixPerNode]
|
||||
key = key[MaxPrefixPerNode:]
|
||||
node.children = node.children.add(child)
|
||||
node = child
|
||||
}
|
||||
}
|
||||
|
||||
InsertItem:
|
||||
// Try to insert the item if possible.
|
||||
if replace || node.item == nil {
|
||||
node.item = item
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (trie *Trie) compact() *Trie {
|
||||
// Only a node with a single child can be compacted.
|
||||
if trie.children.length() != 1 {
|
||||
return trie
|
||||
}
|
||||
|
||||
child := trie.children.head()
|
||||
|
||||
// If any item is set, we cannot compact since we want to retain
|
||||
// the ability to do searching by key. This makes compaction less usable,
|
||||
// but that simply cannot be avoided.
|
||||
if trie.item != nil || child.item != nil {
|
||||
return trie
|
||||
}
|
||||
|
||||
// Make sure the combined prefixes fit into a single node.
|
||||
if len(trie.prefix)+len(child.prefix) > MaxPrefixPerNode {
|
||||
return trie
|
||||
}
|
||||
|
||||
// Concatenate the prefixes, move the items.
|
||||
child.prefix = append(trie.prefix, child.prefix...)
|
||||
if trie.item != nil {
|
||||
child.item = trie.item
|
||||
}
|
||||
|
||||
return child
|
||||
}
|
||||
|
||||
func (trie *Trie) findSubtree(prefix Prefix) (parent *Trie, root *Trie, found bool, leftover Prefix) {
|
||||
// Find the subtree matching prefix.
|
||||
root = trie
|
||||
for {
|
||||
// Compute what part of prefix matches.
|
||||
common := root.longestCommonPrefixLength(prefix)
|
||||
prefix = prefix[common:]
|
||||
|
||||
// We used up the whole prefix, subtree found.
|
||||
if len(prefix) == 0 {
|
||||
found = true
|
||||
leftover = root.prefix[common:]
|
||||
return
|
||||
}
|
||||
|
||||
// Partial match means that there is no subtree matching prefix.
|
||||
if common < len(root.prefix) {
|
||||
leftover = root.prefix[common:]
|
||||
return
|
||||
}
|
||||
|
||||
// There is some prefix left, move to the children.
|
||||
child := root.children.next(prefix[0])
|
||||
if child == nil {
|
||||
// There is nowhere to continue, there is no subtree matching prefix.
|
||||
return
|
||||
}
|
||||
|
||||
parent = root
|
||||
root = child
|
||||
}
|
||||
}
|
||||
|
||||
func (trie *Trie) walk(actualRootPrefix Prefix, visitor VisitorFunc) error {
|
||||
var prefix Prefix
|
||||
// Allocate a bit more space for prefix at the beginning.
|
||||
if actualRootPrefix == nil {
|
||||
prefix = make(Prefix, 32+len(trie.prefix))
|
||||
copy(prefix, trie.prefix)
|
||||
prefix = prefix[:len(trie.prefix)]
|
||||
} else {
|
||||
prefix = make(Prefix, 32+len(actualRootPrefix))
|
||||
copy(prefix, actualRootPrefix)
|
||||
prefix = prefix[:len(actualRootPrefix)]
|
||||
}
|
||||
|
||||
// Visit the root first. Not that this works for empty trie as well since
|
||||
// in that case item == nil && len(children) == 0.
|
||||
if trie.item != nil {
|
||||
if err := visitor(prefix, trie.item); err != nil {
|
||||
if err == SkipSubtree {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Then continue to the children.
|
||||
return trie.children.walk(&prefix, visitor)
|
||||
}
|
||||
|
||||
func (trie *Trie) longestCommonPrefixLength(prefix Prefix) (i int) {
|
||||
for ; i < len(prefix) && i < len(trie.prefix) && prefix[i] == trie.prefix[i]; i++ {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Errors ----------------------------------------------------------------------
|
||||
|
||||
var (
|
||||
SkipSubtree = errors.New("Skip this subtree")
|
||||
ErrNilPrefix = errors.New("Nil prefix passed into a method call")
|
||||
)
|
161
Godeps/_workspace/src/github.com/tchap/go-patricia/patricia/patricia_dense_test.go
generated
vendored
161
Godeps/_workspace/src/github.com/tchap/go-patricia/patricia/patricia_dense_test.go
generated
vendored
@ -1,161 +0,0 @@
|
||||
// Copyright (c) 2014 The go-patricia AUTHORS
|
||||
//
|
||||
// Use of this source code is governed by The MIT License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package patricia
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Tests -----------------------------------------------------------------------
|
||||
|
||||
func TestTrie_InsertDense(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"aba", 0, success},
|
||||
{"abb", 1, success},
|
||||
{"abc", 2, success},
|
||||
{"abd", 3, success},
|
||||
{"abe", 4, success},
|
||||
{"abf", 5, success},
|
||||
{"abg", 6, success},
|
||||
{"abh", 7, success},
|
||||
{"abi", 8, success},
|
||||
{"abj", 9, success},
|
||||
{"abk", 0, success},
|
||||
{"abl", 1, success},
|
||||
{"abm", 2, success},
|
||||
{"abn", 3, success},
|
||||
{"abo", 4, success},
|
||||
{"abp", 5, success},
|
||||
{"abq", 6, success},
|
||||
{"abr", 7, success},
|
||||
{"abs", 8, success},
|
||||
{"abt", 9, success},
|
||||
{"abu", 0, success},
|
||||
{"abv", 1, success},
|
||||
{"abw", 2, success},
|
||||
{"abx", 3, success},
|
||||
{"aby", 4, success},
|
||||
{"abz", 5, success},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_InsertDensePreceeding(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
start := byte(70)
|
||||
// create a dense node
|
||||
for i := byte(0); i <= MaxChildrenPerSparseNode; i++ {
|
||||
if !trie.Insert(Prefix([]byte{start + i}), true) {
|
||||
t.Errorf("insert failed, prefix=%v", start+i)
|
||||
}
|
||||
}
|
||||
// insert some preceeding keys
|
||||
for i := byte(1); i < start; i *= i + 1 {
|
||||
if !trie.Insert(Prefix([]byte{start - i}), true) {
|
||||
t.Errorf("insert failed, prefix=%v", start-i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_InsertDenseDuplicatePrefixes(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"aba", 0, success},
|
||||
{"abb", 1, success},
|
||||
{"abc", 2, success},
|
||||
{"abd", 3, success},
|
||||
{"abe", 4, success},
|
||||
{"abf", 5, success},
|
||||
{"abg", 6, success},
|
||||
{"abh", 7, success},
|
||||
{"abi", 8, success},
|
||||
{"abj", 9, success},
|
||||
{"abk", 0, success},
|
||||
{"abl", 1, success},
|
||||
{"abm", 2, success},
|
||||
{"abn", 3, success},
|
||||
{"abo", 4, success},
|
||||
{"abp", 5, success},
|
||||
{"abq", 6, success},
|
||||
{"abr", 7, success},
|
||||
{"abs", 8, success},
|
||||
{"abt", 9, success},
|
||||
{"abu", 0, success},
|
||||
{"abv", 1, success},
|
||||
{"abw", 2, success},
|
||||
{"abx", 3, success},
|
||||
{"aby", 4, success},
|
||||
{"abz", 5, success},
|
||||
{"aba", 0, failure},
|
||||
{"abb", 1, failure},
|
||||
{"abc", 2, failure},
|
||||
{"abd", 3, failure},
|
||||
{"abe", 4, failure},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_DeleteDense(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"aba", 0, success},
|
||||
{"abb", 1, success},
|
||||
{"abc", 2, success},
|
||||
{"abd", 3, success},
|
||||
{"abe", 4, success},
|
||||
{"abf", 5, success},
|
||||
{"abg", 6, success},
|
||||
{"abh", 7, success},
|
||||
{"abi", 8, success},
|
||||
{"abj", 9, success},
|
||||
{"abk", 0, success},
|
||||
{"abl", 1, success},
|
||||
{"abm", 2, success},
|
||||
{"abn", 3, success},
|
||||
{"abo", 4, success},
|
||||
{"abp", 5, success},
|
||||
{"abq", 6, success},
|
||||
{"abr", 7, success},
|
||||
{"abs", 8, success},
|
||||
{"abt", 9, success},
|
||||
{"abu", 0, success},
|
||||
{"abv", 1, success},
|
||||
{"abw", 2, success},
|
||||
{"abx", 3, success},
|
||||
{"aby", 4, success},
|
||||
{"abz", 5, success},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("DELETE word=%v, success=%v", v.key, v.retVal)
|
||||
if ok := trie.Delete([]byte(v.key)); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
}
|
659
Godeps/_workspace/src/github.com/tchap/go-patricia/patricia/patricia_sparse_test.go
generated
vendored
659
Godeps/_workspace/src/github.com/tchap/go-patricia/patricia/patricia_sparse_test.go
generated
vendored
@ -1,659 +0,0 @@
|
||||
// Copyright (c) 2014 The go-patricia AUTHORS
|
||||
//
|
||||
// Use of this source code is governed by The MIT License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package patricia
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
success = true
|
||||
failure = false
|
||||
)
|
||||
|
||||
type testData struct {
|
||||
key string
|
||||
value interface{}
|
||||
retVal bool
|
||||
}
|
||||
|
||||
// Tests -----------------------------------------------------------------------
|
||||
|
||||
func TestTrie_InsertDifferentPrefixes(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"Pepaneeeeeeeeeeeeee", "Pepan Zdepan", success},
|
||||
{"Honzooooooooooooooo", "Honza Novak", success},
|
||||
{"Jenikuuuuuuuuuuuuuu", "Jenik Poustevnicek", success},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_InsertDuplicatePrefixes(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"Pepan", "Pepan Zdepan", success},
|
||||
{"Pepan", "Pepan Zdepan", failure},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_InsertVariousPrefixes(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"Pepan", "Pepan Zdepan", success},
|
||||
{"Pepin", "Pepin Omacka", success},
|
||||
{"Honza", "Honza Novak", success},
|
||||
{"Jenik", "Jenik Poustevnicek", success},
|
||||
{"Pepan", "Pepan Dupan", failure},
|
||||
{"Karel", "Karel Pekar", success},
|
||||
{"Jenik", "Jenik Poustevnicek", failure},
|
||||
{"Pepanek", "Pepanek Zemlicka", success},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_InsertAndMatchPrefix(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
t.Log("INSERT prefix=by week")
|
||||
trie.Insert(Prefix("by week"), 2)
|
||||
t.Log("INSERT prefix=by")
|
||||
trie.Insert(Prefix("by"), 1)
|
||||
|
||||
if !trie.Match(Prefix("by")) {
|
||||
t.Error("MATCH prefix=by, expected=true, got=false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_SetGet(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"Pepan", "Pepan Zdepan", success},
|
||||
{"Pepin", "Pepin Omacka", success},
|
||||
{"Honza", "Honza Novak", success},
|
||||
{"Jenik", "Jenik Poustevnicek", success},
|
||||
{"Pepan", "Pepan Dupan", failure},
|
||||
{"Karel", "Karel Pekar", success},
|
||||
{"Jenik", "Jenik Poustevnicek", failure},
|
||||
{"Pepanek", "Pepanek Zemlicka", success},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("SET %q to 10", v.key)
|
||||
trie.Set(Prefix(v.key), 10)
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
value := trie.Get(Prefix(v.key))
|
||||
t.Logf("GET %q => %v", v.key, value)
|
||||
if value.(int) != 10 {
|
||||
t.Errorf("Unexpected return value, != 10", value)
|
||||
}
|
||||
}
|
||||
|
||||
if value := trie.Get(Prefix("random crap")); value != nil {
|
||||
t.Errorf("Unexpected return value, %v != <nil>", value)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_Match(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"Pepan", "Pepan Zdepan", success},
|
||||
{"Pepin", "Pepin Omacka", success},
|
||||
{"Honza", "Honza Novak", success},
|
||||
{"Jenik", "Jenik Poustevnicek", success},
|
||||
{"Pepan", "Pepan Dupan", failure},
|
||||
{"Karel", "Karel Pekar", success},
|
||||
{"Jenik", "Jenik Poustevnicek", failure},
|
||||
{"Pepanek", "Pepanek Zemlicka", success},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
matched := trie.Match(Prefix(v.key))
|
||||
t.Logf("MATCH %q => %v", v.key, matched)
|
||||
if !matched {
|
||||
t.Errorf("Inserted key %q was not matched", v.key)
|
||||
}
|
||||
}
|
||||
|
||||
if trie.Match(Prefix("random crap")) {
|
||||
t.Errorf("Key that was not inserted matched: %q", "random crap")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_MatchFalsePositive(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
if ok := trie.Insert(Prefix("A"), 1); !ok {
|
||||
t.Fatal("INSERT prefix=A, item=1 not ok")
|
||||
}
|
||||
|
||||
resultMatchSubtree := trie.MatchSubtree(Prefix("A extra"))
|
||||
resultMatch := trie.Match(Prefix("A extra"))
|
||||
|
||||
if resultMatchSubtree != false {
|
||||
t.Error("MatchSubtree returned false positive")
|
||||
}
|
||||
|
||||
if resultMatch != false {
|
||||
t.Error("Match returned false positive")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_MatchSubtree(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"Pepan", "Pepan Zdepan", success},
|
||||
{"Pepin", "Pepin Omacka", success},
|
||||
{"Honza", "Honza Novak", success},
|
||||
{"Jenik", "Jenik Poustevnicek", success},
|
||||
{"Pepan", "Pepan Dupan", failure},
|
||||
{"Karel", "Karel Pekar", success},
|
||||
{"Jenik", "Jenik Poustevnicek", failure},
|
||||
{"Pepanek", "Pepanek Zemlicka", success},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
key := Prefix(v.key[:3])
|
||||
matched := trie.MatchSubtree(key)
|
||||
t.Logf("MATCH_SUBTREE %q => %v", key, matched)
|
||||
if !matched {
|
||||
t.Errorf("Subtree %q was not matched", v.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_Visit(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"Pepa", 0, success},
|
||||
{"Pepa Zdepa", 1, success},
|
||||
{"Pepa Kuchar", 2, success},
|
||||
{"Honza", 3, success},
|
||||
{"Jenik", 4, success},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
|
||||
t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
|
||||
if err := trie.Visit(func(prefix Prefix, item Item) error {
|
||||
name := data[item.(int)].key
|
||||
t.Logf("VISITING prefix=%q, item=%v", prefix, item)
|
||||
if !strings.HasPrefix(string(prefix), name) {
|
||||
t.Errorf("Unexpected prefix encountered, %q not a prefix of %q", prefix, name)
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_VisitSkipSubtree(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"Pepa", 0, success},
|
||||
{"Pepa Zdepa", 1, success},
|
||||
{"Pepa Kuchar", 2, success},
|
||||
{"Honza", 3, success},
|
||||
{"Jenik", 4, success},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
|
||||
t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
|
||||
if err := trie.Visit(func(prefix Prefix, item Item) error {
|
||||
t.Logf("VISITING prefix=%q, item=%v", prefix, item)
|
||||
if item.(int) == 0 {
|
||||
t.Logf("SKIP %q", prefix)
|
||||
return SkipSubtree
|
||||
}
|
||||
if strings.HasPrefix(string(prefix), "Pepa") {
|
||||
t.Errorf("Unexpected prefix encountered, %q", prefix)
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_VisitReturnError(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"Pepa", 0, success},
|
||||
{"Pepa Zdepa", 1, success},
|
||||
{"Pepa Kuchar", 2, success},
|
||||
{"Honza", 3, success},
|
||||
{"Jenik", 4, success},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
|
||||
t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
|
||||
someErr := errors.New("Something exploded")
|
||||
if err := trie.Visit(func(prefix Prefix, item Item) error {
|
||||
t.Logf("VISITING prefix=%q, item=%v", prefix, item)
|
||||
if item.(int) == 0 {
|
||||
return someErr
|
||||
}
|
||||
if item.(int) != 0 {
|
||||
t.Errorf("Unexpected prefix encountered, %q", prefix)
|
||||
}
|
||||
return nil
|
||||
}); err != nil && err != someErr {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_VisitSubtree(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"Pepa", 0, success},
|
||||
{"Pepa Zdepa", 1, success},
|
||||
{"Pepa Kuchar", 2, success},
|
||||
{"Honza", 3, success},
|
||||
{"Jenik", 4, success},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
|
||||
t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
|
||||
var counter int
|
||||
subtreePrefix := []byte("Pep")
|
||||
t.Log("VISIT Pep")
|
||||
if err := trie.VisitSubtree(subtreePrefix, func(prefix Prefix, item Item) error {
|
||||
t.Logf("VISITING prefix=%q, item=%v", prefix, item)
|
||||
if !bytes.HasPrefix(prefix, subtreePrefix) {
|
||||
t.Errorf("Unexpected prefix encountered, %q does not extend %q",
|
||||
prefix, subtreePrefix)
|
||||
}
|
||||
if len(prefix) > len(data[item.(int)].key) {
|
||||
t.Fatalf("Something is rather fishy here, prefix=%q", prefix)
|
||||
}
|
||||
counter++
|
||||
return nil
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if counter != 3 {
|
||||
t.Error("Unexpected number of nodes visited")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_VisitPrefixes(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"P", 0, success},
|
||||
{"Pe", 1, success},
|
||||
{"Pep", 2, success},
|
||||
{"Pepa", 3, success},
|
||||
{"Pepa Zdepa", 4, success},
|
||||
{"Pepa Kuchar", 5, success},
|
||||
{"Honza", 6, success},
|
||||
{"Jenik", 7, success},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
|
||||
t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
|
||||
var counter int
|
||||
word := []byte("Pepa")
|
||||
if err := trie.VisitPrefixes(word, func(prefix Prefix, item Item) error {
|
||||
t.Logf("VISITING prefix=%q, item=%v", prefix, item)
|
||||
if !bytes.HasPrefix(word, prefix) {
|
||||
t.Errorf("Unexpected prefix encountered, %q is not a prefix of %q",
|
||||
prefix, word)
|
||||
}
|
||||
counter++
|
||||
return nil
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if counter != 4 {
|
||||
t.Error("Unexpected number of nodes visited")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParticiaTrie_Delete(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"Pepan", "Pepan Zdepan", success},
|
||||
{"Honza", "Honza Novak", success},
|
||||
{"Jenik", "Jenik Poustevnicek", success},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
|
||||
t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("DELETE word=%v, success=%v", v.key, v.retVal)
|
||||
if ok := trie.Delete([]byte(v.key)); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParticiaTrie_DeleteNonExistent(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
insertData := []testData{
|
||||
{"Pepan", "Pepan Zdepan", success},
|
||||
{"Honza", "Honza Novak", success},
|
||||
{"Jenik", "Jenik Poustevnicek", success},
|
||||
}
|
||||
deleteData := []testData{
|
||||
{"Pepan", "Pepan Zdepan", success},
|
||||
{"Honza", "Honza Novak", success},
|
||||
{"Pepan", "Pepan Zdepan", failure},
|
||||
{"Jenik", "Jenik Poustevnicek", success},
|
||||
{"Honza", "Honza Novak", failure},
|
||||
}
|
||||
|
||||
for _, v := range insertData {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
|
||||
t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range deleteData {
|
||||
t.Logf("DELETE word=%v, success=%v", v.key, v.retVal)
|
||||
if ok := trie.Delete([]byte(v.key)); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParticiaTrie_DeleteSubtree(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
insertData := []testData{
|
||||
{"P", 0, success},
|
||||
{"Pe", 1, success},
|
||||
{"Pep", 2, success},
|
||||
{"Pepa", 3, success},
|
||||
{"Pepa Zdepa", 4, success},
|
||||
{"Pepa Kuchar", 5, success},
|
||||
{"Honza", 6, success},
|
||||
{"Jenik", 7, success},
|
||||
}
|
||||
deleteData := []testData{
|
||||
{"Pe", -1, success},
|
||||
{"Pe", -1, failure},
|
||||
{"Honzik", -1, failure},
|
||||
{"Honza", -1, success},
|
||||
{"Honza", -1, failure},
|
||||
{"Pep", -1, failure},
|
||||
{"P", -1, success},
|
||||
{"Nobody", -1, failure},
|
||||
{"", -1, success},
|
||||
}
|
||||
|
||||
for _, v := range insertData {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
|
||||
t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range deleteData {
|
||||
t.Logf("DELETE_SUBTREE prefix=%v, success=%v", v.key, v.retVal)
|
||||
if ok := trie.DeleteSubtree([]byte(v.key)); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func TestTrie_Dump(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"Honda", nil, success},
|
||||
{"Honza", nil, success},
|
||||
{"Jenik", nil, success},
|
||||
{"Pepan", nil, success},
|
||||
{"Pepin", nil, success},
|
||||
}
|
||||
|
||||
for i, v := range data {
|
||||
if _, ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
|
||||
t.Logf("INSERT %v %v", v.key, v.value)
|
||||
t.Fatalf("Unexpected return value, expected=%v, got=%v", i, ok)
|
||||
}
|
||||
}
|
||||
|
||||
dump := `
|
||||
+--+--+ Hon +--+--+ da
|
||||
| |
|
||||
| +--+ za
|
||||
|
|
||||
+--+ Jenik
|
||||
|
|
||||
+--+ Pep +--+--+ an
|
||||
|
|
||||
+--+ in
|
||||
`
|
||||
|
||||
var buf bytes.Buffer
|
||||
trie.Dump(buf)
|
||||
|
||||
if !bytes.Equal(buf.Bytes(), dump) {
|
||||
t.Logf("DUMP")
|
||||
t.Fatalf("Unexpected dump generated, expected\n\n%v\ngot\n\n%v", dump, buf.String())
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func TestTrie_compact(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
trie.Insert(Prefix("a"), 0)
|
||||
trie.Insert(Prefix("ab"), 0)
|
||||
trie.Insert(Prefix("abc"), 0)
|
||||
trie.Insert(Prefix("abcd"), 0)
|
||||
trie.Insert(Prefix("abcde"), 0)
|
||||
trie.Insert(Prefix("abcdef"), 0)
|
||||
trie.Insert(Prefix("abcdefg"), 0)
|
||||
trie.Insert(Prefix("abcdefgi"), 0)
|
||||
trie.Insert(Prefix("abcdefgij"), 0)
|
||||
trie.Insert(Prefix("abcdefgijk"), 0)
|
||||
|
||||
trie.Delete(Prefix("abcdef"))
|
||||
trie.Delete(Prefix("abcde"))
|
||||
trie.Delete(Prefix("abcdefg"))
|
||||
|
||||
trie.Delete(Prefix("a"))
|
||||
trie.Delete(Prefix("abc"))
|
||||
trie.Delete(Prefix("ab"))
|
||||
|
||||
trie.Visit(func(prefix Prefix, item Item) error {
|
||||
// 97 ~~ 'a',
|
||||
for ch := byte(97); ch <= 107; ch++ {
|
||||
if c := bytes.Count(prefix, []byte{ch}); c > 1 {
|
||||
t.Errorf("%q appeared in %q %v times", ch, prefix, c)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func TestTrie_longestCommonPrefixLenght(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
trie.prefix = []byte("1234567890")
|
||||
|
||||
switch {
|
||||
case trie.longestCommonPrefixLength([]byte("")) != 0:
|
||||
t.Fail()
|
||||
case trie.longestCommonPrefixLength([]byte("12345")) != 5:
|
||||
t.Fail()
|
||||
case trie.longestCommonPrefixLength([]byte("123789")) != 3:
|
||||
t.Fail()
|
||||
case trie.longestCommonPrefixLength([]byte("12345678901")) != 10:
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
// Examples --------------------------------------------------------------------
|
||||
|
||||
func ExampleTrie() {
|
||||
// Create a new tree.
|
||||
trie := NewTrie()
|
||||
|
||||
// Insert some items.
|
||||
trie.Insert(Prefix("Pepa Novak"), 1)
|
||||
trie.Insert(Prefix("Pepa Sindelar"), 2)
|
||||
trie.Insert(Prefix("Karel Macha"), 3)
|
||||
trie.Insert(Prefix("Karel Hynek Macha"), 4)
|
||||
|
||||
// Just check if some things are present in the tree.
|
||||
key := Prefix("Pepa Novak")
|
||||
fmt.Printf("%q present? %v\n", key, trie.Match(key))
|
||||
key = Prefix("Karel")
|
||||
fmt.Printf("Anybody called %q here? %v\n", key, trie.MatchSubtree(key))
|
||||
|
||||
// Walk the tree.
|
||||
trie.Visit(printItem)
|
||||
// "Pepa Novak": 1
|
||||
// "Pepa Sindelar": 2
|
||||
// "Karel Macha": 3
|
||||
// "Karel Hynek Macha": 4
|
||||
|
||||
// Walk a subtree.
|
||||
trie.VisitSubtree(Prefix("Pepa"), printItem)
|
||||
// "Pepa Novak": 1
|
||||
// "Pepa Sindelar": 2
|
||||
|
||||
// Modify an item, then fetch it from the tree.
|
||||
trie.Set(Prefix("Karel Hynek Macha"), 10)
|
||||
key = Prefix("Karel Hynek Macha")
|
||||
fmt.Printf("%q: %v\n", key, trie.Get(key))
|
||||
// "Karel Hynek Macha": 10
|
||||
|
||||
// Walk prefixes.
|
||||
prefix := Prefix("Karel Hynek Macha je kouzelnik")
|
||||
trie.VisitPrefixes(prefix, printItem)
|
||||
// "Karel Hynek Macha": 10
|
||||
|
||||
// Delete some items.
|
||||
trie.Delete(Prefix("Pepa Novak"))
|
||||
trie.Delete(Prefix("Karel Macha"))
|
||||
|
||||
// Walk again.
|
||||
trie.Visit(printItem)
|
||||
// "Pepa Sindelar": 2
|
||||
// "Karel Hynek Macha": 10
|
||||
|
||||
// Delete a subtree.
|
||||
trie.DeleteSubtree(Prefix("Pepa"))
|
||||
|
||||
// Print what is left.
|
||||
trie.Visit(printItem)
|
||||
// "Karel Hynek Macha": 10
|
||||
|
||||
// Output:
|
||||
// "Pepa Novak" present? true
|
||||
// Anybody called "Karel" here? true
|
||||
// "Pepa Novak": 1
|
||||
// "Pepa Sindelar": 2
|
||||
// "Karel Macha": 3
|
||||
// "Karel Hynek Macha": 4
|
||||
// "Pepa Novak": 1
|
||||
// "Pepa Sindelar": 2
|
||||
// "Karel Hynek Macha": 10
|
||||
// "Karel Hynek Macha": 10
|
||||
// "Pepa Sindelar": 2
|
||||
// "Karel Hynek Macha": 10
|
||||
// "Karel Hynek Macha": 10
|
||||
}
|
||||
|
||||
// Helpers ---------------------------------------------------------------------
|
||||
|
||||
func printItem(prefix Prefix, item Item) error {
|
||||
fmt.Printf("%q: %v\n", prefix, item)
|
||||
return nil
|
||||
}
|
78
Godeps/_workspace/src/github.com/tchap/go-patricia/patricia/patricia_test.go
generated
vendored
78
Godeps/_workspace/src/github.com/tchap/go-patricia/patricia/patricia_test.go
generated
vendored
@ -1,78 +0,0 @@
|
||||
// Copyright (c) 2014 The go-patricia AUTHORS
|
||||
//
|
||||
// Use of this source code is governed by The MIT License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package patricia
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Tests -----------------------------------------------------------------------
|
||||
|
||||
func TestTrie_GetNonexistentPrefix(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"aba", 0, success},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
|
||||
t.Logf("GET prefix=baa, expect item=nil")
|
||||
if item := trie.Get(Prefix("baa")); item != nil {
|
||||
t.Errorf("Unexpected return value, expected=<nil>, got=%v", item)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_RandomKitchenSink(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip()
|
||||
}
|
||||
const count, size = 750000, 16
|
||||
b := make([]byte, count+size+1)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
t.Fatal("error generating random bytes", err)
|
||||
}
|
||||
m := make(map[string]string)
|
||||
for i := 0; i < count; i++ {
|
||||
m[string(b[i:i+size])] = string(b[i+1 : i+size+1])
|
||||
}
|
||||
trie := NewTrie()
|
||||
getAndDelete := func(k, v string) {
|
||||
i := trie.Get(Prefix(k))
|
||||
if i == nil {
|
||||
t.Fatalf("item not found, prefix=%v", []byte(k))
|
||||
} else if s, ok := i.(string); !ok {
|
||||
t.Fatalf("unexpected item type, expecting=%v, got=%v", reflect.TypeOf(k), reflect.TypeOf(i))
|
||||
} else if s != v {
|
||||
t.Fatalf("unexpected item, expecting=%v, got=%v", []byte(k), []byte(s))
|
||||
} else if !trie.Delete(Prefix(k)) {
|
||||
t.Fatalf("delete failed, prefix=%v", []byte(k))
|
||||
} else if i = trie.Get(Prefix(k)); i != nil {
|
||||
t.Fatalf("unexpected item, expecting=<nil>, got=%v", i)
|
||||
} else if trie.Delete(Prefix(k)) {
|
||||
t.Fatalf("extra delete succeeded, prefix=%v", []byte(k))
|
||||
}
|
||||
}
|
||||
for k, v := range m {
|
||||
if !trie.Insert(Prefix(k), v) {
|
||||
t.Fatalf("insert failed, prefix=%v", []byte(k))
|
||||
}
|
||||
if byte(k[size/2]) < 128 {
|
||||
getAndDelete(k, v)
|
||||
delete(m, k)
|
||||
}
|
||||
}
|
||||
for k, v := range m {
|
||||
getAndDelete(k, v)
|
||||
}
|
||||
}
|
7
Makefile
7
Makefile
@ -27,13 +27,8 @@ build-os:
|
||||
build-storage:
|
||||
@$(MAKE) $(MAKE_OPTIONS) -C pkg/storage/erasure/isal lib
|
||||
@godep go test -race -coverprofile=cover.out github.com/minio-io/minio/pkg/storage/erasure
|
||||
@godep go test -race -coverprofile=cover.out github.com/minio-io/minio/pkg/storage/appendstorage
|
||||
@godep go test -race -coverprofile=cover.out github.com/minio-io/minio/pkg/storage/encodedstorage
|
||||
|
||||
build-server:
|
||||
@godep go test -race -coverprofile=cover.out github.com/minio-io/minio/pkg/server
|
||||
|
||||
cover: build-storage build-os build-server build-utils
|
||||
cover: build-storage build-os build-utils
|
||||
|
||||
install: cover
|
||||
|
||||
|
@ -1,11 +0,0 @@
|
||||
all: build test
|
||||
.PHONY: all
|
||||
|
||||
build:
|
||||
@godep go build
|
||||
|
||||
test: build
|
||||
@godep go test -race -coverprofile=cover.out
|
||||
|
||||
clean:
|
||||
@rm -v cover.out
|
@ -1,152 +0,0 @@
|
||||
/*
|
||||
* Mini Object Storage, (C) 2014 Minio, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package appendstorage
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/minio-io/minio/pkg/storage"
|
||||
"github.com/minio-io/minio/pkg/utils/checksum/crc32c"
|
||||
)
|
||||
|
||||
type appendStorage struct {
|
||||
RootDir string
|
||||
file *os.File
|
||||
objects map[string]Header
|
||||
objectsFile string
|
||||
}
|
||||
|
||||
type Header struct {
|
||||
Path string
|
||||
Offset int64
|
||||
Length int
|
||||
Crc uint32
|
||||
}
|
||||
|
||||
func NewStorage(rootDir string, slice int) (storage.ObjectStorage, error) {
|
||||
rootPath := path.Join(rootDir, strconv.Itoa(slice))
|
||||
// TODO verify and fix partial writes
|
||||
file, err := os.OpenFile(rootPath, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0600)
|
||||
if err != nil {
|
||||
return &appendStorage{}, err
|
||||
}
|
||||
objectsFile := path.Join(rootDir, strconv.Itoa(slice)+".map")
|
||||
objects := make(map[string]Header)
|
||||
if _, err := os.Stat(objectsFile); err == nil {
|
||||
mapFile, err := os.Open(objectsFile)
|
||||
defer mapFile.Close()
|
||||
if err != nil {
|
||||
return &appendStorage{}, nil
|
||||
}
|
||||
dec := gob.NewDecoder(mapFile)
|
||||
err = dec.Decode(&objects)
|
||||
if err != nil && err != io.EOF {
|
||||
return &appendStorage{}, nil
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return &appendStorage{}, err
|
||||
}
|
||||
return &appendStorage{
|
||||
RootDir: rootDir,
|
||||
file: file,
|
||||
objects: objects,
|
||||
objectsFile: objectsFile,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (storage *appendStorage) Get(objectPath string) (io.Reader, error) {
|
||||
header, ok := storage.objects[objectPath]
|
||||
if ok == false {
|
||||
return nil, errors.New("Object not found")
|
||||
}
|
||||
|
||||
offset := header.Offset
|
||||
length := header.Length
|
||||
crc := header.Crc
|
||||
|
||||
object := make([]byte, length)
|
||||
_, err := storage.file.ReadAt(object, offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newcrc, err := crc32c.Crc32c(object)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if newcrc != crc {
|
||||
return nil, err
|
||||
}
|
||||
return bytes.NewBuffer(object), nil
|
||||
}
|
||||
|
||||
func (aStorage *appendStorage) Put(objectPath string, object io.Reader) error {
|
||||
header := Header{
|
||||
Path: objectPath,
|
||||
Offset: 0,
|
||||
Length: 0,
|
||||
Crc: 0,
|
||||
}
|
||||
offset, err := aStorage.file.Seek(0, os.SEEK_END)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
objectBytes, err := ioutil.ReadAll(object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := aStorage.file.Write(objectBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
header.Offset = offset
|
||||
header.Length = len(objectBytes)
|
||||
header.Crc, err = crc32c.Crc32c(objectBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
aStorage.objects[objectPath] = header
|
||||
var mapBuffer bytes.Buffer
|
||||
encoder := gob.NewEncoder(&mapBuffer)
|
||||
encoder.Encode(aStorage.objects)
|
||||
ioutil.WriteFile(aStorage.objectsFile, mapBuffer.Bytes(), 0600)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (aStorage *appendStorage) List(objectPath string) ([]storage.ObjectDescription, error) {
|
||||
var objectDescList []storage.ObjectDescription
|
||||
for objectName, _ := range aStorage.objects {
|
||||
if strings.HasPrefix(objectName, objectPath) {
|
||||
var objectDescription storage.ObjectDescription
|
||||
objectDescription.Name = objectName
|
||||
objectDescription.Md5sum = ""
|
||||
objectDescription.Murmur3 = ""
|
||||
objectDescList = append(objectDescList, objectDescription)
|
||||
}
|
||||
}
|
||||
if len(objectDescList) == 0 {
|
||||
return nil, errors.New("No objects found")
|
||||
}
|
||||
return objectDescList, nil
|
||||
}
|
@ -1,116 +0,0 @@
|
||||
package appendstorage
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/minio-io/minio/pkg/storage"
|
||||
"github.com/minio-io/minio/pkg/utils"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
type AppendStorageSuite struct{}
|
||||
|
||||
var _ = Suite(&AppendStorageSuite{})
|
||||
|
||||
func Test(t *testing.T) { TestingT(t) }
|
||||
|
||||
func (s *AppendStorageSuite) TestAppendStoragePutAtRootPath(c *C) {
|
||||
rootDir, err := utils.MakeTempTestDir()
|
||||
c.Assert(err, IsNil)
|
||||
defer os.RemoveAll(rootDir)
|
||||
|
||||
var objectStorage storage.ObjectStorage
|
||||
objectStorage, err = NewStorage(rootDir, 0)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
err = objectStorage.Put("path1", bytes.NewBuffer([]byte("object1")))
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// assert object1 was created in correct path
|
||||
objectResult1, err := objectStorage.Get("path1")
|
||||
c.Assert(err, IsNil)
|
||||
object1, _ := ioutil.ReadAll(objectResult1)
|
||||
c.Assert(string(object1), Equals, "object1")
|
||||
|
||||
err = objectStorage.Put("path2", bytes.NewBuffer([]byte("object2")))
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// assert object1 was created in correct path
|
||||
objectResult2, err := objectStorage.Get("path2")
|
||||
c.Assert(err, IsNil)
|
||||
object2, _ := ioutil.ReadAll(objectResult2)
|
||||
c.Assert(string(object2), Equals, "object2")
|
||||
|
||||
objectResult1, err = objectStorage.Get("path1")
|
||||
c.Assert(err, IsNil)
|
||||
object1, _ = ioutil.ReadAll(objectResult1)
|
||||
c.Assert(string(object1), Equals, "object1")
|
||||
}
|
||||
|
||||
func (s *AppendStorageSuite) TestAppendStoragePutDirPath(c *C) {
|
||||
rootDir, err := utils.MakeTempTestDir()
|
||||
c.Assert(err, IsNil)
|
||||
defer os.RemoveAll(rootDir)
|
||||
|
||||
var objectStorage storage.ObjectStorage
|
||||
objectStorage, err = NewStorage(rootDir, 0)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// add object 1
|
||||
objectStorage.Put("path1/path2/path3", bytes.NewBuffer([]byte("object")))
|
||||
|
||||
// assert object1 was created in correct path
|
||||
objectResult1, err := objectStorage.Get("path1/path2/path3")
|
||||
c.Assert(err, IsNil)
|
||||
object1, _ := ioutil.ReadAll(objectResult1)
|
||||
c.Assert(string(object1), Equals, "object")
|
||||
|
||||
// add object 2
|
||||
objectStorage.Put("path1/path1/path1", bytes.NewBuffer([]byte("object2")))
|
||||
|
||||
// assert object1 was created in correct path
|
||||
objectResult2, err := objectStorage.Get("path1/path1/path1")
|
||||
c.Assert(err, IsNil)
|
||||
object2, _ := ioutil.ReadAll(objectResult2)
|
||||
c.Assert(string(object2), Equals, "object2")
|
||||
}
|
||||
|
||||
func (s *AppendStorageSuite) TestSerialization(c *C) {
|
||||
rootDir, err := utils.MakeTempTestDir()
|
||||
c.Assert(err, IsNil)
|
||||
defer os.RemoveAll(rootDir)
|
||||
|
||||
objectStorage, err := NewStorage(rootDir, 0)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
err = objectStorage.Put("path1", bytes.NewBuffer([]byte("object1")))
|
||||
c.Assert(err, IsNil)
|
||||
err = objectStorage.Put("path2", bytes.NewBuffer([]byte("object2")))
|
||||
c.Assert(err, IsNil)
|
||||
err = objectStorage.Put("path3/obj3", bytes.NewBuffer([]byte("object3")))
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
es := objectStorage.(*appendStorage)
|
||||
es.file.Close()
|
||||
|
||||
objectStorage2, err := NewStorage(rootDir, 0)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
objectResult1, err := objectStorage2.Get("path1")
|
||||
c.Assert(err, IsNil)
|
||||
object1, _ := ioutil.ReadAll(objectResult1)
|
||||
c.Assert(string(object1), Equals, "object1")
|
||||
|
||||
objectResult2, err := objectStorage2.Get("path2")
|
||||
c.Assert(err, IsNil)
|
||||
object2, _ := ioutil.ReadAll(objectResult2)
|
||||
c.Assert(string(object2), Equals, "object2")
|
||||
|
||||
objectResult3, err := objectStorage2.Get("path3/obj3")
|
||||
c.Assert(err, IsNil)
|
||||
object3, _ := ioutil.ReadAll(objectResult3)
|
||||
c.Assert(string(object3), Equals, "object3")
|
||||
}
|
@ -1,288 +0,0 @@
|
||||
/*
|
||||
* Mini Object Storage, (C) 2014 Minio, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package encodedstorage
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/gob"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/minio-io/minio/pkg/storage"
|
||||
"github.com/minio-io/minio/pkg/storage/appendstorage"
|
||||
"github.com/minio-io/minio/pkg/storage/erasure"
|
||||
"github.com/minio-io/minio/pkg/utils/split"
|
||||
"github.com/spaolacci/murmur3"
|
||||
)
|
||||
|
||||
type encodedStorage struct {
|
||||
RootDir string
|
||||
K int
|
||||
M int
|
||||
BlockSize uint64
|
||||
objects map[string]StorageEntry
|
||||
diskStorage []storage.ObjectStorage
|
||||
}
|
||||
|
||||
type StorageEntry struct {
|
||||
Path string
|
||||
Md5sum []byte
|
||||
Murmurhash uint64
|
||||
Blocks []StorageBlockEntry
|
||||
Encoderparams erasure.EncoderParams
|
||||
}
|
||||
|
||||
type StorageBlockEntry struct {
|
||||
Index int
|
||||
Length int
|
||||
}
|
||||
|
||||
type storeRequest struct {
|
||||
path string
|
||||
data []byte
|
||||
}
|
||||
|
||||
type storeResponse struct {
|
||||
data []byte
|
||||
err error
|
||||
}
|
||||
|
||||
func NewStorage(rootDir string, k, m int, blockSize uint64) (storage.ObjectStorage, error) {
|
||||
// create storage files
|
||||
if k == 0 || m == 0 {
|
||||
return nil, errors.New("Invalid protection level")
|
||||
}
|
||||
|
||||
storageNodes := make([]storage.ObjectStorage, k+m)
|
||||
for i := 0; i < k+m; i++ {
|
||||
storageNode, err := appendstorage.NewStorage(rootDir, i)
|
||||
storageNodes[i] = storageNode
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
objects := make(map[string]StorageEntry)
|
||||
indexPath := path.Join(rootDir, "index")
|
||||
if _, err := os.Stat(indexPath); err == nil {
|
||||
indexFile, err := os.Open(indexPath)
|
||||
defer indexFile.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encoder := gob.NewDecoder(indexFile)
|
||||
err = encoder.Decode(&objects)
|
||||
if err != nil && err != io.EOF {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
newStorage := encodedStorage{
|
||||
RootDir: rootDir,
|
||||
K: k,
|
||||
M: m,
|
||||
BlockSize: blockSize,
|
||||
objects: objects,
|
||||
diskStorage: storageNodes,
|
||||
}
|
||||
return &newStorage, nil
|
||||
}
|
||||
|
||||
func (eStorage *encodedStorage) Get(objectPath string) (io.Reader, error) {
|
||||
entry, ok := eStorage.objects[objectPath]
|
||||
if ok == false {
|
||||
return nil, errors.New("Object not found")
|
||||
}
|
||||
reader, writer := io.Pipe()
|
||||
go eStorage.readObject(objectPath, entry, writer)
|
||||
return reader, nil
|
||||
}
|
||||
|
||||
func (eStorage *encodedStorage) List(objectPath string) ([]storage.ObjectDescription, error) {
|
||||
var objectDescList []storage.ObjectDescription
|
||||
for objectName, objectEntry := range eStorage.objects {
|
||||
if strings.HasPrefix(objectName, objectPath) {
|
||||
var objectDescription storage.ObjectDescription
|
||||
objectDescription.Name = objectName
|
||||
objectDescription.Md5sum = hex.EncodeToString(objectEntry.Md5sum)
|
||||
objectDescription.Murmur3 = strconv.FormatUint(objectEntry.Murmurhash, 16)
|
||||
objectDescList = append(objectDescList, objectDescription)
|
||||
}
|
||||
}
|
||||
if len(objectDescList) == 0 {
|
||||
return nil, errors.New("No objects found")
|
||||
}
|
||||
return objectDescList, nil
|
||||
}
|
||||
|
||||
func (eStorage *encodedStorage) Put(objectPath string, object io.Reader) error {
|
||||
// split
|
||||
chunks := split.SplitStream(object, eStorage.BlockSize)
|
||||
|
||||
// for each chunk
|
||||
encoderParameters, err := erasure.ParseEncoderParams(eStorage.K, eStorage.M, erasure.CAUCHY)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
encoder := erasure.NewEncoder(encoderParameters)
|
||||
entry := StorageEntry{
|
||||
Path: objectPath,
|
||||
Md5sum: nil,
|
||||
Murmurhash: 0,
|
||||
Blocks: make([]StorageBlockEntry, 0),
|
||||
Encoderparams: erasure.EncoderParams{
|
||||
K: eStorage.K,
|
||||
M: eStorage.M,
|
||||
Technique: erasure.CAUCHY,
|
||||
},
|
||||
}
|
||||
// Hash
|
||||
murmur := murmur3.Sum64([]byte(objectPath))
|
||||
// allocate md5
|
||||
hash := md5.New()
|
||||
i := 0
|
||||
// encode
|
||||
for chunk := range chunks {
|
||||
if chunk.Err == nil {
|
||||
// encode each
|
||||
blocks, length := encoder.Encode(chunk.Data)
|
||||
// store each
|
||||
storeErrors := eStorage.storeBlocks(objectPath+"$"+strconv.Itoa(i), blocks)
|
||||
for _, err := range storeErrors {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// md5sum only after chunk is committed to disk
|
||||
hash.Write(chunk.Data)
|
||||
blockEntry := StorageBlockEntry{
|
||||
Index: i,
|
||||
Length: length,
|
||||
}
|
||||
entry.Blocks = append(entry.Blocks, blockEntry)
|
||||
} else {
|
||||
return chunk.Err
|
||||
}
|
||||
i++
|
||||
}
|
||||
entry.Md5sum = hash.Sum(nil)
|
||||
entry.Murmurhash = murmur
|
||||
eStorage.objects[objectPath] = entry
|
||||
var gobBuffer bytes.Buffer
|
||||
gobEncoder := gob.NewEncoder(&gobBuffer)
|
||||
gobEncoder.Encode(eStorage.objects)
|
||||
ioutil.WriteFile(path.Join(eStorage.RootDir, "index"), gobBuffer.Bytes(), 0600)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (eStorage *encodedStorage) storeBlocks(path string, blocks [][]byte) []error {
|
||||
returnChannels := make([]<-chan error, len(eStorage.diskStorage))
|
||||
for i, store := range eStorage.diskStorage {
|
||||
returnChannels[i] = storageRoutine(store, path, bytes.NewBuffer(blocks[i]))
|
||||
}
|
||||
returnErrors := make([]error, 0)
|
||||
for _, returnChannel := range returnChannels {
|
||||
for returnValue := range returnChannel {
|
||||
if returnValue != nil {
|
||||
returnErrors = append(returnErrors, returnValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
return returnErrors
|
||||
}
|
||||
|
||||
func (eStorage *encodedStorage) readObject(objectPath string, entry StorageEntry, writer *io.PipeWriter) {
|
||||
ep, err := erasure.ParseEncoderParams(entry.Encoderparams.K, entry.Encoderparams.M, entry.Encoderparams.Technique)
|
||||
if err != nil {
|
||||
writer.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
encoder := erasure.NewEncoder(ep)
|
||||
for i, chunk := range entry.Blocks {
|
||||
blockSlices := eStorage.getBlockSlices(objectPath + "$" + strconv.Itoa(i))
|
||||
if len(blockSlices) == 0 {
|
||||
writer.CloseWithError(errors.New("slices missing!!"))
|
||||
return
|
||||
}
|
||||
var blocks [][]byte
|
||||
for _, slice := range blockSlices {
|
||||
if slice.err != nil {
|
||||
writer.CloseWithError(slice.err)
|
||||
return
|
||||
}
|
||||
blocks = append(blocks, slice.data)
|
||||
}
|
||||
data, err := encoder.Decode(blocks, chunk.Length)
|
||||
if err != nil {
|
||||
writer.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
bytesWritten := 0
|
||||
for bytesWritten != len(data) {
|
||||
written, err := writer.Write(data[bytesWritten:len(data)])
|
||||
if err != nil {
|
||||
writer.CloseWithError(err)
|
||||
}
|
||||
bytesWritten += written
|
||||
}
|
||||
}
|
||||
writer.Close()
|
||||
}
|
||||
|
||||
func (eStorage *encodedStorage) getBlockSlices(objectPath string) []storeResponse {
|
||||
responses := make([]<-chan storeResponse, 0)
|
||||
for i := 0; i < len(eStorage.diskStorage); i++ {
|
||||
response := getSlice(eStorage.diskStorage[i], objectPath)
|
||||
responses = append(responses, response)
|
||||
}
|
||||
results := make([]storeResponse, 0)
|
||||
for _, response := range responses {
|
||||
results = append(results, <-response)
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func getSlice(store storage.ObjectStorage, path string) <-chan storeResponse {
|
||||
out := make(chan storeResponse)
|
||||
go func() {
|
||||
obj, err := store.Get(path)
|
||||
if err != nil {
|
||||
out <- storeResponse{data: nil, err: err}
|
||||
} else {
|
||||
data, err := ioutil.ReadAll(obj)
|
||||
out <- storeResponse{data: data, err: err}
|
||||
}
|
||||
close(out)
|
||||
}()
|
||||
return out
|
||||
}
|
||||
|
||||
func storageRoutine(store storage.ObjectStorage, path string, data io.Reader) <-chan error {
|
||||
out := make(chan error)
|
||||
go func() {
|
||||
if err := store.Put(path, data); err != nil {
|
||||
out <- err
|
||||
}
|
||||
close(out)
|
||||
}()
|
||||
return out
|
||||
}
|
@ -1,99 +0,0 @@
|
||||
package encodedstorage
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/minio-io/minio/pkg/storage"
|
||||
"github.com/minio-io/minio/pkg/utils"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
type EncodedStorageSuite struct{}
|
||||
|
||||
var _ = Suite(&EncodedStorageSuite{})
|
||||
|
||||
func Test(t *testing.T) { TestingT(t) }
|
||||
|
||||
func (s *EncodedStorageSuite) TestFileStoragePutAtRootPath(c *C) {
|
||||
rootDir, err := utils.MakeTempTestDir()
|
||||
c.Assert(err, IsNil)
|
||||
defer os.RemoveAll(rootDir)
|
||||
|
||||
var objectStorage storage.ObjectStorage
|
||||
objectStorage, err = NewStorage(rootDir, 10, 6, 1024)
|
||||
c.Assert(err, IsNil)
|
||||
objectBuffer := bytes.NewBuffer([]byte("object1"))
|
||||
objectStorage.Put("path1", objectBuffer)
|
||||
|
||||
// assert object1 was created in correct path
|
||||
objectResult1, err := objectStorage.Get("path1")
|
||||
c.Assert(err, IsNil)
|
||||
object1, _ := ioutil.ReadAll(objectResult1)
|
||||
c.Assert(string(object1), Equals, "object1")
|
||||
|
||||
objectList, err := objectStorage.List("")
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(objectList[0].Name, Equals, "path1")
|
||||
}
|
||||
|
||||
func (s *EncodedStorageSuite) TestFileStoragePutDirPath(c *C) {
|
||||
rootDir, err := utils.MakeTempTestDir()
|
||||
c.Assert(err, IsNil)
|
||||
defer os.RemoveAll(rootDir)
|
||||
|
||||
var objectStorage storage.ObjectStorage
|
||||
objectStorage, err = NewStorage(rootDir, 10, 6, 1024)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
objectBuffer1 := bytes.NewBuffer([]byte("object1"))
|
||||
objectStorage.Put("path1/path2/path3", objectBuffer1)
|
||||
|
||||
// assert object1 was created in correct path
|
||||
objectResult1, err := objectStorage.Get("path1/path2/path3")
|
||||
c.Assert(err, IsNil)
|
||||
object1, _ := ioutil.ReadAll(objectResult1)
|
||||
c.Assert(string(object1), Equals, "object1")
|
||||
|
||||
// add second object
|
||||
objectBuffer2 := bytes.NewBuffer([]byte("object2"))
|
||||
err = objectStorage.Put("path2/path2/path2", objectBuffer2)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// add third object
|
||||
objectBuffer3 := bytes.NewBuffer([]byte("object3"))
|
||||
err = objectStorage.Put("object3", objectBuffer3)
|
||||
c.Assert(err, IsNil)
|
||||
}
|
||||
|
||||
func (s *EncodedStorageSuite) TestObjectWithChunking(c *C) {
|
||||
rootDir, err := utils.MakeTempTestDir()
|
||||
c.Assert(err, IsNil)
|
||||
defer os.RemoveAll(rootDir)
|
||||
|
||||
var objectStorage storage.ObjectStorage
|
||||
objectStorage, err = NewStorage(rootDir, 10, 6, 1024)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
var buffer bytes.Buffer
|
||||
for i := 0; i <= 2048; i++ {
|
||||
buffer.Write([]byte(strconv.Itoa(i)))
|
||||
}
|
||||
|
||||
reader := bytes.NewReader(buffer.Bytes())
|
||||
|
||||
err = objectStorage.Put("object", reader)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
objectStorage2, err := NewStorage(rootDir, 10, 6, 1024)
|
||||
c.Assert(err, IsNil)
|
||||
objectResult, err := objectStorage2.Get("object")
|
||||
c.Assert(err, IsNil)
|
||||
result, err := ioutil.ReadAll(objectResult)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(bytes.Compare(result, buffer.Bytes()), Equals, 0)
|
||||
|
||||
}
|
@ -1,15 +1 @@
|
||||
package storage
|
||||
|
||||
import "io"
|
||||
|
||||
type ObjectStorage interface {
|
||||
List(objectPath string) ([]ObjectDescription, error)
|
||||
Get(path string) (io.Reader, error)
|
||||
Put(path string, object io.Reader) error
|
||||
}
|
||||
|
||||
type ObjectDescription struct {
|
||||
Name string
|
||||
Md5sum string
|
||||
Murmur3 string
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user